/**	
	The calendar script is a 'standalone' pluggable calendar, it bootstraps itself by modifiying the DOM model using Javascript.
	
	How to consturct:
		call 'new Calendar' with the correct arguments (see below)
	
	How to use:
		Implement a callback function. 
		The callbackfunction is given two arguments
			- The first argument is the date as a string: YYYY-MM-DD (without leading zeroes)
			- The second argument is the Javascript date object !!NOTE!! Javascript months start at 0, so you'll have to add 1 yourself!!

	For added visual compatibility add a stylesheet.
		Template can be found at the bottom of this file
		
	Issues:
	 -	Javascript's months start at 0, some increments and decrements are automatically done. 
		For the user of this component months start at 1 (till 12)
*/

/**
	@parentId Id of the DOM element in which the calendar should print itself
	@callbackfunc name of the function to be exectued when a day is clicked	
	Three dates:
	 -	minimal relative date (negative values are possible)
	 -	maxmimal relative date (negative values are possible)
	 -	selected date
			if selected date is below the minimal date or above the maximal date it will replace the respective boundry so that the 
			selected date is always visible

	@yearmin 	years from today, lowest selectable year
	@monthmin 	months from today, lowest selectable month, stacks with year
	@daymin 	days from today, lowest selectable day, stacks with month
	@yearmax 	years from today, lowest selectable year
	@monthmax 	months from today, lowest selectable month, stacks with year
	@daymax 	days from today, lowest selectable day, stacks with month
	@selyear	selected year
	@selmonth	selected month
	@selday		selected day
*/
function Calendar(parentId, callbackfunc, yearmin, monthmin, daymin, yearmax, monthmax, daymax, selyear, selmonth, selday, prntInput) {
	
	this.months = new Array("januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december");
	this.weekdays = new Array("zondag","maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag");
	this.text = new Array();
	this.text["selected"] = "geselecteerde datum";
	this.text["select"] = "klik om datum te selecteren";
	this.text["today"] = "vandaag";
	this.today = new Date();
	
	this.callbackfunc = callbackfunc;
	
	this.minIntervalDate = new Date(this.today.getFullYear() + yearmin, this.today.getMonth() + monthmin, this.today.getDate() + daymin);
	this.maxIntervalDate = new Date(this.today.getFullYear() + yearmax, this.today.getMonth() + monthmax, this.today.getDate() + daymax);
	this.selectedDate = new Date(selyear, selmonth-1, selday);
	
	this.selectYear = false;
	this.selectMonth = false;
	
	this.calendarBody = false;
	
	/**
		construct the calendar (construct is called at the bottom of the class definition)
	*/
	this.construct = function(parentId) {
		
		// make calendar table
		var parent = document.getElementById(parentId);
		var table = libAppendElement(parent, "table");
		table.className = "ecardCalendar";
		
		// make the top row with the two select boxes
		this.calendarBody = libAppendElement(table, "tbody");
		var column = libAppendElement(libAppendElement(this.calendarBody, "tr"), "td");
		column.colSpan = "7";
		column.style.borderWidth = "0px";
		column.style.width = "auto";
		column.style.textAlign = "left";
		
		// make year selectbox
		this.selectYear = libAppendElement(column, "select");
		this.selectYear.className = "ecardCalendarYear";
		for(var i = this.minIntervalDate.getFullYear(); i <= this.maxIntervalDate.getFullYear(); i++) {
			this.selectYear[i-this.minIntervalDate.getFullYear()] = new Option(i, i, false);
			if(i == this.selectedDate.getFullYear())
				this.selectYear.selectedIndex = i-this.minIntervalDate.getFullYear();
		}
		
		// make month seletbox
		this.selectMonth = libAppendElement(column, "select");
		this.selectMonth.className = "ecardCalendarMonth";
		var that = this
		this.selectYear.onchange = function() {
			that.drawMonthSelector();
			that.drawDaySelector();
		}
		
		// day
		this.selectMonth.onchange = function() {
			that.drawDaySelector();
		}
		
		this.selectYear.onchange(); // fire onchange to draw the month and day selectors
		
		this.callCallBackFunc(this.selectedDate.getFullYear(),
							this.selectedDate.getMonth(),
							this.selectedDate.getDate());
		if (prntInput) {
			inputfield = document.getElementById(prntInput);
			inputfield.onblur = function() {
				hideCalendar(parent);
			}
		}

		parent.onmouseover = function() {
			setOnOver(false);
		}

		parent.onmouseout = function() {
			setOnOver(true);
		}
	}

	/**
		Draw a number of rows with 7 columns, one cell for each day of the seleted month/year
		@selectDate, tbody of the date table DOM-element
		@selectMonth, selectbox DOM-element
		@selectYear, selectbox DOM-element
		@minIntervalDate, today
		@maxIntervalDate, today + years/months/days ahead
	*/
	this.drawDaySelector = function() {
		while(this.calendarBody.childNodes.length > 1)
			this.calendarBody.removeChild(this.calendarBody.lastChild);
		var year	= this.selectYear.options[this.selectYear.selectedIndex].value-0;
		var month	= this.selectMonth.options[this.selectMonth.selectedIndex].value-0;
		var minDate = (year == this.minIntervalDate.getFullYear()
						&& month == this.minIntervalDate.getMonth()	? this.minIntervalDate.getDate() : 1);
		var maxDate = (year == this.maxIntervalDate.getFullYear()
						&& month == this.maxIntervalDate.getMonth()	? this.maxIntervalDate.getDate() : 
							new Date(year, month+1, 0).getDate());
		var columnIndex = 0;
		var row = libAppendElement(this.calendarBody, "tr");
		
		// draw weekdays header row
		for(var i = 0; i < 7; i++)
			libAppendTextNode(libAppendElement(row, "th"), this.weekdays[i].substr(0,2));

		// draw empty unselectable cells for each day that is not in this month,  to pad the table propperly
		row = libAppendElement(this.calendarBody, "tr");
		var day = 1;
		var firstDayOfMonth = new Date(year, month, 1).getDay()
		while(columnIndex < firstDayOfMonth) {
			var td = libAppendElement(row, "td");
			libAppendElement(td, "a").className = "unselectable";
			columnIndex++;
		}
		
		// draw  unselectable cells for each day that is in this month but is not selectable
		while(day < minDate) {
			var td = libAppendElement(row, "td");
			libAppendTextNode(libAppendElement(td, "a"), day).className = "unselectable";
			columnIndex++;
			day++;
			if(columnIndex > 6) {
				columnIndex = 0;
				row = libAppendElement(this.calendarBody, "tr");
			}
		}
		
		//draw selectable cells for each seletable day
		while(day <= maxDate) {
			if(day <= maxDate && columnIndex > 6) {
				columnIndex = 0;
				row = libAppendElement(this.calendarBody, "tr");
			}
			var td = libAppendElement(row, "td");
			var a = libAppendTextNode(libAppendElement(td, "a"), day);
			if(this.selectedDate.getDate() == day
				&& this.selectedDate.getMonth() == month
				&& this.selectedDate.getFullYear() == year) {	// this day is selected?
				addOnMouse(a, "selected", "selected_hover");
				a.title = this.text["selected"];
				this.selectedElement = a;
			} else if (this.today.getDate() == day
				&& this.today.getMonth() == month
				&& this.today.getFullYear() == year) {	// this day is selected?
				addOnMouse(a, "today", "today_hover");
				a.title = this.text["today"];
			} else {
				addOnMouse(a, "", "hover");
				a.title = this.text["select"];
			}
			// add onlick event
			this.setDatumOnClick(a, year, month, day);
			day++;
			columnIndex++;
		}
		
		// draw  unselectable cells for each day that is in this month but is not selectable
		var lastDayOfMonth = new Date(year, month+1, 0).getDate()
		while(day < lastDayOfMonth) {
			if(columnIndex > 6) {
				columnIndex = 0;
				row = libAppendElement(this.calendarBody, "tr");
			}
			var td = libAppendElement(row, "td");
			libAppendTextNode(libAppendElement(td, "a"), day).className = "unselectable";
			columnIndex++;
			day++;
		}
		
		// draw empty unselectable cells for each day that is not in this month, to pad the table propperly
		while(columnIndex < 7) {
			var td = libAppendElement(row, "td");
			libAppendElement(td, "a").className = "unselectable";
			columnIndex++;
		}		
	}

	/**
		Set the onclick event on a table cell
		@year, the selected year
		@month, the select month
		@day, the selected day
		@selectDate, tbody of the date table DOM-element
		@selectMonth, selectbox DOM-element
		@selectYear, selectbox DOM-element
		@minIntervalDate, today
		@maxIntervalDate, today + years/months/days ahead
		
	*/
	this.setDatumOnClick = function(td, year, month, day) {
		var that = this;
		td.onclick = function() {
			that.callCallBackFunc(year, month, day);
		}
	}
	
	this.callCallBackFunc = function(year, month, day) {
		this.selectedDate = new Date(year, month, day);
		this.callbackfunc(year + "-" + (month + 1) + "-" + day, this.selectedDate);	// call the callback function
		this.drawDaySelector();
	}
	
	/**
		draw the month select box
		@selectMonth, selectbox DOM-element
		@selectYear, selectbox DOM-element
		@minIntervalDate, today
		@maxIntervalDate, today + years/months/days ahead
	*/
	this.drawMonthSelector = function() {
		var year = this.selectYear.options[this.selectYear.selectedIndex].value;		
		var minMonth = (this.minIntervalDate.getFullYear() == year ? this.minIntervalDate.getMonth() : 0);
		var endMonth = (this.maxIntervalDate.getFullYear() == year ? this.maxIntervalDate.getMonth() : 11);
		var selectedMonth = (this.selectedDate.getFullYear() == year ? this.selectedDate.getMonth() : 0);
		this.selectMonth.options.length = 0;
		for(var i = minMonth; i <= endMonth; i++) {
			this.selectMonth[i-minMonth] = new Option(this.months[i], i, false);
			if(i == selectedMonth)
				this.selectMonth.selectedIndex = i-minMonth;
		}
	}

	/**
		check if selectedDate falls within in the dat interval, if it doesn't set the interval so that it DOES fit
		modifies globals
	*/
	this.checkInterval = function() {
		if ((this.selectedDate.getFullYear() < this.minIntervalDate.getFullYear())
			|| (this.selectedDate.getMonth() < this.minIntervalDate.getMonth()
				&& this.selectedDate.getFullYear()	== this.minIntervalDate.getFullYear())
			|| (this.selectedDate.getDate() < this.minIntervalDate.getDate()
				&& this.selectedDate.getFullYear()	== this.minIntervalDate.getFullYear()
				&& this.selectedDate.getMonth()		== this.minIntervalDate.getMonth())) {
			this.minIntervalDate = this.selectedDate;
		} else 	if ((this.selectedDate.getFullYear() > this.maxIntervalDate.getFullYear())
			|| (this.selectedDate.getMonth() > this.maxIntervalDate.getMonth()
				&& this.selectedDate.getFullYear()	== this.maxIntervalDate.getFullYear())
			|| (this.selectedDate.getDate() > this.maxIntervalDate.getDate()
				&& this.selectedDate.getFullYear()	== this.maxIntervalDate.getFullYear()
				&& this.selectedDate.getMonth()		== this.maxIntervalDate.getMonth())) {
			this.maxIntervalDate = this.selectedDate;
		}
	}
	
	this.checkInterval();
	
	this.construct(parentId);
}

var calOnOver = false;

/**
	Create a DOM-HTML element and append it to the parent
	@parent, parent to append DOM-HTML element to
	@elementString, name of the DOM-HTML element
	@return, the created DOM-HTML element
*/
function libAppendElement(parent, elementString) {
	var child = document.createElement(elementString);
	parent.appendChild(child);
	return child;
}

/**
	Append a piece of text
	@parent, parent to append the text to
	@text, String, the text
	@return, the parent
*/
function libAppendTextNode (parent, text) {
	parent.appendChild(document.createTextNode(text));
	return parent;
}

/**
	Set a hover class for a DOM-HTML element
	@that, element to add the hover event to
	@class_, default className
	@hoverClass, class to apply as well as the default class
*/
function addOnMouse(that, class_, hoverClass) {
	that.className += " " + class_;
	that.onmouseover = function() {that.className += " "  + hoverClass;}
	that.onmouseout = function() {
		var index = that.className.indexOf(hoverClass)
		if(index >= 0)
			that.className = that.className.substring(0, index)
				+ that.className.substring(index + hoverClass.length, that.className.length);
	}
}

function hideCalendar(div) {

	if (calOnOver)
		div.style.display = "none";
}

function setOnOver(how) {
	calOnOver = how;
}
