
/************************************************************************************
 								calender.js
 Author: Glenn Hassell
 Date: Thu Nov 08 10:13:43 EST 2007
 For: Copyright Glenn Hassell 2007

 Purpose: This provides a calander picking script.
***************************************************************************************/
var calender=
{
	monthMaxDays:		[0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
	monthMaxDaysLeap:	[0,31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
	calObj: 			null,
	calBody:			null,
	selectYears:		null,
	selectMonths:		null,
	startYear:			0,
	endYear:			0,
	curDay:				0,
	curMonth:			0,
	curYear:			0,
	targetElement:		false,
	myFunction:	function() {},

	/*******************************************************************************************
	Function loadCSS:
	Purpose: Ensure the calender.css file is loaded in the browser.
	Arguments:	path (optional) - the path from the base site url that the calender.css file resides,
								  the default is css/
	The call to this function is in this file and is thus run whenever this file is loaded by the
	Browser.  If the css file is not in the default location, either include the css in the head section
	of the html document or insert the path into the call.
	*******************************************************************************************/
	loadCSS: function(path)
	{
		path = path || "css/";
		var win=window;
		var head = document.getElementsByTagName("head");
		var links = document.getElementsByTagName("link");

		// Check to see if the css file is already loaded
		for(var i=0;i<links.length;i++)
		{
			if(links[i].href.match("calender.css"))
				return;
		}

		// Ensure that the css link is inserted before the javascript
		// by inserting before all of the scripts.
		var scripts = document.getElementsByTagName("script");
		var calcss = document.createElement("link");
		calcss.href = path+"/calender.css";
		calcss.rel="stylesheet";
		calcss.type="text/css";
		head[0].insertBefore(calcss,scripts[0]);
		i=1;
	},
	/*******************************************************************************************
	Function getDaysPerMonth:
	Purpose: Determine the number of days in a month for both leap years and non-leap years.
	Arguments:	month (required) - the month of the year (1-12) - numeric;
				year (required) - the year of the month that we are interested in.
	*******************************************************************************************/
	getDaysPerMonth: function (month, year)
	{
		if(!month || !year)
			return 0;
		if(!(year % 4)) // Years evenly divisible by four are normally leap years
		{
			if(!(year % 100) && (year % 400)) //Evenly divisible by 100 and not by 400 are not leap years
				return this.monthMaxDays[month];
			return this.monthMaxDaysLeap[month];
		}
		else
			return this.monthMaxDays[month];
	},
	/*******************************************************************************************
	Function initialize:
	Purpose: Build the basic calender body and insert it into the begining of the body of the
			 document.  This is called automatically from any of the functions that are used to
			 either display the calender or to modify the settings for the calender.  There is no need
			 to call this function seperately.
	Arguments:	None

	*******************************************************************************************/
	initialize: function()
	{
		var curDate=new Date();
		this.curDay=curDate.getDate();
		this.curMonth=curDate.getMonth()+1;
		this.curYear=curDate.getFullYear();
		this.calObj = document.createElement("div");
		this.calObj.setAttribute('id',"CalDIV");
		this.calObj.innerHTML='<table id="calenderTable"><tbody id="calenderTableHead"><tr><td colspan="4" align="left">\
			<select onChange="calender.changeBody();" id="selectMonth">\
	 		<option value="1">January</option><option value="2">February</option><option value="3">March</option>\
	 		<option value="4">April</option><option value="5">May</option><option value="6">June</option>\
	 		<option value="7">July</option><option value="8">August</option><option value="9">September</option>\
	 		<option value="10">October</option><option value="11">November</option><option value="12">December</option></select></td>\
	 		<td colspan="2" align="center"><select onChange="calender.changeBody();" id="selectYear"></select></td>\
	 		<td align="right"><a href="" onClick="return calender.close();">X</a></td></tr></tbody>\
	 		<tbody id="calenderTableDays"><tr style="">\
	 		<td>Su</td><td>Mo</td><td>Tu</td><td>We</td><td>Th</td><td>Fr</td><td>Sa</td>\
	 		</tr></tbody><tbody id="calender"></tbody></table>';
		var docBody = document.getElementsByTagName("body").item(0);
		docBody.insertBefore(this.calObj, docBody.firstChild);
		this.selectYears=document.getElementById('selectYear');
		this.selectMonths=document.getElementById('selectMonth');

	},

	/*******************************************************************************************
	Function: 	create
	Purpose:	This function creates the actual month to be displayed in a set of table rows that
				match the day of the week header rows in the table.
	Arguments:	all arguments are optional they are passed as an object in args:
				year:		the year of the month to be displayed, defaults to current year.
				month:		the month to be displayed (1-12) - defaults to the current month.
				day:		the day of the month to be highlighted (1-31) as the currently picked
							day - defaults to none.
				startYear:	The earliest year that can be picked from the year select list -
							defaults to whatever the current start year is.  If it has not yet
							been set it will be set with a default of the current year minus 7.
				endYear:	The greatest year that can be picked from the year select list -
							defaults to whatever the current end year is.  If it has not yet
							been set it will be set to a default of the current year plus 2.
	*******************************************************************************************/
	create: function(args)
	{
		args=args || {};
		if(!this.calObj)
			this.initialize();
		var year = args.year || this.curYear;
		var month = args.month || this.curMonth;
		var day = args.day || 0;
		var startYear=args.startYear || this.startYear;
		var endYear=args.endYear || this.endYear;
		this.setYears(startYear,endYear);
		for (var i=0; i< this.selectYears.options.length; i++)
		{
			if (this.selectYears.options[i].value == year)
			{
				this.selectYears.selectedIndex = i;
				break;
			}
		}
		if (i >= this.selectYears.options.length)
		{
			if(year > this.selectYears.options[0].value)
			{
				this.setYears(year,this.selectYears.options[(this.selectYears.options.length-1)].value);
				this.selectYears.selectedIndex = 0;
			}
			else
			{
				this.setYears(this.selectYears.options[0].value,year);
				this.selectYears.selectedIndex=this.selectYears.options.length-1;

			}
		}
		document.getElementById('selectMonth').selectedIndex = month-1;

		// first day of the month is a ....
		var firstDayOfMonth = new Date(year, month-1, 1).getDay();

		firstRow	= true;
		var x	= 0;
		var d	= 0;
		var trs = []
		var ti = 0;
		while (d <= this.getDaysPerMonth(month, year))
		{
			if (firstRow)
			{
				trs[ti] = document.createElement("TR");
				if (firstDayOfMonth > 0)
				{
					while (x < firstDayOfMonth)
					{
						trs[ti].appendChild(document.createElement("TD"));
						x++;
					}
				}
				firstRow = false;
				var d = 1;
			}
			if (x % 7 == 0)
			{
				ti++;
				trs[ti] = document.createElement("TR");
			}
			if (day && d == day)
			{
				var setID = 'calenderChoosenDay';
				var styleClass = 'choosenDay';
				var setTitle = 'this day is currently selected';
			}
			else if (d == this.curDay && month == this.curMonth && year == this.curYear)
			{
				var setID = 'calenderToDay';
				var styleClass = 'toDay';
				var setTitle = 'this day today';
			}
			else
			{
				var setID = false;
				var styleClass = 'normalDay';
				var setTitle = false;
			}
			var td = document.createElement("TD");
			td.className = styleClass;
			if (setID)
			{
				td.id = setID;
			}
			if (setTitle)
			{
				td.title = setTitle;
			}
			td.onmouseover = new Function('calender.highLiteDay(this)');
			td.onmouseout = new Function('calender.deHighLiteDay(this)');
			if(this.targetElement)
				td.onclick = new Function('calender.pickDate('+year+', '+(month-1)+', '+d+')');
			else
				td.style.cursor = 'default';
			td.appendChild(document.createTextNode(d));
			trs[ti].appendChild(td);
			x++;
			d++;
		}
		this.calBody=trs;
		return trs;
	},

	/*******************************************************************************************
	Function:
	Purpose:

	Arguments:

	*******************************************************************************************/
	/*******************************************************************************************
	Function:	show
	Purpose:	This function is used to show a calender at a particular position, it will call
				the create function to build the body of the calender prior to display.

	Arguments:
				elementPosition: 	This is used to determine where to display the calender.  It is
									expected to be an object containing at a minimum the variables
									offsetLeft and offsetRight.  Normally you will pass 'this' as the
									argument which will be the element to which the function call
									is attached.  If nothing is supplied it will default to 0,0
									(top left corner of the browser).

	*******************************************************************************************/
	show: function(elementPosition,args)
	{
		args = args || elementPosition || {};
		args.startYear=args.startYear || 0;
		args.endYear=args.endYear || 0;
		elementPosition = elementPosition || {};
		if(elementPosition.offsetLeft == undefined)
		{
			elementPosition.offsetLeft = 0;
			elementPosition.offsetRight = 0;
		}
		var target=args.targetElement || null;
		this.displayElement = args.displayElement || null;
		this.myFunction = args.mfunction ? new Function(args.mfunction) :'';
		this.targetElement = false;
		if(!this.calObj)
			this.initialize();

		if(!(this.displayElement=document.getElementById(this.displayElement)))
		{
			this.displayElement=null;
		}
		if(document.getElementById(target))
		{
			this.targetElement = document.getElementById(target);
		}
		var calTable = document.getElementById('calenderTable');

		var positions = [0,0];
		var positions = this.getParentOffset(elementPosition, positions);	// nieuw
		calTable.style.left = positions[0]+'px';		// nieuw
		calTable.style.top = positions[1]+'px';			// nieuw

		calTable.style.display='block';

		var matchDate = new RegExp('^([0-9]{4})-([0-9]{2})-([0-9]{2})$');
		var m = matchDate.exec(this.targetElement.value);
		if (m == null)
		{
			this.showBody(this.create(args));
		}
		else
		{
			if (m[2].substr(0, 1) == 0)
				m[2] = m[2].substr(1, 1);
			if (m[3].substr(0, 1) == 0)
				m[3] = m[3].substr(1, 1);
			trs = this.create({year:m[1], month:m[2],day: m[3]});
			this.showBody(trs);
		}

		this.hideSelect(document.body, 1);
		return false;
	},
	changeBody: function()
	{
		var year=this.selectYears.options[this.selectYears.selectedIndex].value;
		var month=this.selectMonths.options[this.selectMonths.selectedIndex].value;
		this.showBody(this.create({year: year,month: month}));
	},
	showBody: function(trs)
	{
		var calTBody = document.getElementById('calender');
		while (calTBody.childNodes[0])
		{
			calTBody.removeChild(calTBody.childNodes[0]);
		}
		var j=trs.length;
		for(var i=0;i<j;i++)
		{
			calTBody.appendChild(trs[i]);
		}
		return false;
	},
	setYears: function(startYear, endYear)
	{
		// current Date
			startYear = startYear || this.curYear-7;
			endYear = endYear || this.curYear+2;
			if(startYear == this.startYear && endYear==this.endYear)
				return false;
			this.startYear=startYear;
			this.endYear=endYear;
			if(!this.selectYears)
				this.initialize();
			while(this.selectYears.options.length)
			{
				this.selectYears.remove(0);
			}
		var j = 0;
		for (var y=endYear; y>=startYear; y--)
		{
			try
			{
				this.selectYears.add((new Option(y,y)),null);
			}
			catch(ex)
			{
				this.selectYears.add((new Option(y,y)));
			}
		}
		return false;
	},
	hideSelect: function(el, superTotal)
	{
		if (superTotal >= 100)
		{
			return false;
		}

		var totalChilds = el.childNodes.length;
		for (var c=0; c<totalChilds; c++)
		{
			var thisTag = el.childNodes[c];
			if (thisTag.tagName == 'SELECT')
			{
				if (thisTag.id != 'selectMonth' && thisTag.id != 'selectYear')
				{
					var calenderEl = document.getElementById('calenderTable');
					var positions = [0,0];
					var positions = this.getParentOffset(thisTag, positions);	// nieuw
					var thisLeft	= positions[0];
					var thisRight	= positions[0] + thisTag.offsetWidth;
					var thisTop	= positions[1];
					var thisBottom	= positions[1] + thisTag.offsetHeight;
					var calLeft	= calenderEl.offsetLeft;
					var calRight	= calenderEl.offsetLeft + calenderEl.offsetWidth;
					var calTop	= calenderEl.offsetTop;
					var calBottom	= calenderEl.offsetTop + calenderEl.offsetHeight;
				}


			}
			else if(thisTag.childNodes.length > 0)
			{
				this.hideSelect(thisTag, (superTotal+1));
			}
		}
	},
	close: function()
	{
		document.getElementById('calenderTable').style.display='none';
		onerror='';
		return false;
	},
	highLiteDay: function(el)
	{
		el.className = 'hlDay';
	},
	deHighLiteDay: function(el)
	{
		if (el.id == 'calenderToDay')
			el.className = 'toDay';
		else if (el.id == 'calenderChoosenDay')
			el.className = 'choosenDay';
		else
			el.className = 'normalDay';
	},
	pickDate: function(year, month, day)
	{
		month++;
		day	= day < 10 ? '0'+day : day;
		month	= month < 10 ? '0'+month : month;
		if (!this.targetElement)
		{
			alert('target for date is not set yet');
		}
		else
		{
			this.targetElement.value= month+'/'+day+'/'+year;
			if(this.displayElement)
				this.displayElement.value=this.targetElement.value;
			if(this.myFunction != '')
			{
				onerror=this.close;
				this.myFunction();
			}
			this.close();
		}
	},
	getParentOffset: function(el, positions)
	{
		positions[0] += el.offsetLeft;
		positions[1] += el.offsetTop;
		if (el.offsetParent)
			positions = this.getParentOffset(el.offsetParent, positions);
		return positions;
	}
}
calender.loadCSS();


