var calendar = new Calendar()

/////////////////////////////////////////////////////////////
// Calendar Class ///////////////////////////////////////////
/////////////////////////////////////////////////////////////

function Calendar()
{
	this.calendarBody = null;
	this.container = null;
	this.date = new Date();
	this.field = null;
	this.monthField = null;
	this.onSelectHandler = null;
	this.originalDate = null;
	this.shim = null;
	this.yearField = null;

	this.attachToField = calendar_attachToField;
	this.decrement = calendar_decrement;
	this.hide = calendar_hide;
	this.increment = calendar_increment;
	this.initialize = calendar_initialize;
	this.initiateHide = calendar_initiateHide;
	this.populate = calendar_populate;
	this.selectDay = calendar_selectDay;
	this.setDate = calendar_setDate;
	this.show = calendar_show;
	this.toggleVisibility = calendar_toggleVisibility;
}

function calendar_attachToField(field, handler)
{
	//set reference to field
	this.field = field;
	this.onSelectHandler = handler;

	//check if field contains a valid date
	if (isNaN(Date.parse(field.value)))
		newDate = new Date()
	else
		newDate = new Date(field.value)
		
	//HACK: handles dates before 1915 (may have to update this at some point in the near future)	
	if (newDate.getFullYear() < 1915)
		newDate.setFullYear(newDate.getFullYear() + 100)
		
	//set date to use
	this.date = newDate;
	this.originalDate = newDate;
}

function calendar_decrement()
{
	var newMonth, newDay, newYear;

	if (this.date.getMonth() == 0)
	{
		newMonth = 12;
		newYear = this.date.getFullYear() - 1;
	}
	else
	{
		newMonth = this.date.getMonth();
		newYear = this.date.getFullYear();
	}

	var daysInMonth = getDaysInMonth(newMonth,newYear)
	if (this.date.getDate() > daysInMonth)
		newDay = daysInMonth;
	else
		newDay = this.date.getDate();

	//set new date and populate calendar
	this.date = new Date(newMonth + "/" + newDay + "/" + newYear);
	this.populate();
	this.show();
}

function calendar_hide()
{
	this.container.style.display = "none";
	this.shim.style.display = "none";
}

function calendar_increment()
{
	var newMonth, newDay, newYear;

	if (this.date.getMonth() == 11)
	{
		newMonth = 1;
		newYear = this.date.getFullYear() + 1;
	}
	else
	{
		newMonth = this.date.getMonth() + 2;
		newYear = this.date.getFullYear();
	}

	var daysInMonth = getDaysInMonth(newMonth,newYear)
	if (this.date.getDate() > daysInMonth)
		newDay = daysInMonth;
	else
		newDay = this.date.getDate();

	//set new date and populate calendar
	this.date = new Date(newMonth + "/" + newDay + "/" + newYear);
	this.populate();
	this.show();
}

function calendar_initiateHide()
{
	this.container.hideTimer = window.setTimeout("calendar.hide();", 300);
}

function calendar_initialize(id)
{
	this.calendarBody = document.getElementById(id + "_Days");
	this.container = document.getElementById(id);
	this.shim = document.getElementById(id + "_Shim");
	this.prefix = id;
	this.monthField = document.getElementById(id + "_Month");
	this.yearField = document.getElementById(id + "_Year");
}

function calendar_populate()
{
	var startDay = getMonthStartDay(this.date);
	var month = this.date.getMonth() + 1;
	var year = this.date.getFullYear();
	var isSelected, isToday;	
	var today = new Date();

	var datesEl = this.calendarBody;

	//populate month name and year
	this.monthField.value = month;
	this.yearField.value = year;
	
	//create date rows
	if (datesEl)
	{
		//clear all existing rows
		for (i=0; i<datesEl.rows.length;)
			datesEl.deleteRow(0)

		//insert the number of necessary rows
		var daysInMonth = getDaysInMonth(month,year);
		var monthStartDay = getMonthStartDay(this.date);
		var day = (monthStartDay * -1) + 1
		var weeks = Math.ceil((daysInMonth + monthStartDay)/7);

		//loop through weeks and days to create table
		for (var i=0; i<weeks; i++)
		{
			var row = datesEl.insertRow(-1);
			for (var j=0; j<7; j++,day++)
			{
				var cell = row.insertCell(-1);
				if (day > 0 && day <= daysInMonth)
				{
					isSelected = (this.originalDate.getMonth() + 1 == month && this.originalDate.getDate() == day && this.originalDate.getFullYear() == year)
					isToday = (today.getFullYear() == year && today.getMonth() + 1 == month && today.getDate() == day);
					cell.className = (isSelected ? "SelectedDay" : (isToday ? "Today" : "Day"));
					cell.onclick = calendarCell_onMouseEvent;
					cell.onmouseover = calendarCell_onMouseEvent;
					cell.onmouseout = calendarCell_onMouseEvent;
					cell.appendChild(document.createTextNode(day));
				}
				else
				{
					cell.className = "EmptyDay";
					cell.appendChild(document.createTextNode(""));
				}
			}
		}
	}
}

function calendar_setDate(prefix)
{
	var newMonth, newDay, newYear;

	newMonth = this.monthField.value;
	newYear = this.yearField.value;
	
	var daysInMonth = getDaysInMonth(newMonth,newYear)
	if (this.date.getDate() > daysInMonth)
		newDay = daysInMonth;
	else
		newDay = this.date.getDate();

	//set new date and populate calendar
	this.date = new Date(newMonth + "/" + newDay + "/" + newYear)
	this.populate()
	this.show()
}

function calendar_selectDay(day)
{
	this.field.value = (this.date.getMonth() + 1) + "/" + day + "/" + this.date.getFullYear();
	if (this.onSelectHandler)
		eval(this.onSelectHandler.replace(/this/g,"this.field"));
	this.field.select();	
	this.hide()
}

function calendar_show(field, handler)
{
	//clear timer, attach to provided field
	if (this.container.hideTimer)
		window.clearTimeout(this.container.hideTimer);
	if (field && field != this.field)
	{
		this.attachToField(field, handler);	
		this.populate();
	}
	
	//reposition and show calendar
	item_reposition(field, this.container, true);
	if (this.container.style.display != "block")
	{
		this.container.style.display = "block";

		//set up the shim to cover windowed elements (i.e. select boxes)
		this.shim.style.width = this.container.offsetWidth + "px";
		this.shim.style.height = this.container.offsetHeight + "px";
		this.shim.style.left = this.container.style.left;
		this.shim.style.top = this.container.style.top;
		this.shim.style.display = "block";
	}
}

function calendar_toggleVisibility(field, handler)
{
	if (this.container.style.display != "block")
		this.show(field, handler);
	else
		this.hide()
}

function calendarCell_onMouseEvent(e)
{
	if (!e)
		var e = window.event;

	var el = getEventTarget(e)
	var eventType = e.type

	if (eventType == "mouseover" || eventType == "mouseout")
	{
		switch (el.className)
		{
			case "Day": el.className = "DayHover"; return;
			case "DayHover": el.className = "Day"; return;
			case "Increment": el.className = "IncrementHover"; return;
			case "IncrementHover": el.className = "Increment"; return;
		}
	}
	else if (eventType == "click")
	{
		var day = parseInt(el.firstChild.nodeValue)
		calendar.selectDay(day)
	}
}


/////////////////////////////////////////////////////////////
// SUPPORTING METHODS ///////////////////////////////////////
/////////////////////////////////////////////////////////////

//this funtion determines which day of the week the year starts on. i.e. sunday, monday, etc.
function getMonthStartDay(date)
{
	var i;
	var totalDays = 3;  //offset for first day of week in year 1800

	//add all days in the preceding months of this year
	for (i=date.getMonth()-1; i>=0; i--)
		totalDays += getDaysInMonth(i+1,date.getFullYear());

	//add all days in the preceding years of this year (to 1800)
	for (i=date.getFullYear()-1; i>=1800; i--)
	{
		if (isLeapYear(i))
			totalDays += 366;
		else
			totalDays += 365;
	}

	return (totalDays) % 7;
}

//returns the amount of days in an given month
function getDaysInMonth(month,year)
{
	switch (month)
	{
		case 1: return 31;
		case 2:
			if (isLeapYear(year))
				return 29;
			else
				return 28;
		case 3: return 31;
		case 4: return 30;
		case 5: return 31;
		case 6: return 30;
		case 7: return 31;
		case 8: return 31;
		case 9: return 30;
		case 10: return 31;
		case 11: return 30;
		case 12: return 31;
	}
}

//returns the name of the month
function getMonthName(month,abbrev)
{
	switch (month)
	{
		case 1: return abbrev ? "Jan" : "January";
		case 2: return abbrev ? "Feb" : "February";
		case 3: return abbrev ? "Mar" : "March";
		case 4: return abbrev ? "Apr" : "April";
		case 5: return abbrev ? "May" : "May";
		case 6: return "June";
		case 7: return "July";
		case 8: return abbrev ? "Aug" : "August";
		case 9: return abbrev ? "Sept" : "September";
		case 10: return abbrev ? "Oct" : "October";
		case 11: return abbrev ? "Nov" : "November";
		case 12: return abbrev ? "Dec" : "December";
	}
}

//decides if the given year is a leap year or not
function isLeapYear(year)
{
	return (year % 4 == 0 && year != 1800 && year != 1900 && year != 2100 && year != 2200);
}