/* Enhanced to allow multiple extensions quickly; arguments now allow the destination and unlimited sources */
Object.extend = function() { if (arguments.length < 2) return arguments[0]; var ret = arguments[0]; for (var i = 1; i < arguments.length; i++) for (var property in arguments[i]) ret[property] = arguments[i][property]; return ret; }

/*
Calendar Object
	Used to manipulate Date objects as well as contain Constant values for the DatePicker
*/
var Calendar = {
	//Constants	
	SUNDAY : 0, MONDAY : 1, TUESDAY : 2, WEDNESDAY : 3, THURSDAY : 4, FRIDAY : 5, SATURDAY : 6,
	JANUARY : 0, FEBRUARY : 1, MARCH : 2, APRIL : 3, MAY : 4, JUNE : 5, JULY : 6, AUGUST : 7, SEPTEMBER : 8, OCTOBER : 9, NOVEMBER : 10, DECEMBER : 11,

	//Properties
	Days : ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
	Months : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
	Today : new Date(),

	//Methods
	datesMatch : function(one, two) { return this.formatDate(one, "{yyyy}{mm}{dd}") == this.formatDate(two, "{yyyy}{mm}{dd}"); },
	addDays : function(dt, days) { var date = new Date(dt); var mili = days * 24 * 60 * 60 * 1000; return new Date(date.getTime() + mili); },
	getDate : function(dt, day) { var date = new Date(dt); date.setDate(day); return date; },
	getDayOfWeek : function(dt) { return new Date(dt).getDay(); },
	getDaysInMonth : function(dt) {
		var date = new Date(dt);
		var year = date.getFullYear();
		switch (date.getMonth()) {
			case this.FEBRUARY: return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0? 29 : 28;
			case this.APRIL: case this.JUNE: case this.SEPTEMBER: case this.NOVEMBER: return 30;
			default: return 31;
		}
	},
	getPreviousMonth : function(dt) {
		var date = new Date(dt), prev = new Date();
			prev.setDate(1);
			prev.setMonth(this.JANUARY < date.getMonth()? date.getMonth() - 1 : this.DECEMBER);
			prev.setFullYear(prev.getMonth() == this.DECEMBER? date.getFullYear() - 1 : date.getFullYear());
		return prev;
	},
	getNextMonth : function(dt) {
		var date = new Date(dt), next = new Date();
			next.setDate(1);
			next.setMonth(date.getMonth() < this.DECEMBER? date.getMonth() + 1 : this.JANUARY);
			next.setFullYear(next.getMonth() == this.JANUARY? date.getFullYear() + 1 : date.getFullYear());
		return next;
	},
	isToday : function(dt) { return this.formatDate(dt, "{yyyy}{mm}{dd}") == this.formatDate(new Date(), "{yyyy}{mm}{dd}"); },
	formatDate : function(dt, format) {
		var tmp, ret = format, date = new Date(dt);
		if (date == "Invalid Date") return "Invalid Date";

		tmp = Calendar.Months[date.getMonth()];
		ret = ret.replace(/{month}/, tmp);
		ret = ret.replace(/{mon}/, tmp.substring(0, 3));
		tmp = Calendar.Days[date.getDay()];
		ret = ret.replace(/{day}/, tmp);
		ret = ret.replace(/{shortday}/, tmp.substring(0, 3));

		ret = ret.replace(/{yyyy}/, date.getFullYear());
		ret = ret.replace(/{yy}/, date.getFullYear().toString().substring(2));
		ret = ret.replace(/{dd}/, date.getDate() < 10? "0" + date.getDate() : date.getDate());
		ret = ret.replace(/{d}/, date.getDate());
		tmp = date.getMonth() + 1;
		ret = ret.replace(/{mm}/, tmp < 10? "0" + tmp : tmp);
		ret = ret.replace(/{m}/, tmp);

		ret = ret.replace(/{24HH}/, date.getHours() < 10? "0" + date.getHours() : date.getHours());
		tmp = date.getHours() == 0? 12 : 12 < date.getHours()? date.getHours() - 12 : date.getHours();
		ret = ret.replace(/{HH}/, tmp < 10? "0" + tmp : tmp);
		ret = ret.replace(/{MM}/, date.getMinutes() < 10? "0" + date.getMinutes() : date.getMinutes());
		ret = ret.replace(/{AMPM}/, date.getHours() < 12? "AM" : "PM");
		ret = ret.replace(/{ampm}/, date.getHours() < 12? "am" : "pm");

		return ret;
	},
	Version : { Major:1, Minor:0, Revision:0, toString:function(){ return this.Major + "." + this.Minor + "." + this.Revision; } }
	/*
		Version 1.0.0 >> Initial Build
	*/
	//Days per Month: 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
	//Leap Year Check: (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
}

/*
DatePickers Object
	Manages all DatePicker objects created in a document
*/
var DatePickers = {
	//Constants
	DAYS_NONE : 0, DAYS_SINGLE : 1, DAYS_SHORT : 2, DAYS_FULL : 3,
	LOCATION_NONE : 0, LOCATION_BELOW : 1, LOCATION_RIGHT : 2, LOCATION_ABOVE : 3, LOCATION_LEFT : 4,
	TIME_ONE : 1, TIME_FIVE : 0, TIME_TEN : 2, TIME_FIFTEEN : 3, TIME_TWENTY : 4, TIME_THIRTY : 5,

	//Properties
	Items : new Array(),
	
	//Methods
	add : function(dp) { this.Items[this.Items.length] = dp; Event.observe(dp.TextBox.ownerDocument, "click", this._Document_OnClick); },
	find : function(td) { if (!this.Items.length) return null; if (td.DatePicker) return td.DatePicker; var table = td; while(table.nodeName != "TABLE" && table.nodeName != "HTML") table = table.parentNode; return table.nodeName == "TABLE"? table.DatePicker : null; },
	hideAll : function() { for (var i = 0; i < this.Items.length; i++) this.Items[i].hide(); },
	hideExcept : function(dp) { for (var i = 0; i < this.Items.length; i++) if (this.Items[i] != dp) this.Items[i].hide(); },
	remove : function(dp) { this.Items = this.Items.without(dp); dp.destroy(); if (!this.Items.length) Event.stopObserving(dp.TextBox.ownerDocument, "click", this.__Document_OnClick); },
	
	//Event Methods
	_Document_OnClick : function(e) { if (DatePickers.find(e.target? e.target : e.srcElement)) return false; DatePickers.hideAll(); },

	Version : { Major:1, Minor:0, Revision:0, toString:function() { return this.Major + "." + this.Minor + "." + this.Revision; } }
	/*
		Version 1.0.0 >> Initial Build
	*/
}

/*
DatePicker Object
*/
var DatePicker = Class.create();
DatePicker.prototype = {
	//Methods
	initialize : function(textbox) {
		this.TextBox = $(textbox);
		this.TextBox.DatePicker = this;
		this.Visible = false;
		if (new Date(this.TextBox.value) == "Invalid Date") this.TextBox.value = "";

		//Create Defaults
		var defaults = {
			AlwaysVisible : false
			,ClassName : null
			,DateFormat : "{yyyy}/{mm}/{dd}"
			,DateRange : null
			,DateTimeFormat : "{yyyy}/{mm}/{dd} {HH}:{MM} {AMPM}"
			,DayStyle : DatePickers.DAYS_SINGLE
			,Draggable : false
			,HideOnSelect : false
			,Location : DatePickers.LOCATION_BELOW
			,OnSelected : null
			,SelectedDate : null
			,ShowTime : false
			,TimeFormat : "{HH}:{MM} {AMPM}"
			,TimeIncrement : DatePickers.TIME_FIVE
			,XY : null
			
			,Style_Table : { backgroundColor:"White", border:"1px solid black", borderCollapse:"collapse", emptyCells:"show", tableLayout:"fixed", width:"148px", "float":"left" }
			,Style_TableHeaderCell : { fontSize:"8pt", textAlign:"center", cursor:"default", height:"20px" }
			,Style_TableHeaderCellPrevious : { cursor:"pointer" }
			,Style_TableHeaderCellTitle : {}
			,Style_TableHeaderCellNext : {cursor:"pointer"}
			,Style_TableHeaderCellDays : {}
			,Style_TableBodyCell : { textDecoration:"none", fontWeight:"normal", border:"1px solid black", fontSize:"8pt", textAlign:"center", width:"20px", cursor:"pointer", height:"20px", backgroundColor:"white", color:"black" }
			,Style_TableBodyCellHover : { backgroundColor:"lightblue", color:"black", cursor:"pointer" }
			,Style_TableBodyCellSelected : { backgroundColor:"yellow", color:"black", cursor:"default" }
			,Style_TableBodyCellDisallowed : { textDecoration:"line-through", color:"gray", cursor:"default" }
			,Style_TableBodyCellPrevious : { backgroundColor:"lightgrey", color:"white", cursor:"default" }
			,Style_TableBodyCellPreviousHover : { backgroundColor:"lightgrey", color:"black", cursor:"default" }
			,Style_TableBodyCellPreviousSelected : { backgroundColor:"#FFFF88", color:"black", cursor:"default" }
			,Style_TableBodyCellPreviousDisallowed : { textDecoration:"line-through", backgroundColor:"lightgrey", color:"black", cursor:"default" }
			,Style_TableBodyCellNext : { backgroundColor:"lightgrey", color:"white", cursor:"default" }
			,Style_TableBodyCellNextHover : { backgroundColor:"lightgrey", color:"black", cursor:"default" }
			,Style_TableBodyCellNextSelected : { backgroundColor:"#FFFF88", color:"black", cursor:"default" }
			,Style_TableBodyCellNextDisallowed : { textDecoration:"line-through", backgroundColor:"lightgrey", color:"black", cursor:"default" }
			,Style_TableBodyCellToday : { fontWeight:"bold" }
			,Style_TableFooterCell : { textAlign:"center" }
			,Style_TableFooterDropDownList : { fontSize:"8pt" }
		};
		this.Options = Object.extend(defaults, arguments[1] || {});
		
		//Bind Event Listeners
		this.__TableHeaderPrevious_OnClick = this._TableHeaderPrevious_OnClick.bindAsEventListener(this);
		this.__TableHeaderNext_OnClick = this._TableHeaderNext_OnClick.bindAsEventListener(this);
		this.__Hours_OnChange = this._Hours_OnChange.bindAsEventListener(this);
		this.__Minutes_OnChange = this._Minutes_OnChange.bindAsEventListener(this);
		this.__AmPm_OnChange = this._AmPm_OnChange.bindAsEventListener(this);
		this.__Table_OnClick = this._Table_OnClick.bindAsEventListener(this);
		this.__TextBox_OnClick = this._TextBox_OnClick.bindAsEventListener(this);
		this.__TextBox_OnFocus = this._TextBox_OnFocus.bindAsEventListener(this);
		this.__TextBox_OnKeyPress = this._TextBox_OnKeyPress.bindAsEventListener(this);
		this.__TableCell_OnClick = this._TableCell_OnClick.bindAsEventListener(this);
		this.__TableCell_OnMouseOut = this._TableCell_OnMouseOut.bindAsEventListener(this);
		this.__TableCell_OnMouseOver = this._TableCell_OnMouseOver.bindAsEventListener(this);

		var doc = this.TextBox.ownerDocument;
		//Build Table
		this.Table = Object.extend(doc.createElement("TABLE"), { DatePicker:this, id:(this.TextBox.id + ".DatePicker") });
		this.Table.style.position = "absolute";

		//Build Table Header
		var thead = doc.createElement("THEAD"); thead.id = this.TextBox.id + ".Header"; this.Table.appendChild(thead);
		var td, tr = doc.createElement("TR");
			td = doc.createElement("TD"); td.innerHTML = "&lt;"; tr.appendChild(td); Event.observe(td, "click", this.__TableHeaderPrevious_OnClick);
			td = Object.extend(doc.createElement("TD"), { colSpan:5, innerHTML:"DatePicker", id:(this.TextBox.id + ".DatePicker.Title") }); tr.appendChild(td);
			td = doc.createElement("TD"); td.innerHTML = "&gt;"; tr.appendChild(td); Event.observe(td, "click", this.__TableHeaderNext_OnClick);
		thead.appendChild(tr);
		tr = doc.createElement("TR");
			for (var i = 0; i < 7; i++) { tr.appendChild(doc.createElement("TD")); }
		thead.appendChild(tr);
		
		//Build Table Body
		var tbody = doc.createElement("TBODY");
		for (var i = 0; i < 6; i++) {
			tr = doc.createElement("TR");
			for (var j = 0; j < 7; j++) {
				td = doc.createElement("TD"); tr.appendChild(td);
				Event.observe(td, "click", this.__TableCell_OnClick);
				Event.observe(td, "mouseout", this.__TableCell_OnMouseOut);
				Event.observe(td, "mouseover", this.__TableCell_OnMouseOver);
			}
			tbody.appendChild(tr);
		}
		this.Table.appendChild(tbody);
		
		//Build Table Footer
		var ddl, tfoot = doc.createElement("TFOOT");
		tr = doc.createElement("TR");
		td = Object.extend(doc.createElement("TD"), { colSpan:7 });
		ddl = doc.createElement("SELECT"); ddl.DatePicker = this; for (var i = 0; i < 12; i++) ddl.options[i] = new Option((i + 1 < 10? "0" + (i + 1) : i + 1), i + 1, i == 11); td.appendChild(ddl); this.Hours = ddl; Event.observe(this.Hours, "change", this.__Hours_OnChange);
		ddl = doc.createElement("SELECT"); ddl.DatePicker = this; for (var i = 0; i < 60; i++) ddl.options[i] = new Option(i < 10? "0" + i : i, i, i == 0); td.appendChild(ddl); this.Minutes = ddl; Event.observe(this.Minutes, "change", this.__Minutes_OnChange);
		ddl = doc.createElement("SELECT"); ddl.DatePicker = this; ddl.options[0] = new Option("AM", 0, true); ddl.options[1] = new Option("PM", 1); td.appendChild(ddl); this.AmPm = ddl; Event.observe(this.AmPm, "change", this.__AmPm_OnChange);
		tr.appendChild(td); tfoot.appendChild(tr); this.Table.appendChild(tfoot);

		//Add body to document and set location and view
		doc.body.appendChild(this.Table);
		
		//Set Other important values
		this.Selected = this.Options.SelectedDate? this.Options.SelectedDate : this.Selected? this.Selected : null;
		if (this.Options.DateRange) {
			this.Options.DateRange[0] = new Date(this.Options.DateRange[0]);
			this.Options.DateRange[1] = new Date(this.Options.DateRange[1]);
			if (this.Selected) {
				if (this.Selected < this.Options.DateRange[0]) this.setSelected(this.Options.DateRange[0], false);
				if (this.Options.DateRange[1] < this.Selected) this.setSelected(this.Options.DateRange[1], false);
			}
		}
		this.Table.className = this.Options.ClassName? this.Options.ClassName : "";
		if (!this.Table.className) Object.extend(this.Table.style, this.Options.Style_Table);
		this.Table.style.display = this.Options.AlwaysVisible? "" : "none";
		if (this.Draggable) this.Draggable.destroy();
		if (this.Options.Draggable) this.Draggable = new Draggable(this.Table.id, {handle:this.TextBox.id + ".Header"});
		if (this.Options.AlwaysVisible) this.setLocation();
		if (this.TextBox.value && !this.Selected) {
			//this.setView(this.Selected? this.Selected : Calendar.Today);
			this.setSelected(new Date(this.TextBox.value), false);
		}else if (!this.Selected) {
			this.setView(this.Selected? this.Selected : Calendar.Today);
		}

		//Track needed events
		Event.observe(this.Table, "click", this.__Table_OnClick);
		if (typeof(this.Options.XY) == "string") {
			$(this.Options.XY).DatePicker = this;
			Event.observe($(this.Options.XY), "click", this.__TextBox_OnClick);
		}else {
			Event.observe(this.TextBox, "click", this.__TextBox_OnClick);
			Event.observe(this.TextBox, "focus", this.__TextBox_OnFocus);
			Event.observe(this.TextBox, "keypress", this.__TextBox_OnKeyPress);
		}
		
		//Add to DatePickers collection
		DatePickers.add(this);
		this.__destroyed = false;
	},
	destroy : function() {
		if (this.__destroyed) return;
		Event.stopObserving(this.Table.down("THEAD").down("TR").cells[0], "click", this.__TableHeaderPrevious_OnClick);
		Event.stopObserving(this.Table.down("THEAD").down("TR").cells[2], "click", this.__TableHeaderNext_OnClick);
		Event.stopObserving(this.Hours, "change", this.__Hours_OnChange);
		Event.stopObserving(this.Minutes, "change", this.__Minutes_OnChange);
		Event.stopObserving(this.AmPm, "change", this.__AmPm_OnChange);
		Event.stopObserving(this.Table, "click", this.__Table_OnClick);
		Event.stopObserving(this.TextBox, "click", this.__TextBox_OnClick);
		Event.stopObserving(this.TextBox, "focus", this.__TextBox_OnFocus);
		Event.stopObserving(this.TextBox, "keypress", this.__TextBox_OnKeyPress);
		this.Table.parentNode.removeChild(this.Table);
		$(this.TextBox).DatePicker = null;
		this.__destroyed = true;
		this.Visible = false;
		DatePickers.remove(this);
	},
	hide : function() {
		if (this.TextBox.value && this.TextBox.value != Calendar.formatDate(this.Selected, this.Options.ShowTime? this.Options.DateTimeFormat : this.Options.DateFormat)) this.setSelected(new Date(this.TextBox.value), true);
		if (!this.Options.AlwaysVisible) { this.Table.style.display = "none"; this.Visible = false; }
	},
	refresh : function() {
		var tclass = this.Table.className? true : false;
		var tr, td;

		//Header
		tr = this.Table.tHead? this.Table.tHead.rows[0] : this.Table.rows[0];
		td = tr.cells[0]; if (tclass) td.className = "Previous"; else { td.className = ""; Object.extend(td.style, this.Options.Style_TableHeaderCell, this.Options.Style_TableHeaderCellPrevious); }
		td = tr.cells[1]; if (tclass) td.className = "Title"; else { td.className = ""; Object.extend(td.style, this.Options.Style_TableHeaderCell, this.Options.Style_TableHeaderCellTitle); }
		td = tr.cells[2]; if (tclass) td.className = "Next"; else { td.className = ""; Object.extend(td.style, this.Options.Style_TableHeaderCell, this.Options.Style_TableHeaderCellNext); }
		tr = this.Table.tHead? this.Table.tHead.rows[1] : this.Table.rows[1];
		tr.style.display = this.Options.DayStyle? "" : "none";
		if (this.Options.DayStyle)
			for (var i = 0; i < tr.cells.length; i++) {
				td = tr.cells[i];
				td.innerHTML = this.Options.DayStyle == DatePickers.DAYS_FULL? Calendar.Days[i] : Calendar.Days[i].substring(0, this.Options.DayStyle == DatePickers.DAYS_SHORT? 3 : 1);
				if (tclass) td.className = "Days";
				else {
					td.className = "";
					td.innerHTML = this.Options.DayStyle == DatePickers.DAYS_FULL? Calendar.Days[i] : Calendar.Days[i].substring(0, this.Options.DayStyle == DatePickers.DAYS_SHORT? 3 : 1);
					Object.extend(td.style, this.Options.Style_TableHeaderCell, this.Options.Style_TableHeaderCellDays);
				}
			}

		//Body
		for (var i = 0; i < 6; i++) {
			for (var j = 0; j < 7; j++) {
				td = this.Table.tBodies[0].rows[i].cells[j];
				if (tclass) {
					if (td.isPrevious) td.className = td.isDisallowed? "Previous_Disallowed" : td.isSelected? "Previous_Selected" : "Previous";
					else if (td.isNext) td.className = td.isDisallowed? "Next_Disallowed" : td.isSelected? "Next_Selected" : "Next";
					else td.className = td.isDisallowed? "Disallowed" : td.isSelected? "Selected" : "";
					td.style.fontWeight = td.isToday? "bold" : "";
				}else {
					Object.extend(td.style, this.Options.Style_TableBodyCell);
					if (td.isPrevious) Object.extend(td.style, td.isDisallowed? this.Options.Style_TableBodyCellPreviousDisallowed : td.isSelected? this.Options.Style_TableBodyCellPreviousSelected : this.Options.Style_TableBodyCellPrevious);
					else if (td.isNext) Object.extend(td.style, td.isDisallowed? this.Options.Style_TableBodyCellNextDisallowed : td.isSelected? this.Options.Style_TableBodyCellNextSelected : this.Options.Style_TableBodyCellNext);
					else Object.extend(td.style, td.isDisallowed? this.Options.Style_TableBodyCellDisallowed : td.isSelected? this.Options.Style_TableBodyCellSelected : {});
					if (td.isToday) Object.extend(td.style, this.Options.Style_TableBodyCellToday);
				}
			}
		}

		//Footer
		tr = this.Table.tFoot? this.Table.tFoot.rows[0] : this.Table.rows[this.Table.rows.length - 1];
		tr.style.display = this.Options.ShowTime? "" : "none";
		if (!tclass) {
			td = tr.cells[0];
			Object.extend(td.style, this.Options.Style_TableFooterCell);
			for (var i = 0; i < td.childNodes.length; i++)
				Object.extend(td.childNodes[i].style, this.Options.Style_TableFooterDropDownList);
		}
		if (this.Options.ShowTime) {
			//if (this.Selected) this.Hours.selectedIndex = this.Selected.getHours()? this.Selected.getHours() < 13? this.Selected.getHours() - 1 : this.Selected.getHours() - 13 : 12;
			this.Minutes.options.length = 0;
			var inc  = 0;
			switch(this.Options.TimeIncrement) {
				default: inc = 1; break; //TIME_ONE
				case DatePickers.TIME_FIVE: inc = 5; break;
				case DatePickers.TIME_TEN: inc = 10; break;
				case DatePickers.TIME_FIFTEEN: inc = 15; break;
				case DatePickers.TIME_TWENTY: inc = 20; break;
				case DatePickers.TIME_THIRTY: inc = 30; break;
			}
			for (var i = 0, j = 0; i < 60; i += inc, j++) {
				this.Minutes.options[j] = new Option((i < 10 ? "0" : "") + i, i, this.Selected && this.Selected.getMinutes() == i);
			}
		}
	},
	reload : function(options) {
		if (options && typeof(options) == "object" && options != {}) this.Options = Object.extend(this.Options, options);
		this.Selected = this.Options.SelectedDate? this.Options.SelectedDate : this.Selected? this.Selected : null;
		if (this.Options.DateRange) {
			this.Options.DateRange[0] = new Date(this.Options.DateRange[0]);
			this.Options.DateRange[1] = new Date(this.Options.DateRange[1]);
			if (this.Selected) {
				if (this.Selected < this.Options.DateRange[0]) this.setSelected(this.Options.DateRange[0], false);
				if (this.Options.DateRange[1] < this.Selected) this.setSelected(this.Options.DateRange[1], false);
			}
		}
		this.Table.className = this.Options.ClassName? this.Options.ClassName : "";
		if (!this.Table.className) Object.extend(this.Table.style, this.Options.Style_Table);
		this.Table.style.display = this.Options.AlwaysVisible? "" : "none";
		if (this.Draggable) this.Draggable.destroy();
		if (this.Options.Draggable) this.Draggable = new Draggable(this.Table.id, {handle:this.TextBox.id + ".Header"});
		if (this.Options.AlwaysVisible) this.setLocation();
		this.setView(this.View? this.View : this.Selected? this.Selected : Calendar.Today);
	},
	show : function() { this.setLocation(); if (!this.Options.AlwaysVisible) this.Table.style.display = ""; this.Visible = true; return true; },
	setLocation : function() { // [optional Location argument] (if using Options.XY, arg is [,], if using Location, arg is DatePickers.LOCATION_X)
		if (this.Options.XY && !this.Options.Location) {
			if (arguments.length) this.Options.XY = arguments.length > 1? [arguments[0], arguments[1]] : arguments[0];
			this.Table.style.left = this.Options.XY[0];
			this.Table.style.top = this.Options.XY[1];
		}else {
			if (arguments.length) this.Options.Location = arguments[0];
			if (!this.Options.AlwaysVisible) {
				this.Table.style.left = -500 + "px";
				this.Table.style.top = -500 + "px";
				this.Table.style.display = "";
			}
			var left, top, box = (typeof(this.Options.XY) == "string")? $(this.Options.XY) : this.TextBox, xy = Position.cumulativeOffset(box);
			switch(this.Options.Location) {
				case DatePickers.LOCATION_LEFT: left = xy[0] - parseInt(this.Table.offsetWidth); top = xy[1]; break;
				case DatePickers.LOCATION_RIGHT: left = xy[0] + parseInt(box.offsetWidth); top = xy[1]; break;
				case DatePickers.LOCATION_ABOVE: left = xy[0]; top = xy[1] - parseInt(this.Table.offsetHeight); break;
				/*LOCATION_BELOW*/ default: left = xy[0]; top = xy[1] + box.offsetHeight; break;
			}

			if (!this.Options.AlwaysVisible) this.Table.style.display = "none";
			if (this.Options.Location == DatePickers.LOCATION_ABOVE && top < 0) this.setLocation(DatePickers.LOCATION_BELOW);
			else if (this.Options.Location == DatePickers.LOCATION_LEFT && left < 0) this.setLocation(DatePickers.LOCATION_RIGHT);
			else { this.Table.style.left = left + "px"; this.Table.style.top = top + "px"; }
		}
	},
	setSelected : function(dt, click) {
		//Set Selected and TextBox value
		this.Selected = new Date(dt);
		if (this.Selected == "Invalid Date") { this.Selected == null; this.TextBox.value = ""; this.hide(); return false; }
		if (this.Options.ShowTime)
			if (click) {
				var time = new Date(Calendar.formatDate(Calendar.Today, "{yyyy}/{mm}/{dd} ") + (this.Hours.selectedIndex + 1) + ":" + this.Minutes.options[this.Minutes.selectedIndex].value + " " + (this.AmPm.selectedIndex? "pm" : "am"));
				this.Selected.setHours(time.getHours(), time.getMinutes(), 0, 0);
			}else {
				var hours = this.Selected.getHours(), minutes = this.Selected.getMinutes();
				this.Hours.selectedIndex = hours && hours != 12? hours < 12? hours - 1 : hours - 13 : 11;
				this.AmPm.selectedIndex = hours < 12? 0 : 1;
			}
		this.TextBox.value = Calendar.formatDate(this.Selected, this.Options.ShowTime? this.Options.DateTimeFormat : this.Options.DateFormat);
		this.setView(this.Selected);
		if (click) if (this.Options.OnSelected) this.Options.OnSelected(this.TextBox);
		if (click) if (this.Options.HideOnSelect) this.hide();
		return true;
	},
	setView : function() { // [optional Date object]
		//Set the currently viewed month
		var newView = new Date(arguments.length? arguments[0] : this.View);
		if (!this.View || newView.getMonth() != this.View.getMonth() || newView.getFullYear() != this.View.getFullYear()) {
			this.View = newView;
			newView = true;
		}else {
			newView = false;
		}

		//Get needed details
		var prev = Calendar.getPreviousMonth(this.View);
		var next = Calendar.getNextMonth(this.View);
		var days = [Calendar.getDaysInMonth(prev), Calendar.getDaysInMonth(this.View), Calendar.getDaysInMonth(next)];
		var firstOfMonth = Calendar.getDayOfWeek(Calendar.getDate(this.View, 1));
		
		//Set Title and next/prev tooltips
		if (newView) {
			var tr = this.Table.tHead? this.Table.tHead.rows[0] : this.Table.rows[0];
			tr.cells[0].title = Calendar.Months[prev.getMonth()] + " " + prev.getFullYear();
			tr.cells[1].innerHTML = Calendar.Months[this.View.getMonth()] + " " + this.View.getFullYear();
			tr.cells[2].title = Calendar.Months[next.getMonth()] + " " + next.getFullYear();
		}
		
		//Set the numbers and the 'IS' properties for the cells in the currently viewed month
		for (var date, td, i = 0; i < days[1]; i++) {
			date = Calendar.getDate(this.View, i + 1);  date.setHours(0, 0, 0);
			td = this.Table.tBodies[0].rows[Math.floor((i + firstOfMonth) / 7)].cells[(i + firstOfMonth) % 7];
			if (newView) {
				td.innerHTML = i + 1;
				td.dateMilli = date.getTime();
				td.isPrevious = td.isNext = false;
				td.isToday = Calendar.isToday(date);
			}
			td.isSelected = Calendar.datesMatch(date, this.Selected);
			td.isDisallowed = this.Options.DateRange && (date < Calendar.addDays(this.Options.DateRange[0], -1) || this.Options.DateRange[1] < date);
		}

		//Set the numbers and the 'IS' properties for the cells in the previous month
		if (firstOfMonth != Calendar.SUNDAY) {
			for (var td, date, i = firstOfMonth - 1, j = 0; -1 < i; i--, j++) {
				date = Calendar.getDate(prev, days[0] - j); date.setHours(0, 0, 0);
				td = this.Table.tBodies[0].rows[0].cells[i];
				if (newView) {
					td.innerHTML = days[0] - j;
					td.dateMilli = date.getTime();
					td.isNext = false;
					td.isToday = Calendar.isToday(date);
					td.isPrevious = true;
				}
				td.isSelected = Calendar.datesMatch(date, this.Selected);
				td.isDisallowed = this.Options.DateRange && (date < Calendar.addDays(this.Options.DateRange[0], -1) || this.Options.DateRange[1] < date);
			}
		}

		//Set the numbers and the 'IS' properties for the cells in the next month
		for (var td, date, i = days[1]; i < days[1] + 8; i++) {
			if (Math.floor((i + firstOfMonth) / 7) == 6) break;
			date = Calendar.getDate(next, i - days[1] + 1);  date.setHours(0, 0, 0);
			td = this.Table.tBodies[0].rows[Math.floor((i + firstOfMonth) / 7)].cells[(i + firstOfMonth) % 7];
			if (newView) {
				td.innerHTML = i - days[1] + 1;
				td.dateMilli = date.getTime();
				td.isNext = true;
				td.isToday = Calendar.isToday(date);
				td.isPrevious = false;
			}
			td.isSelected = Calendar.datesMatch(date, this.Selected);
			td.isDisallowed = this.Options.DateRange && (date <= Calendar.addDays(this.Options.DateRange[0], -1) || this.Options.DateRange[1] <= date);
		}
		
		//Hide the last rows if not needed
		if (newView) {
			this.Table.tBodies[0].rows[4].style.display = new Date(this.Table.tBodies[0].rows[4].cells[0].dateMilli).getDate() < 8? "none" : "";
			this.Table.tBodies[0].rows[5].style.display = new Date(this.Table.tBodies[0].rows[5].cells[0].dateMilli).getDate() < 9? "none" : "";
		}
		
		if (this.Options.Location == DatePickers.LOCATION_ABOVE) { this.setLocation(); this.show(); }

		this.refresh();
	},
	_Hours_OnChange : function(e) { if (!this.Selected) return; this.Selected.setHours(this.Hours.selectedIndex + 1 + (this.AmPm.selectedIndex? 12 : 0)); this.TextBox.value = Calendar.formatDate(this.Selected, this.Options.ShowTime? this.Options.DateTimeFormat : this.Options.DateFormat); },
	_Minutes_OnChange : function(e) { if (!this.Selected) return; this.Selected.setMinutes(this.Minutes.options[this.Minutes.selectedIndex].value); this.TextBox.value = Calendar.formatDate(this.Selected, this.Options.ShowTime? this.Options.DateTimeFormat : this.Options.DateFormat); },
	_AmPm_OnChange : function(e) { if (!this.Selected) return; var hours = this.Selected.getHours(); if (hours < 12 && this.AmPm.selectedIndex) hours += 12; else if (12 < hours && !this.AmPm.selectedIndex) hours -= 12; this.Selected.setHours(hours); this.TextBox.value = Calendar.formatDate(this.Selected, this.Options.ShowTime? this.Options.DateTimeFormat : this.Options.DateFormat); },
	_Table_OnClick : function(e) { return false; },
	_TableCell_OnClick : function(e) { var td = e.target? e.target : e.srcElement; if (!td.isNext && !td.isPrevious && !td.isSelected && !td.isDisallowed) this.setSelected(td.dateMilli, true); },
	_TableCell_OnMouseOut : function(e) {
		var td = e.target? e.target : e.srcElement;
		if (td.isSelected || td.isDisallowed) return;
		if (!this.Selected) this.TextBox.value = "";
		if (this.Table.className) {
			td.className = td.className? td.className.replace(/Hover/, "").replace(/_/, "") : "";
		}else {
			var styles = [this.Options.Style_TableBodyCell];
			styles[styles.length] = td.isPrevious? this.Options.Style_TableBodyCellPrevious : td.isNext? this.Options.Style_TableBodyCellNext : this.Options.Style_TableBodyCell;
			if (td.isToday) styles[styles.length] = this.Options.Style_TableBodyCellToday;
			for (var i = 0; i < styles.length; i++) for (var item in styles[i]) td.style[item] = styles[i][item];
		}
	},
	_TableCell_OnMouseOver : function(e) {
		var td = e.target? e.target : e.srcElement;
		if (td.isSelected || td.isDisallowed) return;
		if (!this.Selected) {
			var date = new Date(td.dateMilli);
			var time = new Date(Calendar.formatDate(Calendar.Today, "{yyyy}/{mm}/{dd} ") + (this.Hours.selectedIndex + 1) + ":" + this.Minutes.options[this.Minutes.selectedIndex].value + " " + (this.AmPm.selectedIndex? "pm" : "am"));
			if (this.Options.ShowTime) date.setHours(time.getHours(), time.getMinutes(), 0, 0);
			this.TextBox.value = Calendar.formatDate(date, this.Options.ShowTime? this.Options.DateTimeFormat : this.Options.DateFormat);
		}
		if (this.Table.className) {
			td.className = (td.isPrevious? "Previous_" : td.isNext? "Next_" : "") + "Hover";
		}else {
			var styles = [this.Options.Style_TableBodyCell];
			styles[styles.length] = td.isPrevious? this.Options.Style_TableBodyCellPreviousHover : td.isNext? this.Options.Style_TableBodyCellNextHover : this.Options.Style_TableBodyCellHover;
			if (td.isToday) styles[styles.length] = this.Options.Style_TableBodyCellToday;
			for (var i = 0; i < styles.length; i++) for (var item in styles[i]) td.style[item] = styles[i][item];
		}
	},
	_TableHeaderPrevious_OnClick : function(e) { this.setView(Calendar.getPreviousMonth(this.View)); },
	_TableHeaderNext_OnClick : function(e) { this.setView(Calendar.getNextMonth(this.View)); },
	_TextBox_OnClick : function(e) { DatePickers.hideExcept(this); this.show(); },
	_TextBox_OnKeyPress : function(e) { if (e.keyCode == 9 || e.keyCode == 13 || e.keyCode == 14) return this.setSelected(new Date(this.TextBox.value), false); },
	_TextBox_OnFocus : function(e) { DatePickers.hideExcept(this); this.show(); },

	Version : { Major:1, Minor:1, Revision:1, toString:function(){ return this.Major + "." + this.Minor + "." + this.Revision; } }
	/*
		Version 1.0.0 >> Initial Build
		Version 1.1.0 >> Options.XY can now be the ID of an Element instead of just an [x,y] coordinate
		Version 1.1.1 >> Fixed Event.observe calls to use fn.bindAsEventListener() for IE bugs
		              >> Added check to .hide() to check for updated info in TextBox upon blur
		              >> Fixed bug to allow including the endpoints of a DateRange
	*/
}
