  var monthNames=['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'];
  var weekdayNames=['M', 'D', 'W', 'D', 'V', 'Z', 'Z'];
  var weekdayOffset=1;//starts on monday (instead of sunday)

  var tooltipTimer=null;
  var CalHolderId;

  /*
   * @iYear integer, (fullyear)
   * @iMonth integer, (0..11, january..december)
   * returns json obj with {int days,int startweekday}
   */
  function getMonthSettings(iYear,iMonth)
  {
    var iMonthDays=32 - new Date(iYear,iMonth,32).getDate();
    
    // weekdagnr. Zondag..Zaterdag (0..6)
    var iWeekday=new Date(iYear,iMonth,1).getDay();

  	return {days:iMonthDays,startweekday:iWeekday};
  }

  /*
   * Convert js date object to separate integer parameters
   * @dateObj date, js date obj to convert to integer params.
   * Note: month starts with 0 (january)
   *  weekday starts with 0 (sunday)
   * returns json obj {int year,int month,int day,int weekday}
   */
  function getDateParts(dateObj)
  {
    var iYear  = dateObj.getYear();
    if(iYear<1000) iYear+=1900;//Y2K compliant

    return {year   :iYear
           ,month  :dateObj.getMonth()
           ,day    :dateObj.getDate()
           ,weekday:dateObj.getDay()};
  }


  /*
   * Build a matrix with all monthdays
   * @dateObj date, js date obj with month and year
   * @iWeekdayStart startday in first column to show with (sunday=0)
   * returns json 2d array 
   */
  function buildMonthMatrix(dateObj,iWeekdayStart)
  {
    //Build day matrix
    var oCalender=[];
    
    var iCols=7;//Seven days in a week

    var oDateParts=getDateParts(dateObj);
    var oStartSettings=getMonthSettings(oDateParts.year,oDateParts.month);

    //Nr cells to add at begin and end of calendar display matrix
    var iStartCells=0;
    var iEndCells  =0;
    if(oStartSettings.startweekday>iWeekdayStart)
    {
      iStartCells=(oStartSettings.startweekday-iWeekdayStart);
    }
    else if(oStartSettings.startweekday<iWeekdayStart)
    {
      iStartCells=iCols-iWeekdayStart-oStartSettings.startweekday;
    }

    var iRows=Math.ceil((oStartSettings.days+iStartCells)/iCols);
    iEndCells=(iRows*iCols)-(oStartSettings.days+iStartCells);

    var iCellsDone=0;
    for(var r=0;r<iRows;r++)
    {
      var oRow=[];
      for(var c=0;c<iCols;c++)
      {
        oCell={day:0
              ,month:oDateParts.month
              ,year:oDateParts.year
              ,cssClass:''};

        if(iCellsDone<iStartCells)
        {//Adding cells at start (previous month)

          oCell.cssClass='previous_m';
          oCell.month--;
          if(oCell.month<0)
          {
            oCell.month=11;//december previous year
            oCell.year--;
          }
          oCell.day=getMonthSettings(oCell.year,oCell.month).days-iStartCells+iCellsDone+1;

        }
        else if(iCellsDone<iStartCells+oStartSettings.days)
        {//actual days in selected month

          oCell.cssClass='current_m';
          oCell.day=iCellsDone-iStartCells+1;
          oCell.month=oDateParts.month;
          
          if(oDateParts.day==oCell.day) oCell.cssClass+=' today';
        }
        else
        {//Adding cells at end (next month)

          oCell.cssClass='next_m';
          oCell.month++;
          if(oCell.month>=12)
          {
            oCell.month=0;//january next year
            oCell.year++;
          }
          oCell.day=iCellsDone-(iStartCells+oStartSettings.days)+1;

        }
        iCellsDone++;

        oRow.push(oCell);
      }

      oCalender.push(oRow);
    }

    return oCalender;
  }

  function buildCalendarTable(monthMatrix,dateActive)
  {
    var hmltTable='';
    var iRows=monthMatrix.length;
    for(var r=0;r<iRows;r++)
    {
      if(r==0)
      {//table head

        var oDateParts=getDateParts(dateActive);
        var iPrevMonth=oDateParts.month-1;
        var iPrevYear =oDateParts.year;
        if(iPrevMonth<0)
        {
          iPrevMonth=11;
          iPrevYear--;
        }

        var iNextMonth=oDateParts.month+1;
        var iNextYear =oDateParts.year;
        if(iNextMonth>=12)
        {
          iNextMonth=0;
          iNextYear++;
        }

        hmltTable+='<tr class="month"><th class="first">&nbsp;</th><th colspan="7">';
        hmltTable+='<a href="javascript://" class="prevmonth" onclick="switchToMonth('+iPrevYear+','+iPrevMonth+');">&nbsp;</a>';
        hmltTable+=monthNames[oDateParts.month]+' '+oDateParts.year;
        hmltTable+='<a href="javascript://" class="nextmonth" onclick="switchToMonth('+iNextYear+','+iNextMonth+');">&nbsp;</a>';
        hmltTable+='</th><th class="last">&nbsp;</th></tr>';

        hmltTable+='<tr class="weekdays">';
        hmltTable+='<th class="first">&nbsp;</td>';
        for(var c=0;c<7;c++)
        {
          hmltTable+='<th>'+weekdayNames[c]+'</th>';
        }
        hmltTable+='<th class="last">&nbsp;</td>';
        hmltTable+='</tr>';

      }

      //The days of the month(s)
      hmltTable+='<tr>';
      var iCols=monthMatrix[r].length;
      hmltTable+='<td class="first">&nbsp;</td>';
      for(var c=0;c<iCols;c++)
      {
        //Check of er events gekoppeld zijn aan dag
        var eventIndex=-1;
        for(var ec=0;ec<calendarEvents.length;ec++)
        {
          if(calendarEvents[ec].y==monthMatrix[r][c].year && calendarEvents[ec].m==monthMatrix[r][c].month+1 && calendarEvents[ec].d==monthMatrix[r][c].day)
          {
            eventIndex=ec;
            ec=calendarEvents.length;
          }
        }
      
        if(eventIndex>-1)
        {
          hmltTable+='<td class="active '+monthMatrix[r][c].cssClass+'">';
          hmltTable+='<a href="#'+eventIndex+'">'+monthMatrix[r][c].day+'</a>';
          hmltTable+='</td>';
        }
        else
        {
          hmltTable+='<td class="'+monthMatrix[r][c].cssClass+'">'+monthMatrix[r][c].day+'</td>';      
        }
      }
      hmltTable+='<td class="last">&nbsp;</td>';
      hmltTable+='</tr>';

    }

    //Footer
    hmltTable+='<tr class="footer">';
    hmltTable+='<td class="first">&nbsp;</td>';
    hmltTable+='<td colspan="7"><a id="showagenda" href="#">Alle agendaitems &gt;</a></td>';
    hmltTable+='<td class="last">&nbsp;</td>';
    hmltTable+='</tr>';

    hmltTable='<table cellspacing="0">'+hmltTable+'</table>';
    
    return hmltTable;
  }

  function switchToMonth(iYear,iMonth)
  {
    var dateCalendar=new Date(iYear,iMonth,1);
    var oCalendar=buildMonthMatrix(dateCalendar,weekdayOffset);

    var calendarHtml=buildCalendarTable(oCalendar,dateCalendar);
    $('#'+CalHolderId).html(calendarHtml);

    bindCalendarEvents(CalHolderId);
  }

  function bindCalendarEvents(placeholderId)
  {
    infotoolObj=$(document.createElement('div')).attr('id','calendarinfo').appendTo('body');

    $(infotoolObj).mouseover(function(){
      clearTimeout(tooltipTimer);
    });

    $(infotoolObj).mouseout(function(){
      tooltipTimer=setTimeout(hideCalendarTooltip,1000);
    });

    $('#'+placeholderId+' td.active a').mouseover(function(ev){
      clearTimeout(tooltipTimer);

      var infotoolObj=$('#calendarinfo');
      $(infotoolObj).hide();

      var infoIndex=$(this).attr('href').replace(/([^0-9])/g,'');
      var myInfoHtml='<a href="'+calendarEvents[infoIndex].link+'"><strong>'+calendarEvents[infoIndex].title+'</strong><br />';
      myInfoHtml+=calendarEvents[infoIndex].descr+' <strong>&gt;</strong></a>';

      $(infotoolObj).html(myInfoHtml).css({'top':ev.pageY+'px','left':ev.pageX+'px'}).fadeIn(300);

    });

    $('#'+placeholderId+' td.active a').mouseout(function(ev){
      tooltipTimer=setTimeout(hideCalendarTooltip,1000);
    });

  }


  function hideCalendarTooltip()
  {
    clearTimeout(tooltipTimer);
    $('#calendarinfo').fadeOut(300,function(){$(this)});
  }


  function startCalendar(placeholderId)
  {
    CalHolderId=placeholderId;

    var dateCalendar = new Date();//Today
    var oCalendar=buildMonthMatrix(dateCalendar,weekdayOffset);

    var calendarHtml=buildCalendarTable(oCalendar,dateCalendar);
    $('#'+placeholderId).html(calendarHtml);

    bindCalendarEvents(placeholderId);

  }

