  function AjaxRequest(u, cb) {
    this.callback = cb;
    this.url = u;
  }

  AjaxRequest.prototype.send = function() {
    this.request = (window.XMLHttpRequest ? new XMLHttpRequest() : (window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : null));
    var that = this;
    this.request.onreadystatechange = function() {
      that.callback(that);
    }
    this.request.open("GET", this.url, true);
    this.request.send(null);
  }

  function hasEvent(day, month, events) {
    var date = month + (day < 10 ? '0' + day : day) + '';
    for (var i=0;i<events.length;++i) {
      var startDate = DateFormatter.format(new Date(events[i].startDate), 'Ymd');
      var endDate = DateFormatter.format(new Date(events[i].endDate), 'Ymd');
      var startDay = DateFormatter.format(new Date(events[i].startDate), 'j');
      var endDay = DateFormatter.format(new Date(events[i].endDate), 'j');
      if (events[i].endDate == 0 || startDate == endDate) {
        if (day == startDay) {
          return true;
        }
      } else if (startDate < endDate) {
        if (date >= startDate && date <= endDate) {
          return true;
        }
      }
    }
    return false;
  }

  function eventsReceived(req) {
    var request = req.request;
    if (request.readyState == 4) {

      if (request.status == 200) {
        var resultText = request.responseText;
        var result = eval('(' + resultText + ')');

        var x = document.getElementById(req.resultDiv);
        x.innerHTML = "";
        x.style.display = 'block';
        if (req.renderCalendar) {
          var prevMonth = DateFormatter.format(new Date(result.prevMonth), "Ym");
          var month = DateFormatter.format(new Date(result.month), "Ym");
          var monthStr = DateFormatter.format(new Date(result.month), "F Y");
          var nextMonth = DateFormatter.format(new Date(result.nextMonth), "Ym");
          var today = new Date().getDate();
          var inCurrentMonth = (DateFormatter.format(new Date(), "Ym") == month);
        

          var code = '<table class="cal" cellspacing="0" border="0">';

          // days header
          code += '<tr>';
          for (i=0;i<7;++i) {
            code += '<th class="cal_day_title">' + DateFormatter.weekFull_nl[i].substr(0, 1) + '</td>';
          }
          code += '</tr>';

          var day = 1;
          var startWeekDay = DateFormatter.startWeekDay(new Date(result.month));
          var lastDayOfMonth = DateFormatter.lastDayOfMonth(new Date(result.month));

          // first row of days
          code += '<tr>';
          var extraClass = "";
          for (i=0;i<7;++i) {
            if (i < startWeekDay) {
              code += '<td class="cal_day">&nbsp;</td>';
            } else {
              if (today == day && inCurrentMonth) {
                extraClass = " cal_today";            
              } else {
                extraClass = "";
              }             
              if (hasEvent(day, month, result.events)) {
                code += '<td class="cal_active'+extraClass+'"><a class="cal_active" href="#' + req.eventsAnchorName + '" onclick="getEvents(\'' + 'content_wrapper' + '\',\'bigeventList_tpl\', \'' + month + day + '\', \'' + month + (day + 1) + '\', \'\', \'' + req.category + '\');return false;">' + day + '</a></td>';
              } else {
                code += '<td class="cal_day'+extraClass+'">' + day + '</td>';
              }
              day++;
            }
          }
          code += '</tr>';

          // middle days
          while (day + 7 <= lastDayOfMonth) {
            code += '<tr>';
            for (i=0;i<7;++i) {
              if (today == day && inCurrentMonth) {
                extraClass = " cal_today";            
              } else {
                extraClass = "";
              }             
              if (hasEvent(day, month, result.events)) {
                code += '<td class="cal_active'+extraClass+'"><a class="cal_active" href="#' + req.eventsAnchorName + '" onclick="getEvents(\'' + 'content_wrapper' + '\',\'bigeventList_tpl\', \'' + month + day + '\', \'' + month + (day + 1) + '\', \'\', \'' + req.category + '\');return false;">' + day + '</a></td>';
              } else {
                code += '<td class="cal_day'+extraClass+'">' + day + '</td>';
              }
              day++;
            }
            code += '</tr>';
          }

          // last row of days
          finalEmptyFields = day - lastDayOfMonth + 6;
          code += '<tr>';
          while (day <= lastDayOfMonth) {
            if (today == day && inCurrentMonth) {
              extraClass = " cal_today";            
            } else {
              extraClass = "";
            }             
            if (hasEvent(day, month, result.events)) {
              code += '<td class="cal_active'+extraClass+'"><a class="cal_active" href="#' + req.eventsAnchorName + '" onclick="getEvents(\'' + 'content_wrapper' + '\', \'bigeventList_tpl\', \'' + month + day + '\', \'' + month + (day + 1) + '\', \'\', \'' + req.category + '\');return false;">' + day + '</a></td>';
            } else {
              code += '<td class="cal_day'+extraClass+'">' + day + '</td>';
            }
            day++;
          }
          for (i=0;i<finalEmptyFields;++i) {
            code += '<td class="cal_day">&nbsp;</td>';
          }
          code += '</tr>';

          // navigation
          code += '<tr><td colspan="7" class="cal_nav">';
          code += '<a class="cal_nav" href="#' + req.calendarAnchorName + '" onclick="renderCalendar(\'' + req.resultDiv + '\', \'' + req.targetDiv + '\', \'' + req.targetDivToHide + '\', \'' + req.calendarAnchorName + '\', \'' + req.eventsAnchorName + '\', \'' + prevMonth + '\', \'' + req.category + '\');return false;">&laquo;</a>';
          code += '<span class="cal_cur_date">' + monthStr + '</span>';
          code += '<a class="cal_nav" href="#' + req.calendarAnchorName + '" onclick="renderCalendar(\'' + req.resultDiv + '\', \'' + req.targetDiv + '\', \'' + req.targetDivToHide + '\', \'' + req.calendarAnchorName + '\', \'' + req.eventsAnchorName + '\', \'' + nextMonth + '\', \'' + req.category + '\');return false;">&raquo;</a>';
          code += '</td></tr>';
          code += '</table>';
          x.innerHTML = code;
        } else {
          if (result.events.length == 0) {
            x.innerHTML = "Geen events gevonden";
          } else {
            var baseContent = document.getElementById(req.sourceDiv).innerHTML;
            for (i=0;i<result.events.length;++i) {
              var line = baseContent;
              line = line.replace(/@ID@/g, result.events[i].id);
              line = line.replace(/@TITLE@/g, result.events[i].title);
              line = line.replace(/@SUMMARY@/g, result.events[i].summary);
              line = line.replace(/@CONTENT@/g, result.events[i].content);
              line = line.replace(/@LOCATION@/g, result.events[i].location);
              line = line.replace(/@STARTDATE@/g, DateFormatter.format(new Date(result.events[i].startDate), 'd/m/Y H:i'));
              line = line.replace(/@ENDDATE@/g, DateFormatter.format(new Date(result.events[i].endDate), 'd/m/Y H:i'));
              line = line.replace(/@ORGANIZER@/g, result.events[i].organizer);
              x.innerHTML = x.innerHTML + line;
            }
          }
        }
      } else {
        var x = document.getElementById(req.resultDiv);
        x.innerHTML = "";
        x.style.display = 'block';
        x.innerHTML = "Er is een probleem opgetreden in de eventmanager.";
      }
    }
  }

  function eventReceived(req) {
    var request = req.request;
    if (request.readyState == 4) {

      if (request.status == 200) {
        var resultText = request.responseText;
        var result = eval('(' + resultText + ')');

        if (req.customCallback) {
          req.customCallback(result.event);
        }

        var x = document.getElementById(req.resultDiv);
        x.innerHTML = "";
        x.style.display = 'block';
        var baseContent = document.getElementById(req.sourceDiv).innerHTML;
        var line = baseContent;
        line = line.replace(/@ID@/g, result.event.id);
        line = line.replace(/@TITLE@/g, result.event.title);
        line = line.replace(/@SUMMARY@/g, result.event.summary);
        line = line.replace(/@CONTENT@/g, result.event.content);
        line = line.replace(/@LOCATION@/g, result.event.location);
        line = line.replace(/@STARTDATE@/g, DateFormatter.format(new Date(result.event.startDate), 'd/m/Y H:i'));
        line = line.replace(/@ENDDATE@/g, DateFormatter.format(new Date(result.event.endDate), 'd/m/Y H:i'));
        line = line.replace(/@ORGANIZER@/g, result.event.organizer);
        x.innerHTML = line;
      } else {
        var x = document.getElementById(req.resultDiv);
        x.innerHTML = "";
        x.style.display = 'block';
        x.innerHTML = "Er is een probleem opgetreden in de eventmanager.";
      }
    }
  }

  function getEvents(outputDiv, templateDiv, from, to, max, category) {
    var url = '/muzikantendag/servlet/jsonevent?method=getEvents';
    if (from) {
      url += '&from=' + from;
    }
    if (to) {
      url += '&to=' + to;
    }
    if (max) {
      url += '&max=' + max;
    }
    if (category && category != 'undefined') {
      url += '&category=' + category;
    }

    var ajax = new AjaxRequest(url, eventsReceived);
    ajax.resultDiv = outputDiv;
    ajax.sourceDiv = templateDiv;
    ajax.renderCalendar = false;
    ajax.send();
  }

  function renderCalendar(div, targetDiv, targetDivToHide, calendarAnchorName, eventsAnchorName, month, category) {
    var url;
    var cat = "";
    if (category && category != 'undefined') cat = '&category='+category;
    url = '/muzikantendag/servlet/jsonevent?method=getEvents&month=' + month + cat;
    var ajax = new AjaxRequest(url, eventsReceived);
    ajax.resultDiv = div;
    ajax.targetDiv = targetDiv;
    ajax.targetDivToHide = targetDivToHide;
    ajax.calendarAnchorName = calendarAnchorName;
    ajax.eventsAnchorName = eventsAnchorName;
    ajax.category = category;
    ajax.renderCalendar = true;
    ajax.send();
  }

  function getEvent(id, outputDiv, templateDiv, customCallback) {
    var url;
    url = '/muzikantendag/servlet/jsonevent?method=getEvent&id=' + id;
    var ajax = new AjaxRequest(url, eventReceived);
    ajax.resultDiv = outputDiv;
    ajax.sourceDiv = templateDiv;
    ajax.customCallback = customCallback;
    ajax.send();
  }

