var oDisplay, oValue, oCalDiv;
var sCalPath = '';
var nInput = 0;
var oCurCal;
var oCals = new Array();

var bHasCalDiv = false;
var nTimerID = 0;
var nTickerID = 0;

var dToday = dateOnly(new Date());

var sMonth = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
var sMonthShort = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
var sDay = new Array("Su", "Mo", "Tu", "We", "Th", "Fr", "Sa");
var sDayLong = new Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
//var sDayLong = new Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");

var c_LOCALE_CODE_UK = "en-gb";
var c_LOCALE_CODE_US = "en-us";

// SQLServer stores the null date as a time component, which comes back to the 
// web with a default day, month and year:
var c_NULL_DATE = "1899-12-30 00:00:00";

var oTempPopup, bTempShow;

sCalPath = getPath();


function twoDigits(n)
{
	var mod = n % 100;
	if (mod < 10)
		return "0" + mod;
	else
		return mod;
}

function formatDateServer(dDate, bSuppressTimeComponent)
{
	if (!dDate)
		return "";

	return dDate.getFullYear() + "-" + twoDigits(dDate.getMonth() + 1) + "-" + twoDigits(dDate.getDate()) + 
		(bSuppressTimeComponent ? "" : " " + twoDigits(dDate.getHours()) + ":" + twoDigits(dDate.getMinutes()) + ":" + twoDigits(dDate.getSeconds()));
}

function formatDateDisplay(dDate, bSuppressTimeComponent)
{
	if (!dDate)
		return "";

	var sDate, sTime;
	
	sDate = sDayLong[dDate.getDay()] + ", " + dDate.getDate() + " " + sMonthShort[dDate.getMonth()] + " " + twoDigits(dDate.getFullYear());
	sTime = twoDigits(dDate.getHours()) + ":" + twoDigits(dDate.getMinutes()) + ":" + twoDigits(dDate.getSeconds());
	if (bSuppressTimeComponent)
		return sDate;
	else
		return sDate + " " + sTime;
}

function stringToDate(sDate, bNoDefaultDate)
{
	if (sDate) {
		// check if the date string should be interpreted as null
		if (sDate == c_NULL_DATE || sDate == '0') {
			null;
		} else if (sDate.match(/^\d{2}\D\d{2}\D\d{4} \d{2}\D\d{2}\D\d{2}$/)) {
			return new Date(sDate.substr(6, 4), sDate.substr(3, 2) - 1, sDate.substr(0, 2), sDate.substr(11,2), sDate.substr(14, 2));
		} else if (sDate.match(/^\d{4}\D\d{2}\D\d{2} \d{2}\D\d{2}\D\d{2}$/)) {
			return new Date(sDate.substr(0, 4), sDate.substr(5, 2) - 1, sDate.substr(8, 2), sDate.substr(11,2), sDate.substr(14, 2));
		} else if (sDate.match(/^\d{4}\D\d{2}\D\d{2}$/)) {
			return new Date(sDate.substr(0, 4), sDate.substr(5, 2) - 1, sDate.substr(8, 2));
		}
	}
	if (bNoDefaultDate) {
		return null;
	}
	return new Date();
}

function displayDate(sDate, bSuppressTimeComponent)
{
	// the second parameter is optional; if this parameter is not supplied 
	// then the full date and time will be displayed in the long date format 
	// for that region
	document.write(formatDateDisplay(stringToDate(sDate), bSuppressTimeComponent));
}

function displayDateWithoutTime(sDate)
{
	// call the displayDate function with the suppress time boolean set to 
	// true, i.e. do not display the time component
	displayDate(sDate, true);
}

function dateOnly(dDate)
{
	if (dDate) {
		return new Date(dDate.getFullYear(), dDate.getMonth() ,dDate.getDate());
	} else {
		return null;
	}
}

function getDaysInMonth(dDate)
{
	var nMonth = dDate.getMonth() + 1;
	var nYear = dDate.getFullYear();
	if ((nMonth == 1 ) || (nMonth == 3) || (nMonth == 5) || (nMonth == 7) || (nMonth == 8) || (nMonth == 10) || (nMonth == 12))
		return 31;
	else if ((nMonth == 4 ) || (nMonth == 6) || (nMonth == 9) || (nMonth == 11))
		return 30;
	else if ((nYear % 400) == 0)
		return 29;
	else if ((nYear % 100) == 0)
		return 28;
	else if ((nYear % 4) == 0)
		return 29;
	else
		return 28;
}

function getMonthName(dDate)
{
	return sMonth[dDate.getMonth()];
}

function cal_navBackMonth()
{
	if (!(oCurCal.dDate)) {
		oCurCal.dDate = dToday;
	}

	var nMonth = oCurCal.dDate.getMonth();

	if (nMonth == 0)
		oCurCal.dDate = new Date(oCurCal.dDate.getFullYear() - 1, 11, 1);
	else
		oCurCal.dDate = new Date(oCurCal.dDate.getFullYear(), nMonth - 1, 1);
	oCurCal.drawCal();
	return false;
}

function cal_navForwardMonth()
{
	if (!(oCurCal.dDate)) {
		oCurCal.dDate = dToday;
	}

	var nMonth = oCurCal.dDate.getMonth();

	if (nMonth == 11)
		oCurCal.dDate = new Date(oCurCal.dDate.getFullYear() + 1, 0, 1);
	else
		oCurCal.dDate = new Date(oCurCal.dDate.getFullYear(), nMonth + 1, 1);
	oCurCal.drawCal();
	return false;
}

function cal_navBackYear()
{
	if (!(oCurCal.dDate))
		oCurCal.dDate = dToday;

	oCurCal.dDate = new Date(oCurCal.dDate.getFullYear() - 1, oCurCal.dDate.getMonth(), 1);
	oCurCal.drawCal();
	return false;
}

function cal_navForwardYear()
{
	if (!(oCurCal.dDate))
		oCurCal.dDate = dToday;

	oCurCal.dDate = new Date(oCurCal.dDate.getFullYear() + 1, oCurCal.dDate.getMonth(), 1);
	oCurCal.drawCal();
	return false;
}

function clearDate()
{

	oCurCal.dTime = null;
	oCurCal.dDate = null;
	oCurCal.dSelDate = null;

	oCurCal.oInput.value = ''
	oCurCal.oDisplay.value = '';
	hideCal(true);

	if (oCurCal.fOnChange && (oCurCal.dTime == null)) {
		oCurCal.fOnChange(oCurCal);
	}
	return false;
}

function cal_navToday()
{
	oCurCal.setDate(dToday);
	oCurCal.drawCal();
	return false;
}

function cal_choose(nDay)
{
	if (!(oCurCal.dDate)) {
		oCurCal.dDate = dToday;
	}

	oCurCal.setDate(new Date(oCurCal.dDate.getFullYear(), oCurCal.dDate.getMonth(), nDay));
	hideCal(true);
	return false;
}

function cal_hourUp()
{
	if (!(oCurCal.dTime)) {
		oCurCal.dTime = new Date();
	}

	oCurCal.setTime(new Date(oCurCal.dTime.getTime() + new Date(1970, 0, 1, 1).getTime()));
	drawTime();
}

function cal_hourDown()
{
	if (!(oCurCal.dTime)) {
		oCurCal.dTime = new Date();
	}

	oCurCal.setTime(new Date(oCurCal.dTime.getTime() - new Date(1970, 0, 1, 1).getTime()));
	drawTime();
}

function cal_minuteUp()
{
	if (!(oCurCal.dTime)) {
		oCurCal.dTime = new Date();
	}

	oCurCal.dTime.setMinutes(oCurCal.dTime.getMinutes() - oCurCal.dTime.getMinutes() % 5);
	oCurCal.setTime(new Date(oCurCal.dTime.getTime() + new Date(1970, 0, 1, 0, 5).getTime()));
	drawTime();
}

function cal_minuteDown()
{
	if (!(oCurCal.dTime)) {
		oCurCal.dTime = new Date();
	}

	oCurCal.dTime.setMinutes(oCurCal.dTime.getMinutes() + 4);
	oCurCal.dTime.setMinutes(oCurCal.dTime.getMinutes() - oCurCal.dTime.getMinutes() % 5);
	oCurCal.setTime(new Date(oCurCal.dTime.getTime() - new Date(1970, 0, 1, 0, 5).getTime()));
	drawTime();
}

function startTicker(sFunction)
{
	if (nTickerID != 0)
		clearInterval(nTickerID);
	eval(sFunction + "()");
	nTickerID = setInterval(sFunction + "()", 200);
}

function stopTicker()
{
	if (nTickerID != 0)
		clearInterval(nTickerID);
	nTickerID = 0;
}


function drawTime()
{
	document.getElementById('calcontroltime').innerHTML = twoDigits(oCurCal.dTime.getHours()) + ":" + twoDigits(oCurCal.dTime.getMinutes());
}

function getOffsetLeft(o)
{
	if (o == null)
		return 0;
	else if (o.tagName == "BODY")
		return 0;
	else
		return o.offsetLeft + getOffsetLeft(o.offsetParent);
}

function getOffsetTop(o)
{
	if (o == null)
		return 0;
	else if (o.tagName == "BODY")
		return 0;
	else
		return o.offsetTop + getOffsetTop(o.offsetParent);
}

function setSelectVisibility(oPopup, bShow)
{
	var n;
	var oSels, oSel;
	var nPLeft, nPRight, nPTop, nPBottom;
	var nSLeft, nSRight, nSTop, nSBottom;

	if (oPopup.style.top != "")
	{
		if ((oPopup.offsetTop + "px") != oPopup.style.top)
		{
			oTempPopup = oPopup;
			bTempShow = bShow;
			setTimeout("setSelectVisibility(oTempPopup, bTempShow)", 0);
			return;
		}
	}

	nPLeft = getOffsetLeft(oPopup);
	nPTop = getOffsetTop(oPopup);
	nPRight = nPLeft + oPopup.offsetWidth;
	nPBottom = nPTop + oPopup.offsetHeight;

	var oSels = document.getElementsByTagName("SELECT")
	for (n = 0; n < oSels.length; n++)
	{
		oSel = oSels[n];
		nSLeft = getOffsetLeft(oSel);
		nSTop = getOffsetTop(oSel);
		nSRight = nSLeft + oSel.offsetWidth;
		nSBottom = nSTop + oSel.offsetHeight;
		if ((nSLeft < nPRight) && (nSRight > nPLeft) && (nSTop < nPBottom) && (nSBottom > nPTop)) 
			if (bShow)
				oSel.style.visibility = "visible";
			else
				oSel.style.visibility = "hidden";
	}
}

function showCal(oCal)
{
	var nTop;
	
	if (nTimerID != 0)
	{
		clearTimeout(nTimerID);
		nTimerID = 0;
	}

	if ((oCal != null) && (oCal.nID != null))
	{
		oCurCal = oCal;
		oCurCal.dDate = dateOnly(oCurCal.dTime);
		oCurCal.drawCal();
		nTop = getOffsetTop(oCal.oDisplay) + oCal.oDisplay.offsetHeight;
		oCalDiv.style.top = nTop + "px";
		oCalDiv.style.left = getOffsetLeft(oCal.oDisplay) + "px";
		if (nTop != oCalDiv.offsetTop) // Kludge for IE 5.0
		{
			setTimeout("showCal(oCurCal)", 0);
			return;
		}

		if (((oCalDiv.offsetTop + oCalDiv.offsetHeight) - (document.body.scrollTop + document.body.clientHeight)) > 0)
			oCalDiv.style.top = (getOffsetTop(oCal.oDisplay) - oCalDiv.offsetHeight) + "px";
	}

	if (oCalDiv.style.visibility == 'visible')
		return;
	setSelectVisibility(oCalDiv, false);
	oCalDiv.style.visibility = "visible";	
}

function hideCal(bImmediate)
{
	if (oCalDiv.style.visibility == 'hidden')
		return;

	if (!((typeof(bImmediate) == "boolean") && bImmediate))
	{
		nTimerID = setTimeout("hideCal(true)", 500);
		return;
	}

	setSelectVisibility(oCalDiv, true);
	oCalDiv.style.visibility = 'hidden';	
}

function getPath() {

	var sCalPath = '';
	var nSheet, n, sHref;

	for (nSheet = 0; nSheet < document.styleSheets.length; nSheet++)
	{		
		sHref = document.styleSheets[nSheet].href;		
		if ((n = sHref.lastIndexOf("/")) != -1)
			if (sHref.substr(n + 1) == "calendar.css")
				sCalPath = sHref.substr(0, n + 1);
	}
	
	return sCalPath;
}


function CCal_drawCal()
{
	var sHTML;
	var n;
	var dDay;
	var bDisabled, bTodayOK;
	var nDay = 0;

	var vCalendarDateTime = new Date();
	var vCalendarDate = new Date();
	var vSelectedDate = new Date();

	if (this.dTime) {
		vCalendarDateTime = this.dTime;
	}
	if (this.dDate) {
		vCalendarDate = this.dDate;
	}
	if (this.dSelDate) {
		vSelectedDate = this.dSelDate;
	}

	var nDayLast = getDaysInMonth(vCalendarDate);
	var nDay1 = new Date(vCalendarDate.getFullYear(), vCalendarDate.getMonth(), 1).getDay();

	sHTML = "<table border='0' cellspacing='2' cellpadding='0' width='100%'>";
	sHTML = sHTML + "<tr><td class='calnav'><a href='#' class='calnav' title='Previous month' onclick='return cal_navBackMonth()'>&lt;</a></td>";
	sHTML = sHTML + "<td class='calmonth' colspan='5'>" + getMonthName(vCalendarDate) + "</td>";
	sHTML = sHTML + "<td class='calnav'><a href='#' class='calnav' title='Next month' onclick='return cal_navForwardMonth()'>&gt;</a></td></tr>";
	sHTML = sHTML + "<tr><td class='calnav'><a href='#' class='calnav' title='Previous year' onclick='return cal_navBackYear()'>&lt;&lt;</a></td>";
	sHTML = sHTML + "<td class='calmonth' colspan='5'>" + vCalendarDate.getFullYear() + "</td>";
	sHTML = sHTML + "<td class='calnav'><a href='#' class='calnav' title='Next year' onclick='return cal_navForwardYear()'>&gt;&gt;</a></td></tr>";

	sHTML = sHTML + "<tr>";
	for (n = 0; n < 7; n++)
		sHTML = sHTML + "<td class='calweekday'>" + sDay[n] + "</td>";

	for (n = 0; n < 42; n++)
	{
		if (n % 7 == 0)
			sHTML = sHTML + "</tr><tr>";
		if ((nDay > 0) || (n == nDay1))
			dDay = new Date(vCalendarDate.getFullYear(), vCalendarDate.getMonth(), ++nDay);
		else
			dDay = new Date(0);
		if ((nDay > 0) && (nDay <= nDayLast))
		{
			bDisabled = (dDay < this.dMinDate) || (dDay > this.dMaxDate);
			if (dDay.getTime() == vSelectedDate.getTime())
				sHTML = sHTML + "<td class='calselday'>";
			else if (dDay.getTime() == dToday.getTime())
				sHTML = sHTML + "<td class='caltoday'>";
			else if (bDisabled)
				sHTML = sHTML + "<td class='caldisday'>";
			else
				sHTML = sHTML + "<td class='calday'>";
			if (bDisabled)
				sHTML = sHTML + nDay;
			else
				sHTML = sHTML + "<a href='#' class='calenabled' onclick='return cal_choose(" + nDay + ")'>" + nDay + "</a>";
			sHTML = sHTML + "</td>";
		}
		else
			sHTML = sHTML + "<td class='calday'>&nbsp;</td>";
	}

	sHTML = sHTML + "</tr>";
	bTodayOK = (dToday >= this.dMinDate) && (dToday <= this.dMaxDate);
	if (bTodayOK || !this.bIsMandatory || !this.bNoTimeComponent)
	{
		sHTML = sHTML + "<tr><td class='calstd' colspan='7'><table style='width: 100%'><tr>";

		if (this.bNoTimeComponent) {
			if (!this.bIsMandatory)
				sHTML = sHTML + "<td class='calstd' align='left'><a href='#' class='calnav' onclick='return clearDate()'>Clear</a></td>";
		} else {
			sHTML = sHTML + "<td class='calstd'>"
			sHTML = sHTML + "<table class='caltimetable' onmouseout='stopTicker()'><tr>";
			sHTML = sHTML + "<td class='caltimenav'><img src='" + sCalPath + "cal_time_up.gif' onmousedown='startTicker(\"cal_hourUp\")' onmouseup='stopTicker()'></td>";
			sHTML = sHTML + "<td class='caltime' rowspan='2' id='calcontroltime'>" + twoDigits(vCalendarDateTime.getHours()) + ":" + twoDigits(vCalendarDateTime.getMinutes()) + "</td>";
			sHTML = sHTML + "<td class='caltimenav'><img src='" + sCalPath + "cal_time_up.gif' onmousedown='startTicker(\"cal_minuteUp\")' onmouseup='stopTicker()'></td>";
			sHTML = sHTML + "</tr><tr>"
			sHTML = sHTML + "<td class='caltimenav'><img src='" + sCalPath + "cal_time_down.gif' onmousedown='startTicker(\"cal_hourDown\")' onmouseup='stopTicker()'></td>";
			sHTML = sHTML + "<td class='caltimenav'><img src='" + sCalPath + "cal_time_down.gif' onmousedown='startTicker(\"cal_minuteDown\")' onmouseup='stopTicker()'></td>";
			sHTML = sHTML + "</tr></table>";
			sHTML = sHTML + "</td>";	
			if (!this.bIsMandatory)
				sHTML = sHTML + "<td class='calstd' align='center'><a href='#' class='calnav' onclick='return clearDate()'>Clear</a></td>";
		}

		if (bTodayOK)
			sHTML = sHTML + "<td class='calstd' align='right'><a href='#' class='calnav' onclick='return cal_navToday()'>Go to today</a></td>";

		sHTML = sHTML + "</tr></table></td></tr>";
	}
	sHTML = sHTML + "</table>";

	oCalDiv.innerHTML = sHTML;
}

function CCal_setDate(dDate, bNoChange)
{
	if (!(this.dTime)) {
		this.dTime = new Date();
	}

	this.setTime(new Date(dDate.getFullYear(), dDate.getMonth() ,dDate.getDate(),
		this.dTime.getHours(), this.dTime.getMinutes()), bNoChange);
}

function CCal_setTime(dTime, bNoChange, dTimeAsString)
{	
	this.dOldTime = this.dTime;
	this.dTime = dTime;
	this.dDate = dateOnly(this.dTime);
	this.dSelDate = this.dDate;

	if (!this.bIsMandatory && dTimeAsString == '0') {
		this.oInput.value = '';
		this.oDisplay.value = '';
	}
	else {
		this.oInput.value = formatDateServer(this.dTime, this.bNoTimeComponent);
		this.oDisplay.value = formatDateDisplay(this.dTime, this.bNoTimeComponent);
	}
	if (this.fOnChange && !bNoChange) {
		this.fOnChange(this);
	}
}

function CCal(sHiddenName, sDisplayName, sImgName, sDate, sMinDate, sMaxDate, fOnChange, bIsMandatory, bNoDefaultDate, bNoTimeComponent)
{
	var textCal = "Click to select a date";
	this.drawCal = CCal_drawCal;
	this.setDate = CCal_setDate;
	this.setTime = CCal_setTime;

	this.dMinDate = stringToDate(sMinDate);
	this.dMaxDate = stringToDate(sMaxDate);
	this.bIsMandatory = bIsMandatory ? true : false;
	this.bNoDefaultDate = bNoDefaultDate ? true : false;
	this.bNoTimeComponent = bNoTimeComponent ? true : false;
	this.fOnChange = fOnChange;

	this.nID = oCals.length;
	oCals[this.nID] = this;

	this.oInput = document.getElementById(sHiddenName);
	this.oInput.original_value = sDate;
	
	this.oDisplay = document.getElementById(sDisplayName);
	this.oDisplay.onfocus = this.oDisplay.blur;
	this.oDisplay.onclick = new Function("showCal(oCals[" + this.nID + "]); return false");
	this.oDisplay.title = textCal;
	this.oDisplay.onmouseout = hideCal;

	this.oImage = document.getElementById(sImgName);
	this.oImage.onclick = new Function("showCal(oCals[" + this.nID + "]); return false");	 
	this.oImage.title = textCal;
	this.setTime(stringToDate(sDate, bNoDefaultDate), true, sDate);

	if (!oCalDiv)
	{
		oCalDiv = document.createElement("DIV");
		oCalDiv.className = "calcontrol";
		oCalDiv.onmouseover = showCal;
		oCalDiv.onmouseout = hideCal;
		document.body.insertBefore(oCalDiv, document.body.childNodes[0]);
	}
}

function attachCalControl(sHiddenName, sDisplayName, sImgName, sDate, sMinDate, sMaxDate, fOnChange, bIsMandatory, bNoDefaultDate, bNoTimeComponent)
{
	var oCal = new CCal(sHiddenName, sDisplayName, sImgName, sDate, sMinDate, sMaxDate, fOnChange, bIsMandatory, bNoDefaultDate, bNoTimeComponent);
}
