
Calendar = function ()
{
	Dispatcher.call(this, "onbeforedraw", "onshow", "onbeforehide", "onhide", "onchange");

	this.elem = document.createElement("div");
	this.elem.className = "datecontrol";

	this.label = null;
	this.date = null;
	this.control = null;
	this.minDate = null;
	this.maxDate = null;

	this.displayMonth = null;
	this.displayYear = null;
	this.visible = false;

	this.buttonMBack = null;
	this.buttonMNext = null;

	this.settings = new CalendarSettings;

	document.body.insertBefore(this.elem, document.body.childNodes[0]);
}
Calendar.prototype = new Dispatcher();

Calendar.setup = function ()
{
	Calendar.current = new Calendar();

	var elems = document.getElementsByTagName("input");
	for (var i = 0; i < elems.length; i++)
	{
		if (elems[i].type == "text" && css.containsClassName(elems[i], "date"))
		{
			evt.addHandler(elems[i], "onfocus", createCallback(Calendar.current, "input_onfocus", [elems[i]]));
			evt.addHandler(elems[i], "onblur", createCallback(Calendar.current, "input_onblur", [elems[i]]));
			evt.addHandler(elems[i], "onkeyup", createCallback(Calendar.current, "input_onkeyup", [elems[i]]));
			evt.addHandler(elems[i], "onchange", createCallback(Calendar.current, "input_onchange", [elems[i]]));
			evt.addHandler(elems[i], "onmousedown", createCallback(Calendar.current, "input_onmousedown", [elems[i]]));
			evt.addHandler(elems[i], "onmouseup", createCallback(Calendar.current, "input_onmouseup", [elems[i]]));
			evt.addHandler(elems[i], "ondblclick", createCallback(Calendar.current, "input_ondblclick", [elems[i]]));
		}

		if (elems[i].type == "button" && elems[i].className.match(/linkto_/))
		{
			evt.addHandler(elems[i], "onmousedown", createCallback(Calendar.current, "button_onmousedown", [elems[i]]));
			evt.addHandler(elems[i], "onmouseup", createCallback(Calendar.current, "button_onmouseup", [elems[i]]));
		}
	}
	evt.addHandler(document, "onmousedown", createCallback(Calendar.current, "document_onmousedown"));
}
Calendar.current = null;

Calendar.dayNames = ["s", "m", "t", "w", "t", "f", "s"];
Calendar.monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
Calendar.firstWeekday = 1;
Calendar.singleDay = 86400000;
Calendar.defaultFormat = "dd-mm-yyyy";
Calendar.widthMonth = 150;
Calendar.labelSingle = "{0} {1}";
Calendar.labelDouble1 = "{0}-{1} {2}";
Calendar.labelDouble2 = "{0} {1} - {2} {3}";

Calendar.prototype.setMinDate = function (date)
{
	this.minDate = date;
}

Calendar.prototype.setMaxDate = function (date)
{
	this.maxDate = date;
}

Calendar.prototype.setDate = function (control, date, updateControl)
{
	if (arguments.length == 0 || date == null || date == "")
	{
		this.date = null;
		if (updateControl && control)
		{
			control.value = "";
		}
	}
	else
	{
		if (date instanceof Date)
			this.date = new Date(date);
		else
			this.date = new Date(Date.parse(date));

		//if (this.date && this.minDate && this.date < this.minDate)
			//this.date = new Date(this.minDate);
		//if (this.date && this.maxDate && this.date > this.maxDate)
			//this.date = new Date(this.maxDate);

		if (updateControl)
		{
			css.removeClassName(control, "defaultvalue");
			control.value = this.date.toString(this.settings.format);
		}
	}

	this.draw(this.date);
}

Calendar.prototype.getDate = function (elem)
{
	var date = null;
	if (elem)
	{
		if (elem.value != "")
		{
			date = new Date(Date.parse(elem.value));
			if (date && this.minDate && date < this.minDate)
				date = new Date(this.minDate);
			if (date && this.maxDate && date > this.maxDate)
				date = new Date(this.maxDate);
		}
		return date;
	}
	else
		return this.date;
}

Calendar.prototype.prepare = function (months)
{
	this.elem.innerHTML = "";

	var table, thead, tbody, tr, td;

	table = this.elem.appendChild(document.createElement("table"));
	tbody = table.appendChild(document.createElement("tbody"));

	table.className = "header";
	table.cellSpacing = 0;
	css.setPixelWidth(this.elem, Calendar.widthMonth * months);

	with (tbody.appendChild(document.createElement("tr")))
	{
		td = appendChild(document.createElement("td"));
		td.className = "prev";

		var button = document.createElement("input");
		button.type = "button";
		button.className = "button monthPrev";
		button.unselectable = "on";

		this.buttonMBack = button;

		td.appendChild(button);
		evt.addHandler(button, "onclick", createCallback(this, "moveMonth", [-1]));

		td = appendChild(document.createElement("td"));
		this.label = td.appendChild(document.createElement("div"));
		this.label.className = "label";
		this.label.innerHTML = "LABEL";

		td = appendChild(document.createElement("td"));
		td.className = "next";

		var button = document.createElement("input");
		button.type = "button";
		button.className = "button monthNext";
		button.unselectable = "on";

		this.buttonMNext = button;

		td.appendChild(button);
		evt.addHandler(button, "onclick", createCallback(this, "moveMonth", [1]));
	}

	var ftable = this.elem.appendChild(document.createElement("table"));
	var ftbody = ftable.appendChild(document.createElement("tbody"));
	for (var i = 0; i < months; i++)
	{
		if (i % 3 == 0)
			tr = ftbody.appendChild(document.createElement("tr"));

		td = tr.appendChild(document.createElement("td"));
		td.className = "frame";
		td.id = "cmonth" + i;

		var div = td.appendChild(document.createElement("div"));
		div.className = "clabel";
		div.id = "clabel" + i

		table = td.appendChild(document.createElement("table"));
		thead = table.appendChild(document.createElement("thead"));
		tbody = table.appendChild(document.createElement("tbody"));

		table.cellSpacing = 0;
		table.className = "calendar";
		table.onmousedown = createCallback(this, "calendar_mousedown");
		table.onmouseup = createCallback(this, "calendar_mouseup");

		with (thead.appendChild(document.createElement("tr")))
		{
			for (var d = 0; d < 7; d++)
			{
				var index = Calendar.firstWeekday + d;
				if (index > 6) index -= 7;

				td = appendChild(document.createElement("th"));
				td.innerHTML = Calendar.dayNames[index];
			}
		}
		evt.addHandler(table, "onmouseover", createCallback(this, "calendar_mouseover"));
		evt.addHandler(table, "onmouseout", createCallback(this, "calendar_mouseout"));
	}

	table = this.elem.appendChild(document.createElement("table"));
	tbody = table.appendChild(document.createElement("tbody"));

	table.className = "footer";
	table.cellSpacing = 0;
	css.setPixelWidth(table, Calendar.widthMonth * months);

	tr = tbody.appendChild(document.createElement("tr"));
	td = tr.appendChild(document.createElement("td"));

	var btn1 = document.createElement("input");
	var btn2 = document.createElement("input");
	btn1.type = "button";
	btn2.type = "button";
	btn1.value = "None";
	btn2.value = "Today";
	btn1.className = "button none";
	btn2.className = "button today";

	td.appendChild(btn1);
	td.appendChild(btn2);

	evt.addHandler(btn1, "onclick", createCallback(this, "calendar_setDateNone"));
	evt.addHandler(btn2, "onclick", createCallback(this, "calendar_setDateToday"));
}

Calendar.prototype.draw = function (year, month, day)
{
	var today = new Date();
	var date = null;
	var selection = false;
	if (arguments.length == 1 && arguments[0] instanceof Date)
	{
		date = new Date(arguments[0].getFullYear(), arguments[0].getMonth(), arguments[0].getDate());
		selection = true;
	}
	else if (arguments.length == 2)
	{
		date = new Date(arguments[0], arguments[1]);
	}
	else
	{
		if (this.minDate)
			var date = new Date(this.minDate.getFullYear(), this.minDate.getMonth());
		else
			date = new Date(today.getFullYear(), today.getMonth());
	}

	this.prepare(this.settings.months);

	var yearMin = date.getFullYear();
	var monthMin = date.getMonth();

	var ddate = new Date(date);
	for (var i = 0; i < this.settings.months; i++)
	{
		ddate.setMonth(date.getMonth() + i);
		var year = ddate.getFullYear();
		var month = ddate.getMonth();

		var cell = document.getElementById("cmonth" + i);
		var label = cell.getElementsByTagName("div")[0];
		label.innerHTML = Calendar.labelSingle.format(Calendar.monthNames[month], year);

		var table = cell.getElementsByTagName("table")[0];
		this.drawMonth(table, year, month, date, selection);
	}

	this.displayYear = yearMin;
	this.displayMonth = monthMin;

	var yearMax = ddate.getFullYear();
	var monthMax = ddate.getMonth();

	if (yearMin == yearMax && monthMin == monthMax)
		var label = Calendar.labelSingle.format(Calendar.monthNames[monthMin], yearMin);
	else if (yearMin == yearMax)
		var label = Calendar.labelDouble1.format(Calendar.monthNames[monthMin], Calendar.monthNames[monthMax], yearMin);
	else
		var label = Calendar.labelDouble2.format(Calendar.monthNames[monthMin], yearMin, Calendar.monthNames[monthMax], yearMax);

	this.label.innerHTML = label;
	if (this.minDate)
	{
		var dcur = new Date(this.displayYear, this.displayMonth);
		var dmin = new Date(this.minDate.getFullYear(), this.minDate.getMonth());
		if (dcur > dmin)
			css.display(this.buttonMBack);
		else
			css.undisplay(this.buttonMBack);
	}
	if (this.maxDate)
	{
		var dcur = new Date(this.displayYear, this.displayMonth);
		var dmax = new Date(this.maxDate.getFullYear(), this.maxDate.getMonth());
		if (dcur < dmax)
			css.display(this.buttonMNext);
		else
			css.undisplay(this.buttonMNext);
	}
}

Calendar.prototype.drawMonth = function (table, year, month, date, selection)
{
	var today = new Date();

	var cy = date.getFullYear();
	var cm = date.getMonth();
	var cd = date.getDate();

	var ty = today.getFullYear();
	var tm = today.getMonth();
	var td = today.getDate();

	var mdate = new Date(year, month, 1);
	while (mdate.getDay() != Calendar.firstWeekday)
		mdate.setDate(mdate.getDate() - 1);

	var weeks = 6;
	var days = 7;

	var timestart = mdate.getTime();
	var timeweek6 = timestart + ((35 + Calendar.firstWeekday) * Calendar.singleDay);
	if ((new Date(timeweek6)).getMonth() > month)
		weeks = 5;

	var tbody = table.getElementsByTagName("tbody")[0];
	while (tbody.childNodes.length)
		tbody.removeChild(tbody.childNodes[0]);

	var cell;
	for (var w = 0; w < weeks; w++)
	{
		with (tbody.appendChild(document.createElement("tr")))
		{
			for (var d = 0; d < days; d++)
			{
				cell = appendChild(document.createElement("td"));
				cell.setAttribute("unselectable", "on");

				var dy = mdate.getFullYear();
				var dm = mdate.getMonth();
				var dd = mdate.getDate();
				var dw = mdate.getDay();

				cell.date = mdate.toString("dd-mm-yyyy");
				cell.innerHTML = mdate.getDate();

				if (dm % 2 == 0)
					cell.className = "date meven";
				else
					cell.className = "date modd";

				if (dw == 0) css.addClassName(cell, "sunday");
				if (dw == 6) css.addClassName(cell, "saturday");

				if (dy == ty && dm == tm && dd == td)
				{
					css.addClassName(cell, "today");
				}

				if ((this.minDate && mdate < this.minDate) || (this.maxDate && mdate > this.maxDate))
				{
					css.addClassName(cell, "disabled");
				}

				if (selection == true && dy == cy && dm == cm && dd == cd)
					css.addClassName(cell, "selected");

				mdate.setDate(mdate.getDate() + 1);
			}
		}
	}
}

Calendar.prototype.moveMonth = function (count)
{
	var date = new Date(this.displayYear, this.displayMonth);
	date.setMonth(date.getMonth() + count);

	this.draw(date.getFullYear(), date.getMonth());
}

Calendar.prototype.button_onmousedown = function (elem)
{
	this.buttonMouseDown = true;
	if (elem.className.match(/linkto_(\w+)/))
	{
		var targetID = RegExp.$1;
		var targetObj = document.getElementById(targetID);
		if (this.control == targetObj)
			this.hide();
		else
			this.show(targetObj);
	}
}

Calendar.prototype.button_onmouseup = function (elem)
{
	this.buttonMouseDown = false;
}

Calendar.prototype.input_onblur = function (elem)
{
}

Calendar.prototype.input_onfocus = function (elem)
{
	if (this.visible)
		this.show(elem);
}

Calendar.prototype.input_ondblclick = function (elem)
{
	this.show(elem);
}

Calendar.prototype.input_onmousedown = function (elem)
{
	this.inputMouseDown = true;
}

Calendar.prototype.input_onmouseup = function (elem)
{
	this.inputMouseDown = false;
}

Calendar.prototype.input_onkeyup = function (elem)
{
	if (this.visible && elem.prevValue != elem.value)
	{
		elem.prevValue = elem.value;
		if (elem.value == "")
			this.setDate(elem, null);
		else
		{
			var date = this.settings.parseDate(elem.value);
			window.status = "Parsed: " + date;
			if (date != null)
				this.setDate(elem, date);
		}
	}
}

Calendar.prototype.input_onchange = function (elem)
{
	var current = this.settings.parseDate(elem.value);
	if (current && this.minDate && current < this.minDate)
		this.setDate(elem, this.minDate, true);
	if (current && this.maxDate && current > this.maxDate)
		this.setDate(elem, this.maxDate, true);
}

Calendar.prototype.document_onkeypress = function (e)
{
	var event = e || window.event;
	var charCode = (event.charCode) ? event.charCode : ((event.keyCode) ? event.keyCode : ((event.which) ? event.which : 0));

	if (this.visible && charCode == 27)
	{
		this.hide();
		return false;
	}
}

Calendar.prototype.document_onmousedown = function (e)
{
	var srcElement = evt.srcElement(e);
	if (!this.inputMouseDown && !this.buttonMouseDown && !dom.elementContains(this.elem, srcElement))
		this.hide();
}

Calendar.prototype.show = function (elem, dateObj)
{
	this.control = elem;
	this.settings = new CalendarSettings(elem);

	var x = dom.getLeft(elem);
	var y = dom.getTop(elem) + elem.offsetHeight + 2;

	if (dateObj && dateObj instanceof Date)
		var date = dateObj
	if (elem.value != "" && elem.value != elem.dateFormat)
		var date = new Date(Date.parse(elem.value));
	else
		var date = null;

	this.fireEvent("onbeforedraw");
	this.draw(date);

	css.setPixelCoord(this.elem, y, x);
	css.show(this.elem);

	this.visible = true;

	this.fireEvent("onshow");
}

Calendar.prototype.hide = function ()
{
	this.fireEvent("onbeforehide");
	this.elem.style.visibility = "hidden";
	this.visible = false;
	this.control = null;
	this.fireEvent("onhide");
}

Calendar.prototype.calendar_setDateNone = function ()
{
	this.setDate(this.control, null, true);
	this.fireEvent("onchange");
	this.hide();
}

Calendar.prototype.calendar_setDateToday = function ()
{
	this.setDate(this.control, new Date(), true);
	this.fireEvent("onchange");
	this.hide();
}

Calendar.prototype.calendar_mouseover = function (e)
{
	var srcElement = evt.srcElement(e);
	if (!css.containsClassName(srcElement, "disabled"))
		css.addClassName(srcElement, "hover");
}

Calendar.prototype.calendar_mouseout = function (e)
{
	var srcElement = evt.srcElement(e);
	if (!css.containsClassName(srcElement, "disabled"))
		css.removeClassName(srcElement, "hover");
}

Calendar.prototype.calendar_mousedown = function (e)
{
	var srcElement = evt.srcElement(e);
	if (!css.containsClassName(srcElement, "disabled"))
	{
		var dateString = "";
		while (srcElement)
		{
			if (srcElement.date != null)
			{
				css.addClassName(srcElement, "selected");
				this.setDate(this.control, srcElement.date, true);
				this.fireEvent("onchange");
				this.hide();
				break;
			}
			srcElement = srcElement.parentNode;
		}
	}
}

Calendar.prototype.calendar_mouseup = function (e)
{
}

Calendar.prototype.createEventObject = function (eventName)
{
	return { event: eventName, control: this, target: this.control, value: this.getDate() };
}

function CalendarSettings(elem)
{
	this.format = Calendar.defaultFormat;
	this.months = 2;
	this.century = 2000;

	if (elem && elem.className)
	{
		if (elem.className.match(/format_@(.*?)@/))
			this.format = RegExp.$1;
		if (elem.className.match(/months_(\d+)/))
			this.months = parseInt(RegExp.$1);
		if (elem.className.match(/century\^([^\^]+)\^/))
			this.century = parseInt(RegExp.$1);
	}

	if (this.format.match(/(?:d|m|y)+([^\d])(?:d|m|y)+\1(?:d|m|y)+/))
	{
		var separator = RegExp.$1;
		var parts = this.format.split(separator);
		var expression = new RegExp("(\\d{1,{0}}){3}(\\d{1,{1}}){3}(\\d{1,{2}})".format(
			parts[0].length, parts[1].length, parts[2].length, separator));

		var indexd, indexm, indexy;
		for (var i = 0; i < parts.length; i++)
		{
			if (parts[i].match("d"))
				indexd = i+1;
			else if (parts[i].match("m"))
				indexm = i+1;
			else if (parts[i].match("y"))
				indexy = i+1;
		}

		this.expression = expression;
		this.indexd = indexd;
		this.indexm = indexm;
		this.indexy = indexy;
	}
}

CalendarSettings.prototype.parseDate = function (value)
{
	var result;
	if ((result = value.match(this.expression)))
	{
		var day = result[this.indexd];
		var month = result[this.indexm];
		var year = result[this.indexy];

		if (year.length == 3)
			year = year.substring(1);
		if (year.length < 4)
			year = this.century + parseInt(year);

		var date = new Date(year, month-1, day);
		return date;
	}
	return null;
}


evt.addHandler(window, "onready", Calendar.setup);
