  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 findDayEntries(day, entries) {
    var dayEntries = [];
    for (var i=0;i<entries.length;++i) {
      var startDay = DateFormatter.format(new Date(entries[i].date), 'j');
      if (day == startDay) {
        dayEntries.push(i);
      }
    }
    return dayEntries;
  }

  function findDayEvents(day, events) {
    var dayEvents = [];
    for (var i=0;i<events.length;++i) {
      var startDay = DateFormatter.format(new Date(events[i].date), 'j');
      if (events[i].endDate != 0) {
        var endDay = DateFormatter.format(new Date(events[i].endDate), 'j');
        if (day >= startDay && day <= endDay) {
          dayEvents.push(i);
        }
      } else {
        if (day == startDay) {
          dayEvents.push(i);
        } else {
          for (var j=0;j<events[i].dates.length;++j) {
            var otherDay = DateFormatter.format(new Date(events[i].dates[j]), 'j');
            if (day == otherDay) {
              dayEvents.push(i);
            }
          }
        }
      }
    }
    return dayEvents;
  }

  function findEvent(day, events) {
    for (var i=0;i<events.length;++i) {
      var startDay = DateFormatter.format(new Date(events[i].date), 'j');
      if (events[i].endDate != 0) {
        var endDay = DateFormatter.format(new Date(events[i].endDate), 'j');
        if (day >= startDay && day <= endDay) {
          return true;
        }
      } else {
        if (day == startDay) {
          return true;
        } else {
          for (var j=0;j<events[i].dates.length;++j) {
            var otherDay = DateFormatter.format(new Date(events[i].dates[j]), 'j');
            if (day == otherDay) {
              return true;
            }
          }
        }
      }
    }
    return false;
  }
  
  function generateCalendarCodeEntries(req, result, entries) {
    var page = req.page;
    var funct = req.funct;
    var id = req.id;

    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");

    // month navigation 
    var code = '<table class="ppt_cal_table" cellspacing="0" cellpadding="0">';
    code += '<tr>';
    code += '<th colspan="7" class="ppt_cal_table_title">';
    code += '<a class="ppt_cal_arrow_right ppt_tooltip" href="#' + req.calendarAnchorName + '" onclick="' + funct + '(\'' + req.resultDiv + '\', \'' + req.targetDiv + '\', \'' + req.targetDivToHide + '\', \'' + req.calendarAnchorName + '\', \'' + req.eventsAnchorName + '\', ' + id + ', \'' + page + '\', \'' + nextMonth + '\');return false;" title="Next month"><span>next</span></a>';
    code += '<a class="ppt_cal_arrow_left ppt_tooltip" href="#' + req.calendarAnchorName + '" onclick="' + funct + '(\'' + req.resultDiv + '\', \'' + req.targetDiv + '\', \'' + req.targetDivToHide + '\', \'' + req.calendarAnchorName + '\', \'' + req.eventsAnchorName + '\', ' + id + ', \'' + page + '\', \'' + prevMonth + '\');return false;" title="Previous month"><span>previous</span></a>';
    code += '<span>' + monthStr + '</span>';
    code += '</th>';
    code += '</tr>';

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

    var day = 1, i = 0;
    var startWeekDay = DateFormatter.startWeekDay(new Date(result.month)) - 1;
    if (startWeekDay < 0) startWeekDay += 7;
    var lastDayOfMonth = DateFormatter.lastDayOfMonth(new Date(result.month));
    var lastDayOfPrevMonth = DateFormatter.lastDayOfMonth(new Date(result.prevMonth));

    // first row of days
    code += '<tr>';
    for (i=0;i<7;++i) {
      if (i < startWeekDay) {
        code += '<td><a class="ppt_cal_table_mask" href="#" onclick="return false;">' + (lastDayOfPrevMonth - (startWeekDay - i) + 1) + '</a></td>';
      } else {
        var dayEntriesA = findDayEntries(day, entries);
        if (dayEntriesA.length > 0) {
          var dayEntryA = entries[dayEntriesA[0]];
          code += '<td><a class="ppt_cal_table_mark" href="/';
          if (page.indexOf('?') == -1) code += page + '?id=' + dayEntryA.id;
          else code += page + '&id=' + dayEntryA.id;
          code += '" title="' + dayEntryA.title + '">' + day + '</a></td>';
        } else {
          code += '<td><a href="#" onclick="return false;">' + day + '</a></td>';
        }
        day++;
      }
    }
    code += '</tr>';

    // middle days
    while (day + 7 <= lastDayOfMonth) {
      code += '<tr>';
      for (i=0;i<7;++i) {
        var dayEntriesB = findDayEntries(day, entries);
        if (dayEntriesB.length > 0) {
          var dayEntryB = entries[dayEntriesB[0]];
          code += '<td><a class="ppt_cal_table_mark" href="/';
          if (page.indexOf('?') == -1) code += page + '?id=' + dayEntryB.id;
          else code += page + '&id=' + dayEntryB.id;
          code += '" title="' + dayEntryB.title + '">' + day + '</a></td>';
        } else {
          code += '<td><a href="#" onclick="return false;">' + day + '</a></td>';
        }
        day++;
      }
      code += '</tr>';
    }

    // last row of days
    finalEmptyFields = day - lastDayOfMonth + 6;
    code += '<tr>';
    while (day <= lastDayOfMonth) {
      var dayEntriesC = findDayEntries(day, entries);
      if (dayEntriesC.length > 0) {
        var dayEntryC = entries[dayEntriesC[0]];
        code += '<td><a class="ppt_cal_table_mark" href="/';
        if (page.indexOf('?') == -1) code += page + '?id=' + dayEntryC.id;
        else code += page + '&id=' + dayEntryC.id;
        code += '" title="' + dayEntryC.title + '">' + day + '</a></td>';
      } else {
        code += '<td><a href="#" onclick="return false;">' + day + '</a></td>';
      }
      day++;
    }
    for (i=0;i<finalEmptyFields;++i) {
      code += '<td><a class="ppt_cal_table_mask" href="#" onclick="return false;">' + (i + 1) + '</a></td>';
    }
    code += '</tr>';
    code += '</table>';
    
    return code;
  }
  
  function generateCalendarCodeEvents(req, result, events) {
    var page = req.page;
    var funct = req.funct;
    var id = req.id;

    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 prevMonthFunct = '';
    var nextMonthFunct = '';
    if (id) {
      prevMonthFunct = funct + "('" + req.resultDiv + "', '" + req.targetDiv + "', '" + req.targetDivToHide + "', '" + req.calendarAnchorName + "', '" + req.eventsAnchorName + "', " + id + ", '" + page + "', '" + prevMonth + "')";
      nextMonthFunct = funct + "('" + req.resultDiv + "', '" + req.targetDiv + "', '" + req.targetDivToHide + "', '" + req.calendarAnchorName + "', '" + req.eventsAnchorName + "', " + id + ", '" + page + "', '" + nextMonth + "')";
    } else {
      prevMonthFunct = funct + "('" + req.resultDiv + "', '" + req.targetDiv + "', '" + req.targetDivToHide + "', '" + req.calendarAnchorName + "', '" + req.eventsAnchorName + "', '" + page + "', '" + prevMonth + "', '" + req.type + "')";
      nextMonthFunct = funct + "('" + req.resultDiv + "', '" + req.targetDiv + "', '" + req.targetDivToHide + "', '" + req.calendarAnchorName + "', '" + req.eventsAnchorName + "', '" + page + "', '" + nextMonth + "', '" + req.type + "')";
    }

    // month navigation 
    var code = '<table class="ppt_cal_table" cellspacing="0" cellpadding="0">';
    code += '<tr>';
    code += '<th colspan="7" class="ppt_cal_table_title">';
    code += '<a class="ppt_cal_arrow_right ppt_tooltip" href="#' + req.calendarAnchorName + '" onclick="' + nextMonthFunct + ';return false;" title="Next month"><span>next</span></a>';
    code += '<a class="ppt_cal_arrow_left ppt_tooltip" href="#' + req.calendarAnchorName + '" onclick="' + prevMonthFunct + ';return false;" title="Previous month"><span>previous</span></a>';
    code += '<span>' + monthStr + '</span>';
    code += '</th>';
    code += '</tr>';

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

    var day = 1, i = 0, j = 0;
    var startWeekDay = DateFormatter.startWeekDay(new Date(result.month)) - 1;
    if (startWeekDay < 0) startWeekDay += 7;
    var lastDayOfMonth = DateFormatter.lastDayOfMonth(new Date(result.month));
    var lastDayOfPrevMonth = DateFormatter.lastDayOfMonth(new Date(result.prevMonth));

    // first row of days
    code += '<tr>';
    for (i=0;i<7;++i) {
      if (i < startWeekDay) {
        code += '<td><a class="ppt_cal_table_mask" href="#" onclick="return false;">' + (lastDayOfPrevMonth - (startWeekDay - i) + 1) + '</a></td>';
      } else {
        var dayEventsA = findDayEvents(day, events);
        if (dayEventsA.length > 0) {
          var titleA = '';
          if (dayEventsA.length == 1) {
            titleA = events[dayEventsA[0]].title;
          } else if (dayEventsA.length > 1) {
            titleA = '<ul>';
            for (j=0;j<dayEventsA.length;j++) {
              titleA += '<li>' + events[dayEventsA[j]].title + '</li>';
            }
            titleA += '</ul>'
          }
          code += '<td><a title="' + titleA.replace(/"/g, '\'') + '" class="ppt_cal_table_mark" href="/';
          if (page.indexOf('?') == -1) code += page + '?date=' + month;
          else code += page + '&date=' + month;
          if (day < 10) code += '0';
          code += day + '">' + day + '</a></td>';
        } else {
          code += '<td><a href="#" onclick="return false;">' + day + '</a></td>';
        }
        day++;
      }
    }
    code += '</tr>';

    // middle days
    while (day + 7 <= lastDayOfMonth) {
      code += '<tr>';
      for (i=0;i<7;++i) {
        var dayEventsB = findDayEvents(day, events);
        if (dayEventsB.length > 0) {
          var titleB = '';
          if (dayEventsB.length == 1) {
            titleB = events[dayEventsB[0]].title;
          } else if (dayEventsB.length > 1) {
            titleB = '<ul>';
            for (j=0;j<dayEventsB.length;j++) {
              titleB += '<li>' + events[dayEventsB[j]].title + '</li>';
            }
            titleB += '</ul>'
          }
          code += '<td><a title="' + titleB.replace(/"/g, '\'') + '" class="ppt_cal_table_mark" href="/';
          if (page.indexOf('?') == -1) code += page + '?date=' + month;
          else code += page + '&date=' + month;
          if (day < 10) code += '0';
          code += day + '">' + day + '</a></td>';
        } else {
          code += '<td><a href="#" onclick="return false;">' + day + '</a></td>';
        }
        day++;
      }
      code += '</tr>';
    }

    // last row of days
    finalEmptyFields = day - lastDayOfMonth + 6;
    code += '<tr>';
    while (day <= lastDayOfMonth) {
      var dayEventsC = findDayEvents(day, events);
      if (dayEventsC.length > 0) {
        var titleC = '';
        if (dayEventsC.length == 1) {
          titleC = events[dayEventsC[0]].title;
        } else if (dayEventsC.length > 1) {
          titleC = '<ul>';
          for (j=0;j<dayEventsC.length;j++) {
            titleC += '<li>' + events[dayEventsC[j]].title + '</li>';
          }
          titleC += '</ul>'
        }
        code += '<td><a title="' + titleC.replace(/"/g, '\'') + '" class="ppt_cal_table_mark" href="/';
        if (page.indexOf('?') == -1) code += page + '?date=' + month;
        else code += page + '&date=' + month;
        if (day < 10) code += '0';
        code += day + '">' + day + '</a></td>';
      } else {
        code += '<td><a href="#" onclick="return false;">' + day + '</a></td>';
      }
      day++;
    }
    for (i=0;i<finalEmptyFields;++i) {
      code += '<td><a class="ppt_cal_table_mask" href="#" onclick="return false;">' + (i + 1) + '</a></td>';
    }
    code += '</tr>';
    code += '</table>';
    
    return code;
  }

  function entriesReceived(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) {
          x.innerHTML = generateCalendarCodeEntries(req, result, result.entries)
        } else {
          if (result.entries.length == 0) {
            x.innerHTML = "Geen entries gevonden";
          } else {
            var baseContent = document.getElementById(req.sourceDiv).innerHTML;
            for (i=0;i<result.entries.length;++i) {
              var line = baseContent;
              line = line.replace(/@ID@/g, result.entries[i].id);
              line = line.replace(/@TITLE@/g, result.entries[i].title);
              line = line.replace(/@SUMMARY@/g, result.entries[i].summary);
              line = line.replace(/@CONTENT@/g, result.entries[i].content);
              line = line.replace(/@LOCATION@/g, result.entries[i].location);
              line = line.replace(/@STARTDATE@/g, DateFormatter.format(new Date(result.entries[i].startDate), 'd/m/Y H:i'));
              line = line.replace(/@ENDDATE@/g, DateFormatter.format(new Date(result.entries[i].endDate), 'd/m/Y H:i'));
              line = line.replace(/@ORGANIZER@/g, result.entries[i].organizer);
              x.innerHTML = x.innerHTML + line;
            }
          }
        }
      } else {
        var div = document.getElementById(req.resultDiv);
        div.innerHTML = "";
        div.style.display = 'block';
        div.innerHTML = "Er is een probleem opgetreden in de entrymanager.";
      }
    }
  }

  function clubEventsReceived(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) {
          x.innerHTML = generateCalendarCodeEvents(req, result, result.events);
        }
        tooltips();
      } else {
        var div = document.getElementById(req.resultDiv);
        div.innerHTML = "";
        div.style.display = 'block';
        div.innerHTML = "Er is een probleem opgetreden in de entrymanager.";
      }
    }
  }

  function entryReceived(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 div = document.getElementById(req.resultDiv);
        div.innerHTML = "";
        div.style.display = 'block';
        div.innerHTML = "Er is een probleem opgetreden in de entriesmanager.";
      }
    }
  }

  function getEntries(outputDiv, templateDiv, feed, from, to, max) {
    var url = '/servlet/jsonfeed?method=entries&feedid=' + feed;
    if (from) {
      url += '&from=' + from;
    }
    if (to) {
      url += '&to=' + to;
    }
    if (max) {
      url += '&max=' + max;
    }

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

  function renderCalendar(div, targetDiv, targetDivToHide, calendarAnchorName, eventsAnchorName, feed, page, month) {
    var url;
    url = '/servlet/jsonfeed?action=entriesWithDate&feedid=' + feed + '&month=' + month;
    var ajax = new AjaxRequest(url, entriesReceived);
    ajax.resultDiv = div;
    ajax.targetDiv = targetDiv;
    ajax.targetDivToHide = targetDivToHide;
    ajax.calendarAnchorName = calendarAnchorName;
    ajax.eventsAnchorName = eventsAnchorName;
    ajax.renderCalendar = true;
    ajax.page = page;
    ajax.id = feed;
    ajax.funct = 'renderCalendar';
    ajax.send();
  }

  function renderEventCalendar(div, targetDiv, targetDivToHide, calendarAnchorName, eventsAnchorName, page, month, type) {
    var url = '/servlet/jsonevent?method=getEvents&month=' + month + (type === undefined ? '' : '&type=' + type);
    var ajax = new AjaxRequest(url, clubEventsReceived);
    ajax.resultDiv = div;
    ajax.targetDiv = targetDiv;
    ajax.targetDivToHide = targetDivToHide;
    ajax.calendarAnchorName = calendarAnchorName;
    ajax.eventsAnchorName = eventsAnchorName;
    ajax.renderCalendar = true;
    ajax.page = page;
    ajax.funct = 'renderEventCalendar';
    ajax.type = type;
    ajax.send();
  }

  function renderClubEventCalendar(div, targetDiv, targetDivToHide, calendarAnchorName, eventsAnchorName, club, page, month) {
    var url;
    url = '/servlet/jsonevent?method=getClubEvents&clubid=' + club + '&month=' + month;
    var ajax = new AjaxRequest(url, clubEventsReceived);
    ajax.resultDiv = div;
    ajax.targetDiv = targetDiv;
    ajax.targetDivToHide = targetDivToHide;
    ajax.calendarAnchorName = calendarAnchorName;
    ajax.eventsAnchorName = eventsAnchorName;
    ajax.renderCalendar = true;
    ajax.page = page;
    ajax.id = club;
    ajax.funct = 'renderClubEventCalendar';
    ajax.send();
  }

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