/**
	A calendar script
	@author Frits van Campen
	@date August 6 2007
	@version 1.0
	
	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!!
		
	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) {
	
//	this.months = new Array("januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december");
	this.months = ALL_MONTHS;
//	this.weekdays = new Array("zondag","maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag");
	this.weekdays = new Array();
	var i;
	for(i = 0; i < 6; i++) {
		this.weekdays[i + 1] = ALL_WEEKS[i];
	}
	this.weekdays[0] = ALL_WEEKS[6];
	this.text = new Array();
	this.text["selected"] = SELECTED_DATE;
	this.text["select"] = CLICK_TO_SELECT;
	this.text["today"] = STR_TODAY;
	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;
	this.id = 'omgwtf';
	
	/**
		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 = "calendar";
		
		// 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 = "calendarYear";
		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 = "calendarMonth";
		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());
	}

	/**
		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 selectable 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() {
			//alert(that.callbackfunc);
			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);

	return this;
}

/**
	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);
	}
}

/**
	Shows and hides the div with the calendar by focussing on an element.
	Make sure the call to this function happens with an onfocus

	@monthselected, For checking if a user clicked on the month-dropdown list.
	@yearselected,	For checking if a user clicked on the year-dropdown list.
	@divs,			To make sure that every div that was once shown is hidden again, so that only one div is visible.
	@id,			The ID of the div with the calendar.
	@elem,			The element which you focus on to show the calendar.
*/

var monthselected = false;
var yearselected = false;
var divs = new Array();

function showCal(id, elem) {

	var calendarDiv = document.getElementById(id);
	divs[divs.length] = calendarDiv;
	
	for(var i=0; i<divs.length; i++){
		divs[i].style.display = "none";
	}
	if(divs.length > 1){
		divs.length = 0;
		divs[0] = calendarDiv;
	}
	calendarDiv.style.display = "block";

	var months = calendarDiv.firstChild.firstChild.firstChild.firstChild.childNodes[1];
	var years = calendarDiv.firstChild.firstChild.firstChild.firstChild.childNodes[0];

	document.onmousedown = function(e){
		
		months.onfocus = function() {
			monthselected = true;
		}
		years.onfocus = function() {
			yearselected = true;
		}

		months.onblur = function() {
			monthselected = false;
		}
		years.onblur = function() {
			yearselected = false;
		}

		var target = window.event ? window.event.srcElement : e ? e.target : null;
		if	(!monthselected && !yearselected && calendarDiv.style.display == "block" &&
				((getMouseX(e) < findPosX(calendarDiv) || getMouseX(e) > findPosX(calendarDiv)+calendarDiv.offsetWidth)
						||
				 (getMouseY(e) < findPosY(calendarDiv) || getMouseY(e) > findPosY(calendarDiv)+calendarDiv.offsetHeight))
			) {

			calendarDiv.style.display = "none";
		} else if(target == elem && calendarDiv.style.display == "none"){
			calendarDiv.style.display = "block";
		} else if(target != elem && calendarDiv.style.display == "none"){
			document.onmousedown = function(){
				//Reset the function for onmousedown by leaving it empty
			}
		}
	}

	document.onkeyup = function(e){
		var e = window.event || e;
		if(e.keyCode == 9){
			months.onblur = function () {
				calendarDiv.style.display = "none";
				monthselected = false;
				document.onkeyup = function(){
					//Reset the function for onkeyup by leaving it empty
				}
			}
		}
	}
}
