var MilliInDay = 24 * 60 * 60 * 1000;

var arrShortMonth = new Array(
	"jan",
	"feb",
	"mrt",
	"apr",
	"mei",
	"jun",
	"jul",
	"aug",
	"sep",
	"okt",
	"nov",
	"dec"
	);
var arrLongMonth = new Array(
	"januari",
	"februari",
	"maart",
	"april",
	"mei",
	"juni",
	"juli",
	"augustus",
	"september",
	"oktober",
	"november",
	"december"
	);
function BindCal(elm, txt)
{
  var cal = new Cal(elm, txt);
  cal.updateFromBox();
}

function Cal(table, txt)
{
  // Het HTML element
  this._elm = table;
  table.MyObj = this;
  
  // De textbox waarin de datum staat
  this.txt = txt;
  txt.MyCal = this;
  
  // Bevat de geselecteerde datum (dat is de datum die in het textboxje te zien is)
  this.selectedDate = null;

  // Bevat de eerste datum die 'in beeld' is.
  this.startVisDate = null;
  // Bevat de laatste datum die 'in beeld' is.
  this.endVisDate = null;
  
  // Bevat de eerste van de eerste maand in beeld.
  this.firstOfFirstMonth = null;
  
  // Bevat de laatste van de eerste maand in beeld (dat hoeft niet de laatste van de maand te zijn).
  this.lastOfFirstMonth = null;
  
  // Bevat de eerste van de tweede maand in beeld.
  this.firstOfSecondMonth = null;
  
  
  
  // Bevat de vroegste datum die geselecteerd mag worden
  this.max = null;
  // Bevat de laatste datum die geselecteerd mag worden.
  this.min = null;
  
  if(this._elm.getAttribute("MinValue") != null)
  {
    this.min = TxtToDate(this._elm.getAttribute("MinValue"));
  }
  if(this._elm.getAttribute("MaxValue"))
  {
    this.max = TxtToDate(this._elm.getAttribute("MaxValue"));
  }
  
  this.update = Cal_Update;
  this.updateFromBox = Cal_UpdateFromBox;
  this.setStartVisDate = Cal_SetStartVisDate;

  xAddEventListener(this._elm, "click", DateClick, false);
  xAddEventListener(this._elm, "mouseover", DateOver, false);
  xAddEventListener(this._elm, "mouseout", DateOut, false);
  
}


/*
  Deze functie bouwt de gehele kalender opnieuw op op basis van de selectedDate,
  startVisDate en endVisDate. De waarden van deze velden wordt hierbij niet 
  veranderd. 
  
  Deze methode wordt aangeroepen nadat een waarde gewijzigd is, bijvoorbeeld 
  door klikken op een next/prev button of het selecteren van een datum.
*/
function Cal_Update()
{
  var today = new Date();
  today = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate()));

  // Change the window of visible dates if no date is set at all  
  if(!this.selectedDate && !(this.startVisDate && this.endVisDate))
  {
    //verander de startdate
    this.setStartVisDate(today);
  }
  
  // Now loop through all of the cells and set the correct date
  // month 1
  var startMillis=this.startVisDate.getTime();
  
  for(var week = 0; week < 5; week++)
  {
    for(var weekday = 0; weekday < 7; weekday++)
    {
      var currDate = new Date(startMillis + ((week * 7)+ weekday) * MilliInDay);
      

			// format the date cells
      var cell = this._elm.rows[week + 2].cells[weekday];
      
      if(currDate >=this.firstOfSecondMonth)
      {
				cell.innerHTML = "";
				cell.className = "";
				continue;
      }
      
      cell.innerHTML = currDate.getUTCDate();
      if(currDate >= this.firstOfFirstMonth)
      {
        cell.className = "calDay";
      }else{
        cell.className = "otherMonthDay";
      }
      if(this.selectedDate && currDate.getTime() == this.selectedDate.getTime())
				cell.className += " selDay";
      if(currDate.getTime() == today.getTime())
				cell.className += " thisDay";
			if((this.max && currDate.getTime() > this.max.getTime()) || (this.min && this.min.getTime() > currDate.getTime()))
			{
				cell.className += " badDay";
				cell.date = null;
			}else{
				cell.date = currDate;
			}
    }
  }
  // month 2
  startMillis=mondayOnOrBefore(this.firstOfSecondMonth).getTime();
  
  for(var week = 0; week < 5; week++)
  {
    for(var weekday = 0; weekday < 7; weekday++)
    {
      var currDate = new Date(startMillis + ((week * 7)+ weekday) * MilliInDay);

			// format the date cells
      var cell = this._elm.rows[week + 2].cells[weekday + 8];
      
      if(currDate < this.firstOfSecondMonth && currDate <= this.lastOfFirstMonth)
      {
				cell.innerHTML = "";
				cell.className = "";
				continue;
      }
      
      cell.innerHTML = currDate.getUTCDate();
      var firstOfNextMonth =new Date(Date.UTC(this.firstOfSecondMonth.getUTCFullYear(), this.firstOfSecondMonth.getUTCMonth() + 1, 1));   

      if(currDate < firstOfNextMonth)
      {
        cell.className = "calDay";
      }else{
        cell.className = "otherMonthDay";
      }
      if(this.selectedDate && currDate.getTime() == this.selectedDate.getTime())
				cell.className += " selDay";
      if(currDate.getTime() == today.getTime())
				cell.className += " thisDay";
			if((this.max && currDate.getTime() > this.max.getTime()) || (this.min && this.min.getTime() > currDate.getTime()))
			{
				cell.className += " badDay";
				cell.date = null;
			}else{
				cell.date = currDate;
			}
    }
  }
  if(this.selectedDate)
  {
    this.txt.value = DateToTxt(this.selectedDate);
  }
  
  // Update the month and year indicators
  
  var firstMonth = this.firstOfFirstMonth.getUTCMonth();
  var lastMonth = this.firstOfSecondMonth.getUTCMonth();
  this._elm.rows[0].cells[2].innerHTML = arrLongMonth[firstMonth] + " " + this.firstOfFirstMonth.getUTCFullYear();
  this._elm.rows[0].cells[4].innerHTML = arrLongMonth[lastMonth] + " " + this.firstOfSecondMonth.getUTCFullYear();
}

/*
  Deze methode stelt dat kalender opnieuw in op basis van de waarde van het 
  textboxje in this.txt. Zonodig wordt ook het zchtbare gebied tussen startVisDate 
  en endVisDate aangepast om de nieuwe selectedDate in beeld te krijgen.
  
  Tot slot roepen we update() aan.
*/

function Cal_UpdateFromBox()
{
  // update date from textbox
  var d;
  if(this.txt)
  {
    d = TxtToDate(this.txt.value);
  }
  if(d )this.selectedDate = d;

  if(this.selectedDate && 
			(
			  this.selectedDate < this.startVisDate ||
			  this.selectedDate > this.endVisDate 
			)
		)
  {
    // Change the window of dates when the selected date falls outside
    this.setStartVisDate(new Date(this.selectedDate.getTime()));
  }
  this.update();
}

/*
  Deze functie past het zichtbare window zo aan dat de doorgegeven datum op 
  de eerste rij terecht komt. Dit is de enige 'officiele' manier om de waardes
  van startVisDate en endVisDate te wijzigen.
*/
function Cal_SetStartVisDate(d)
{
  // Bereken de eerste van de maand
  var firstOfMonth = new Date(d.getTime() - MilliInDay * (d.getUTCDate() -1));
  this.startVisDate = mondayOnOrBefore(firstOfMonth);
  this.firstOfFirstMonth = firstOfMonth;
  
  // Bereken laatste dag van volgende maand
  var firstOfNext = new Date(Date.UTC(firstOfMonth.getUTCFullYear(), firstOfMonth.getUTCMonth() + 1, 1));
  this.firstOfSecondMonth = firstOfNext;
  // pak nu de maandag voor de firstOfNext + 5 wk
  this.endVisDate = sundayOnOrAfter(new Date(firstOfNext.getTime() + MilliInDay * 7 * 4));
  this.lastOfFirstMonth = new Date(this.startVisDate.getTime() + MilliInDay * (5 * 7 -1));
}

/*
  De event handler voor het klikken op de kalender. De handler zit op de 
  gehele tabel, dus moet wel goed gecontrolleerd worden of er wel echt op 
  een klikbare dag is geklikt.
*/
function DateClick(e)
{
  var xEvt = new xEvent(e);
  if(!xEvt.target.className.match(/(calDay|otherMonthDay)/))return;
  if(!xEvt.target.date)return;
  var cal = xEvt.target.parentNode.parentNode.parentNode.MyObj;
  if(cal && cal.txt.value != DateToTxt(xEvt.target.date))
  {
    cal.txt.value = DateToTxt(xEvt.target.date);
    if(typeof(cal.txt.onchange == "function"))cal.txt.onchange();
  }
  if(cal)HideCal(cal._elm.id);
  
}

/*
  De event handler voor het oplichten van de klikbare dag vakjes 
  onmouseover. We habben niet op ieder vakje een eventhandler gezet 
  (dat werden wel erg veel event handlers), dus we moeten hier nog 
  even goed kijken of we wel echt bover een klikbaar vakje zitten.
*/
function DateOver(e)
{
  var xEvt = new xEvent(e);
  if(!xEvt.target.date)return;
  if(!xEvt.target.className.match(/(calDay|otherMonthDay)/))return;
  
  var cell = xEvt.target;
  if(cell)
  {
    cell.className += " calDayHover";
  }
}
function DateOut(e)
{
  var xEvt = new xEvent(e);
  if(!xEvt.target.className.match(/(calDay|otherMonthDay)/))return;
  var cell = xEvt.target;
  if(cell)
  {
    cell.className = cell.className.replace(" calDayHover", "");
  }
}

/*
  Algemene functie om op een vaste manier een Date te converteren naar een String
*/
function DateToTxt(d)
{
  var s = "";
  s += Pad(d.getDate(), "0", 2);
  s += "-";
  
  s += Pad(d.getMonth() + 1, "0", 2);
  s += "-";
  
  s += Pad(d.getFullYear(), "0", 4);
  return s;
}
/*
  Algemene functie om op een vaste manier een string te converteren naar een Date
*/
function TxtToDate(s)
{
  var d;
  var res = s.match(/^(\d\d)-(\d\d)-(\d\d\d\d)$/);
  if(res)
  {
	  d = new Date(Date.UTC(Number(res[3]), Number(res[2]) - 1, Number(res[1])));
  }
  return d;
}

/*
  Vind de maandag voor of op de meegegeven datum. Een hulp functie om eenvoudig het begin van 
  een week te bepalen
*/
function mondayOnOrBefore(d)
{
  var monday = new Date(d.getTime() - MilliInDay * (d.getUTCDay() -1));
  // Zondag correctie:
  if(d.getUTCDay() == 0)monday = new Date(monday.getTime() - 7 * MilliInDay);
  return monday;
}
function sundayOnOrAfter(d)
{
  var monday = mondayOnOrBefore(d);
  // plus zes dagen
  return new Date(monday.getTime() + MilliInDay * 6);
}

/*
  Vult een string aan met een vast karacter tot de gewenste lengte.
  4 -> 04
*/ 
function Pad(base, character, length)
{
  var strBase = String(base);
  while(strBase.length < length)
  {
    strBase = character + strBase;
  }
  return strBase;
}

/*
  Event handler voor het Next Month knopje
*/
function OnCalNext(e)
{
  var Evt = new xEvent(e);
  var cal = Evt.target.parentNode.parentNode.parentNode.parentNode.MyObj;
  if(cal)
  {
    cal.setStartVisDate(new Date(Date.UTC(cal.firstOfFirstMonth.getUTCFullYear(), cal.firstOfFirstMonth.getUTCMonth() + 1, 1)));
    cal.update();
  }
}
/*
  Event handler voor het Previous Month knopje
*/
function OnCalPrev(e)
{
  var Evt = new xEvent(e);
  var cal = Evt.target.parentNode.parentNode.parentNode.parentNode.MyObj;
  if(cal)
  {
    cal.setStartVisDate(new Date(Date.UTC(cal.firstOfFirstMonth.getUTCFullYear(), cal.firstOfFirstMonth.getUTCMonth() - 1, 1)));
    cal.update();
  }
}
/*
  Event handler voor het Next Year knopje
*/
function OnCalNextNext(e)
{
  var Evt = new xEvent(e);
  var cal = Evt.target.parentNode.parentNode.parentNode.parentNode.MyObj;
  if(cal)
  {
    cal.setStartVisDate(new Date(Date.UTC(cal.firstOfFirstMonth.getUTCFullYear() + 1, cal.firstOfFirstMonth.getUTCMonth(), 1)));
    cal.update();
  }
}
/*
  Event handler voor het Previous Year knopje
*/
function OnCalPrevPrev(e)
{
  var Evt = new xEvent(e);
  var cal = Evt.target.parentNode.parentNode.parentNode.parentNode.MyObj;
  if(cal)
  {
    cal.setStartVisDate(new Date(Date.UTC(cal.firstOfFirstMonth.getUTCFullYear() - 1, cal.firstOfFirstMonth.getUTCMonth(), 1)));
    cal.update();
  }
}
function ShowCal(id)
{
  var cal = xGetElementById(id);
  cal.style.display = "block";
}
function HideCal(id)
{
  var cal = xGetElementById(id);
  cal.style.display = "none";
}