﻿// A better calendar popup. Written by Geoffrey J. Swenson / geewhizbang.com
//
//  Entirely client-side javascript, no server side postback necessary, so it is fast and flexible.
//	Should be compatible with most AJAX frameworks, as all it does is popup and set the value of a page control, usually a text box.
//	Tested with Opera, Firefox, and IE6 / IE7.
//  Includes useful extensions to the javascript Date object, but may be incompatible with code that has also extended the Date object.
// 
//

var CurrentCalendar = null;
function Calendar(AnchorElement, format, SelectedDate)
{
	this.AnchorElement = getObj(AnchorElement);
	this.SelectedDate = null;
	if (typeof(SelectedDate) == "undefined")
	{
		if (IsDate.Check(this.AnchorElement.value))
		{
			this.SelectedDate = IsDate.DateValue;
		}
		else
		{
			this.SelectedDate = Now().start();
		}
	}
	else
	{
		this.SelectedDate = SelectedDate;
	}
	this.CurrentMonth = this.SelectedDate.firstDayOfMonth();
	this.Format = format;
	CurrentCalendar = this;
	this.show()
}

function ShowCal(TbObjOrId, OnSelect)
{
	tb = getObj(TbObjOrId);
	if (tb.disabled) return false;
	new Calendar(tb, "%MM/%DD/%YYYY");
	if (typeof(OnSelect) != undefined)
	{
		CurrentCalendar.OnSelect = OnSelect;
	}
	else
	{
		CurrentCalendar.OnSelect = null;
	}
	return false;
}

Calendar.prototype.BuildButton = function (UpDn, incr)
{
	var szOut = "<td class='Cal_" + UpDn + "' onclick='CurrentCalendar.show(" + incr + ");' onmouseover=\"this.className='Cal_" + UpDn + "_mo';\" onmouseout=\"this.className='Cal_" + UpDn + "';\">&nbsp;</td>";
	return szOut
}

Calendar.prototype.close = function()
{
	document.body.removeChild(this.element);
	document.body.removeChild(this.backgroundElement);
	this.OnSelect();
	CurrentCalendar = null;
}

Calendar.prototype.click = function(iMonth, iDay)
{
	var m = Number(this.CurrentMonth.getMonth());
	m = m + Number(iMonth) + 1;
	var y = this.CurrentMonth.getFullYear();
	if (m > 12)
	{
		m = m-12;
		y++;
	}
	else if (m < 1)
	{
		m = m+12;
		y--;
	}
	this.AnchorElement.value = m + "/" + iDay + "/" + y;
	this.close();
}


Calendar.prototype.CurrentDate = function()
{
	this.AnchorElement.value = Now().format(this.Format);
	this.close();
}

Calendar.prototype.show = function(MonthOffset)
{	
	if (typeof(MonthOffset) == "undefined") var MonthOffset = 0;
	this.CurrentMonth = this.CurrentMonth.addMonths(MonthOffset);
	
	var iDayOfWeek = this.CurrentMonth.getDay();
	var BaseDate;
	var iDisplayRows = 6;
	var iMonth = -1;
	if (iDayOfWeek == 0) 
	{
		iMonth = 0
		BaseDate = this.CurrentMonth;
		iDisplayRows = 5
	}
	else
	{
		BaseDate = this.CurrentMonth.addDays(-iDayOfWeek);
	}
	var iDay = BaseDate.getDate();
	
	var iLastDay = BaseDate.getDaysInMonth();
	
	var iMarkDate = -1
	if (this.CurrentMonth.getFullYear() == this.SelectedDate.getFullYear() && this.CurrentMonth.getMonth() == this.SelectedDate.getMonth())
	{
		iMarkDate = this.SelectedDate.getDate();
	}
	var szHtml = "<table cellpadding=0 cellspacing=0><tr>";
	szHtml += this.BuildButton("dn", -1) + "<td class='Cal_Month'>" + this.CurrentMonth.getMonthName() + "</td>" + this.BuildButton("up", 1) + "<td class='Cal_MYSpacer'>&nbsp;</td>";
	szHtml += this.BuildButton("dn", -12) + "<td class='Cal_Year'>" + this.CurrentMonth.getFullYear()  + "</td>" + this.BuildButton("up", 12);
	szHtml += "</tr></table><table cellpadding=0 cellspacing=0 class='Cal_tblHed'><tr>"
	var arr = new Array("Su","Mo","Tu","We","Th","Fr","Sa")
	for (i=0; i < 7; i++)
	{
		szHtml += "<td class='Cal_TH'>" + arr[i] + "</td>";
	}
	szHtml += "</tr></table>"
	szHtml += "<table cellpadding=0 cellspacing=0>"
	var bContinue = true;
	var iOdd = (iMonth == 0 ? 0 : 1);
	var iRows = 0;
	for (z=0; z < iDisplayRows && bContinue; z++)
	{	
		if (iMonth == 1)
		{
			bContinue = false;
		}
		else
		{	
			iRows++;
			szHtml += "<tr>";
			for (i=0; i < 7; i++)
			{
				szHtml += "<td class='Cal_m' onclick='CurrentCalendar.click(" + iMonth + "," + iDay + ");' onmouseover=\"this.className='Cal_m_mo';\" onmouseout=\"this.className='Cal_m';\">"
				var szMark = "";
				if (iMarkDate > 0 && iMonth == 0 && iMarkDate == iDay)
				{
					szMark = " Cal_Mark";
				}
				szHtml += "<span class='Cal_OE" + iOdd + szMark + "'>" + iDay + "</span></td>";
				iDay++;
				if (iDay > iLastDay)
				{
					iDay = 1;
					iMonth++;
					iOdd = (iMonth == 0 ? 0 : 1);
					iLastDay = this.CurrentMonth.addMonths(iMonth).getDaysInMonth();
				}
			}
			szHtml += "</tr>";
		}
	}
	while (iRows < 6)
	{
		szHtml += "<tr><td colspan='7' class='Cal_BR'></td></tr>";
		iRows++;
	}
	
	szHtml += "<tr><td colspan='7' class='Cal_BtnRow'><span class='Cal_btn' onmouseover=\"this.className='Cal_btn_mo'\" onmouseout=\"this.className='Cal_btn'\" onclick='CurrentCalendar.CurrentDate()'>Today</td></td></tr></table>";
	if (this.element == null)
	{
		this.backgroundElement = addElement("div", document.body, "CalBG_" + this.AnchorElement.id, "<div class='Cal_BgFreeze' onclick='CurrentCalendar.close();'>&nbsp;</div>", "Cal_BgParent", true);
		this.element = addElement("div", document.body, "Cal_" + this.AnchorElement.id, szHtml, "Cal_Outer", true);
	}
	else
	{
		this.element.innerHTML = szHtml;
	}
	this.backgroundElement.style.height = document.body.clientHeight + "px";
	this.backgroundElement.style.width = document.body.clientWidth + "px";
	PositionPopUp(this.element, this.AnchorElement) 
}

// Global Now() function
function Now() 
{
	return new Date();
}

Date.prototype.addMilliseconds = function(ms)
{	
	return new Date(new Date().setTime(this.getTime() + (ms)));	
}

Date.prototype.addSeconds = function(s)
{
	return AddTime(this, 1000, s)
}

Date.prototype.addMinutes = function(m)
{
	return AddTime(this, 60000, m)
}

Date.prototype.addHours = function(h)
{
	return AddTime(this, 3600000, h);
}

Date.prototype.addDays = function(d)
{
	return AddTime(this, 86400000, d);
}

function AddTime (theDate, Incr, howMany)
{
	var Count = Math.abs(Math.round(howMany))
	var incr = (howMany > 0) ? Incr : -Incr;
	for (var i=0; i < Count; i++)
	{
		theDate = theDate.addMilliseconds(incr);	
	}
	return theDate
}

Date.prototype.addWeeks = function(w)
{
	return AddTime(this, 604800000, w)
}

Date.prototype.addMonths = function(mm)
{
	mm = Math.floor(mm)
	if (mm == 0) return this;
	var iMAbs = Math.abs(mm);
	var iSign = (mm < 0 ? -1 : 1)	
	var iYDiff = iSign * Math.floor(iMAbs / 12);
	var iMDiff = iSign * (iMAbs % 12);
	
	var iYear = this.getFullYear()+iYDiff;
	var iMonth = this.getMonth() + iMDiff;
	
	var iDay = this.getDate();
	if (iDay > 28)
	{
		iMonthDays = getDaysInMonth(iMonth, iYear);
		if (iDay > iMonthDays)
		{
			this.setDays(iMonthDays);
		}
	}
	var dt = this.clone();
	dt.setYear(iYear);
	dt.setMonth(iMonth);
	return dt;
}

Date.prototype.addYears = function(yy)
{
	dt.setYear(this.getFullYear()+yy);
}

Date.prototype.getMonthName = function() 
{
	var m = new Array ("January","February","March","April","May","June","July","August","September","October","November","December");
	return m[(0 == arguments.length) ? this.getMonth() : arguments[0]]
}

Date.prototype.getMonthAbbreviation = function() 
{
	var m = new Array ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
	return m[(0 == arguments.length) ? this.getMonth() : arguments[0]]
}

Date.prototype.getDayName = function() 
{
	var d = Array ("Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday","Saturday");
	return d[(0 == arguments.length) ? this.getDay() : arguments[0]];
}

Date.prototype.getDayAbbreviation = function() 
{
	var d = Array ("Sun","Mon", "Tue","Wed","Thu","Fri","Sat");
	return d[(0 == arguments.length) ? this.getDay() : arguments[0]];
}

Date.prototype.getCivilianHours = function() 
{
	return (this.getHours() < 12) ? this.getHours() : this.getHours() - 12;
}
Date.prototype.getMeridiem = function() 
{
	return (this.getHours() < 12) ? "AM" : "PM";
}

Date.prototype.to_s = Date.prototype.toString;
Date.prototype.format = function(fs) 
{
	 fs = fs.replace(/%YYYY/, this.getFullYear().toString());
	 fs = fs.replace(/%YY/, this.getFullYear().toString().substr(2,2));
	 
	 fs = fs.replace(/%MMMM/, this.getMonthName(this.getMonth()).toString());
	 fs = fs.replace(/%MMM/, this.getMonthAbbreviation(this.getMonth()).toString());
	 fs = fs.replace(/%MM/, (this.getMonth() + 1) > 9 ? (this.getMonth() + 1).toString() : "0" + (this.getMonth() + 1).toString());
	 fs = fs.replace(/%M/, (this.getMonth() + 1).toString());
	 
	 fs = fs.replace(/%DDDD/, this.getDayName(this.getDay()).toString());
	 fs = fs.replace(/%DDD/, this.getDayAbbreviation(this.getDay()).toString());
	 fs = fs.replace(/%DD/, this.getDate() > 9 ? this.getDate().toString() : "0" + this.getDate().toString());
	 fs = fs.replace(/%D/, this.getDate().toString());
	 
	 fs = fs.replace(/%HH/, this.getHours() > 9 ? this.getHours().toString() : "0" + this.getHours().toString());
	 fs = fs.replace(/%H/, this.getHours().toString());
	 fs = fs.replace(/%hh/, this.getCivilianHours() > 9 ? this.getCivilianHours().toString() : "0" + this.getCivilianHours().toString());
	 fs = fs.replace(/%h/, this.getCivilianHours());

	 fs = fs.replace(/%mm/, this.getMinutes() > 9 ? this.getMinutes().toString() : "0" + this.getMinutes().toString());
	 fs = fs.replace(/%m/, this.getMinutes().toString());

	 fs = fs.replace(/%ss/, this.getSeconds() > 9 ? this.getSeconds().toString() : "0" + this.getSeconds().toString());
	 fs = fs.replace(/%s/, this.getSeconds().toString());

	 fs = fs.replace(/%nnn/, this.getMilliseconds().toString());
	 fs = fs.replace(/%p/, this.getMeridiem());
	 return fs;
}

Date.prototype.getDaysInMonth = function() 
{
	var mm = this.getMonth();
	switch (mm)
	{
		case 1:
			return (this.getFullYear() ? 29 : 28);
			break;
		default:
			var m = new Array ( 31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
			return m[mm];
			break;
	}
}

Date.prototype.toString = function() 
{
	if (0 == arguments.length || 1 < arguments.length) return this.to_s();
	return this.format(arguments[0].toString());
} 

Date.prototype.isLeapYear = function() 
{
	return isLeapYear(this.getFullYear());
}

function isLeapYear(iYear)
{
	if (0 == iYear % 400) return true;
	if (0 == iYear % 100) return false;
	return (0 == iYear % 4) ? true : false;
}

Date.prototype.start = function() 
{
	var dt = this.clone();
	dt.setHours(0);
	dt.setMinutes(0);
	dt.setSeconds(0);
	dt.setMilliseconds(1);
	return dt;
}

Date.prototype.noon = function() 
{
	var dt = this.clone();
	dt.setHours(12);
	dt.setMinutes(0);
	dt.setSeconds(0);
	dt.setMilliseconds(0);
	return dt;
}

Date.prototype.firstDayOfMonth = function() 
{	
	return new Date(this.getFullYear(), this.getMonth(), 1, 12, 0, 0);
}
Date.prototype.lastDayOfMonth = function() 
{	
	return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth(), 12, 0, 0);
}

Date.prototype.clone = function() 
{
	var dt = new Date();
	dt.setTime(this.getTime());	
	return dt;
}

function DateChecker()
{
	this.DateValue = null;
}

DateChecker.prototype.Check = function(szValue)
{
	if (szValue == "")
	{
		return false;
	}
	this.DateValue = null;
	try
	{
		var dt = new Date(szValue);
		if (isNaN(dt)) return false;
		this.DateValue = dt;
		var szArrDate = szValue.split('/');
		if (szArrDate.length != 3) return false;
		if (dt.getMonth()+1 != Number(szArrDate[0])) return false;
		if (dt.getDate() != Number(szArrDate[1])) return false;
		var FullYear = dt.getFullYear()
		if (FullYear != Number(szArrDate[2])) return false;
		
		if (FullYear < 1995) return false;
		//var dtNow = new Date()
		//if (dtNow.addDays(5000) < dt) return false;
	}
	catch (e)
	{
		return false;
	}
	return true;
}

var IsDate = new DateChecker()