/*
 * DateElement Object Definitions
 */

/* DateElement --
 *	DateElement initialization
 *
 *	formName	: the associated form name
 *	yearName	: the associated Input or Select name for displaying year
 *	monthName	: ths associated Input or Select name for displaying month
 *	dayName		: ths associated Input or Select name for displaying day
 */
function DateElement(formName, yearName, monthName, dayName)
{
	//Object ID
	this.id = null;

	//Associated form/elements info
	this.formPath	= "document." + formName;
	this.yearPath	= this.formPath + "." + yearName;
	this.monthPath	= this.formPath + "." + monthName;
	this.dayPath	= this.formPath + "." + dayName;

	this.yearElement	= eval(this.yearPath);
	this.monthElement	= eval(this.monthPath);
	this.dayElement		= eval(this.dayPath);

	//Default date (AD)
	this.defaultYear	= null;
	this.defaultMonth	= null;
	this.defaultDay		= null;

	//flag to identify MG Year or AD Year
	this.isAD	= true;

	//Upperbound date (AD)
	this.upperYear	= null;
	this.upperMonth	= null;
	this.upperDay	= null;

	//Lowerbound date (AD)
	this.lowerYear	= null;
	this.lowerMonth	= null;
	this.lowerDay	= null;

	//Year Offset, corresponding to the default year
	this.yearFromOffset = 0;	//yearFrom	= defaultYear - yearFromOffset
	this.yearToOffset = -2;		//yearTo	= defaultYear + yearToOffset

	//formatted date size
	this.yearSize	= 0;
	this.monthSize	= 0;
	this.daySize	= 0;

	//Call initialization method and return
	return this.init();
}

/*
 * init() method --
 *	method to initialize DateElement object
 */
DateElement.prototype.init = function()
{
	//set object id
	if (!window.dateElements)
	{ window.dateElements = new Array(); }

	this.id = window.dateElements.length;
	window.dateElements[window.dateElements.length] = this;

	//set events
	this.setEvents(this.getDefaultHandler(), true);

	//set default to current date
	this.setDefaultDate();
	return this;
}

/*
 * onDateChange() method --
 *	onDateChange event handler
 */
DateElement.prototype.onDateChange = function()
{
	this.populate();
	return true;
}

/*
 * setEvents() method --
 *	to setup the date elements' onchange event with the given or default handler
 *
 *	handler		: the handler function
 *	isAppend	: a boolean value to specify if appending the event handler(true)
 *	isReplace	: a boolean value to specify if replacing the event handler(true)
 */
DateElement.prototype.setEvents = function(handler, isAppend, isReplace)
{
	var sHandler = (handler)? handler.toString() : "";

	var sYearEvent = (this.yearElement.type.indexOf("text") >= 0)? "onblur" : "onchange";
	var sMonthEvent = (this.monthElement.type.indexOf("text") >= 0)? "onblur" : "onchange";
	var sDayEvent = (this.dayElement.type.indexOf("text") >= 0)? "onblur" : "onchange";

	addEvent(this.yearPath, sYearEvent, sHandler, isAppend, isReplace);
	addEvent(this.monthPath, sMonthEvent, sHandler, isAppend, isReplace);
	addEvent(this.dayPath, sDayEvent, sHandler, isAppend, isReplace);
}

/*
 * getDefaultHandler() method --
 *	to return the function object of the default event handler
 */
DateElement.prototype.getDefaultHandler = function()
{
	return new Function("return window.dateElements[" + this.id + "].onDateChange();");
}

/*
 * getDefaultYear() method --
 *	to return an integer of:
 *		1. the default year in AD or MG format if the 'isAD' is specified
 *		2. the default AD year if the object's 'isAD' filed is set to true
 *		3. the default MG year if the object's 'isAD' field is set to false
 *
 *	isAD: boolean value to specify AD(true) or MG(false) year
 */
DateElement.prototype.getDefaultYear = function(isAD)
{
	if (null == this.defaultYear)
	{ return null; }

	if (null == isAD)
	{ isAD = this.isAD; }

	return ((isAD)? this.defaultYear : this.defaultYear - 1911);
}

/*
 * getUpperYear() method --
 *	to return an integer of:
 *		1. the upper-bound year in AD or MG format if the 'isAD' is specified
 *		2. the upper-bound AD year if the object's 'isAD' filed is set to true
 *		3. the upper-bound MG year if the object's 'isAD' field is set to false
 *
 *	isAD: boolean value to specify AD(true) or MG(false) year
 */
DateElement.prototype.getUpperYear = function(isAD)
{
	if (null == this.upperYear)
	{ return null; }

	if (null == isAD)
	{ isAD = this.isAD; }

	return ((isAD)? this.upperYear : this.upperYear - 1911);
}

/*
 * getLowerYear() method --
 *	to return an integer of:
 *		1. the lower-bound year in AD or MG format if the 'isAD' is specified
 *		2. the lower-bound AD year if the object's 'isAD' filed is set to true
 *		3. the lower-bound MG year if the object's 'isAD' field is set to false
 *
 *	isAD: boolean value to specify AD(true) or MG(false) year
 */
DateElement.prototype.getLowerYear = function(isAD)
{
	if (null == this.lowerYear)
	{ return null; }

	if (null == isAD)
	{ isAD = this.isAD; }

	return ((isAD)? this.lowerYear : this.lowerYear - 1911);
}

/*
 * getYearSize() method --
 *	to return:
 *		1. the formatted year size if available and reasonable
 *		2. the default value, if useDefault is specified, while the year
 *		   size is not set or not reasonable.
 *		3. 0, otherwise
 *
 *	useDefault	: boolean flag to specify if the default value should be applied
 */
DateElement.prototype.getYearSize = function(useDefault)
{
	var iSize = this.yearSize;
	if (0 >= iSize)
	{
		if (useDefault)
		{
			if (this.isAD)
			{ iSize = 4; }
			else
			{ iSize = 3; }
		}
		else
		{ iSize = 0; }
	}

	return iSize;
}

/*
 * getMonthSize() method --
 *	to return:
 *		1. the formatted month size if available and reasonable
 *		2. the default value, if useDefault is specified, while the month
 *		   size is not set or not reasonable.
 *		3. 0, otherwise
 *
 *	useDefault	: boolean flag to specify if the default value should be applied
 */
DateElement.prototype.getMonthSize = function(useDefault)
{
	var iSize = this.monthSize;
	if (0 >= iSize)
	{
		if (useDefault)
		{ iSize = 2; }
		else
		{ iSize = 0; }
	}

	return iSize;
}

/*
 * getDaySize() method --
 *	to return:
 *		1. the formatted day size if available and reasonable
 *		2. the default value, if useDefault is specified, while the day
 *		   size is not set or not reasonable.
 *		3. 0, otherwise
 *
 *	useDefault	: boolean flag to specify if the default value should be applied
 */
DateElement.prototype.getDaySize = function(useDefault)
{
	var iSize = this.daySize;
	if (0 >= iSize)
	{
		if (useDefault)
		{ iSize = 2; }
		else
		{ iSize = 0; }
	}

	return iSize;
}

/*
 * getYear() method --
 *	to return an integer of:
 *		1. user input or selected year if available
 *		2. the default year otherwise
 *
 *	isAD: a boolean value indicating to return AD(true) or MG(false) year
 */
DateElement.prototype.getYear = function(isAD)
{
	var iYear = parseInt(getValue(this.yearElement, this.getDefaultYear(isAD)), 10);
	if ((null != isAD) && (!this.isAD == isAD))
	{
		if (isAD) //system default to MG year, but asking for AD year
		{
			iYear += 1911;
		}
		else //system default to AD year, but asking for MG year
		{
			iYear -= 1911;
		}
	}
	return iYear;
}

/*
 * getMonth() method --
 *	to return an integer of:
 *		1. user input or selected month if available
 *		2. the default month otherwise
 */
DateElement.prototype.getMonth = function()
{
	return parseInt(getValue(this.monthElement, this.defaultMonth), 10);
}

/*
 * getDay() method --
 *	to return an integer of:
 *		1. user input or selected day if available
 *		2. the default day otherwise
 */
DateElement.prototype.getDay = function()
{
	return parseInt(getValue(this.dayElement, this.defaultDay), 10);
}

/*
 * populate() method --
 *	to populate the date elements
 */
DateElement.prototype.populate = function()
{
	this.populateYear();
	this.populateMonth();
	this.populateDay();
}

/*
 * populateYear() method --
 *	to populate the year element
 */
DateElement.prototype.populateYear = function()
{
	var iDefault = this.getDefaultYear();
	var iBegin = iDefault - this.yearFromOffset;
	var iEnd = iDefault + this.yearToOffset;

	if (this.hasUpperBound())
	{
		var iUpperYear = this.getUpperYear();
		while (iBegin > iUpperYear)		{ --iBegin; }
		while (iEnd > iUpperYear)		{ --iEnd; }
	}

	if (this.hasLowerBound())
	{
		var iLowerYear = this.getLowerYear();
		while (iBegin < iLowerYear)		{ ++iBegin; }
		while (iEnd < iLowerYear)		{ ++iEnd; }
	}

	this.populateElement(this.yearElement, iBegin, iEnd, this.getYear(), this.getYearSize(true));
}

/*
 * populateMonth() method --
 *	to populate the month element
 */
DateElement.prototype.populateMonth = function()
{
	var iSelectedYear = this.getYear();

	var iBegin = 1;
	var iEnd = 12;

	if (this.hasUpperBound())
	{
		var iUpperYear = this.getUpperYear();

		if (iUpperYear == iSelectedYear)
		{
			iEnd = this.upperMonth;
		}
		else if (iUpperYear < iSelectedYear)
		{
			iBegin = 0;
			iEnd = 0;
		}
	}

	if (this.hasLowerBound())
	{
		var iLowerYear = this.getLowerYear();

		if (iLowerYear == iSelectedYear)
		{
			iBegin = this.lowerMonth;
		}
		else if (iLowerYear > iSelectedYear)
		{
			iBegin = 0;
			iEnd = 0;
		}
	}

	this.populateElement(this.monthElement, iBegin, iEnd, this.getMonth(), this.getMonthSize(true));
}

/*
 * populateDay() method --
 *	to populate the day element
 */
DateElement.prototype.populateDay = function()
{
	var iSelectedYear = this.getYear();
	var iSelectedMonth = this.getMonth();
	var iBegin = 1;
	var iEnd = getMaxPossibleDate(this.getYear(true), iSelectedMonth);

	if (this.hasUpperBound())
	{
		var iUpperYear = this.getUpperYear();
		var iUpperMonth = this.upperMonth;

		if ((iUpperYear == iSelectedYear) && (iUpperMonth == iSelectedMonth))
		{
			iEnd = this.upperDay;
		}
		else if ((iUpperYear < iSelectedYear) ||
				((iUpperYear == iSelectedYear) && (iUpperMonth < iSelectedMonth)))
		{
			iBegin = 0;
			iEnd = 0;
		}

	}

	if (this.hasLowerBound())
	{
		var iLowerYear = this.getLowerYear();
		var iLowerMonth = this.lowerMonth;

		if ((iLowerYear == iSelectedYear) && (iLowerMonth == iSelectedMonth))
		{
			iBegin = this.lowerDay;
		}
		else if ((iLowerYear > iSelectedYear) ||
				((iLowerYear == iSelectedYear) && (iLowerMonth > iSelectedMonth)))
		{
			iBegin = 0;
			iEnd = 0;
		}
	}

	this.populateElement(this.dayElement, iBegin, iEnd, this.getDay(), this.getDaySize(true));
}

/*
 * populateElement() method --
 *	to populate the date element
 *
 *	element	: a reference to a date element
 *	begin	: begin value (integer)
 *	end		: end value (integer)
 *	default	: default value (integer)
 *	size	: preferred formatted size (integer)
 */
DateElement.prototype.populateElement = function(element, begin, end, defaultDate, size)
{
	if (null == element)	{ return; }

	if (0 > begin)
	{ begin = 0; }

	if (0 > end)
	{ end = 0; }

	if (begin > end)
	{
		if (defaultDate > begin)
		{ defaultDate = begin; }
		else if (defaultDate < end)
		{ defaultDate = end; }
	}
	else //begin <= end
	{
		if (defaultDate < begin)
		{ defaultDate = begin; }
		else if (defaultDate > end)
		{ defaultDate = end; }
	}

	if (element.type.indexOf("select") >= 0)
	{
		var iNumOfEntries = (end < begin) ? begin-end+1 : end-begin+1;

		if ((0 >= begin) || (0 >= end))
		{ iNumOfEntries = 0; }

		while (element.length > iNumOfEntries)
		{
			element.options[element.length-1] = null;
		}

		for (var i=0; i<iNumOfEntries; ++i)
		{
			var iValue;
			var bSelected = false;

			if (begin > end)
			{ iValue = begin - i; }
			else
			{ iValue = begin + i; }

			if (iValue == defaultDate)
			{ bSelected = true; }

			var sValue = new String(iValue);

			while ((null != size) && (sValue.length < size))
			{ sValue = "0" + sValue; }

			if (null != element.options[i])
			{
				if (sValue != element.options[i].text)
				{
					element.options[i].text = sValue;
				}

				if (sValue != element.options[i].value)
				{
					element.options[i].value = sValue;
				}
			}
			else
			{
				element.options[i] = new Option(sValue, sValue);
			}

			if (bSelected)	{ element.options[i].selected = true; }
		} //end for
	}
	else //text input
	{
		var sValue = new String(defaultDate);
		while ((null != size) && (sValue.length < size))
		{ sValue = "0" + sValue; }

		element.value = sValue;
	}

}

/*
 * setDefaultDate() method --
 *	to set the default year, month, day as:
 *		1. the given values of year, month, and day
 *		2. the current date value, otherwise
 *
 *	year	: the number of the default year
 *	month	: the number of the default month (1 to 12)
 *	day		: the number of the default day (1 to 31)
 *	isAD	: a boolean value to identify AD(true) year or MG(false) year
 */
DateElement.prototype.setDefaultDate = function(year, month, day, isAD)
{
	var iYear = getInteger(year, getCurrentYear());
	var iMonth = getInteger(month, getCurrentMonth());
	var iDay = getInteger(day, getCurrentDay());

	if (null == isAD)	{ isAD = this.isAD; }

	if ((true == isAD) && (100 > iYear))
	{
		if (50 < iYear)
		{ iYear += 1900; }
		else if (50 >= iYear)
		{ iYear += 2000; }
	}
	else if (false == isAD)
	{
		iYear += 1911;
	}

	if (!isDateValid(iYear, iMonth, iDay))
	{
		 var closedDate = getClosedDateLessThan(iYear, iMonth, iDay);
		 iYear = closedDate.getFullYear();
		 iMonth = closedDate.getMonth() + 1;
		 iDay = closedDate.getDate();

	} //end while

	this.defaultYear = iYear;
	this.defaultMonth = iMonth;
	this.defaultDay = iDay;
}

/*
 * setUpperBound() method --
 *	to set the upper bound date
 *
 *	year	: the upper-bound number of year
 *	month	: the upper-bound number of month
 *	day		: the upper-bound number of day
 *	isAD	: a boolean value to identify AD(true) year or MG(false) year
 */
DateElement.prototype.setUpperBound = function(year, month, day, isAD)
{
	var iYear = getInteger(year, 9999);
	var iMonth = getInteger(month, 12);
	var iDay = getInteger(day, 31);

	if ((true == isAD) && (100 > iYear))
	{
		if (50 < iYear)
		{ iYear += 1900; }
		else if (50 >= iYear)
		{ iYear += 2000; }
	}
	else if (false == isAD)
	{
		iYear += 1911;
	}

	if (!isDateValid(iYear, iMonth, iDay))
	{
		 var closedDate = getClosedDateGreaterThan(iYear, iMonth, iDay);
		 iYear = closedDate.getFullYear();
		 iMonth = closedDate.getMonth() + 1;
		 iDay = closedDate.getDate();

	} //end while

	this.upperYear = iYear;
	this.upperMonth = iMonth;
	this.upperDay = iDay;
}

/*
 * setUpperBoundAsDefault() method --
 *	to set the upper bound date as the default date
 */
DateElement.prototype.setUpperBoundAsDefault = function()
{
	this.setUpperBound(this.getDefaultYear(), this.defaultMonth, this.defaultDay, this.isAD);
}

/*
 * setLowerBound() method --
 *	to set the lower bound date
 *
 *	year	: the lower-bound number of year
 *	month	: the lower-bound number of month
 *	day		: the lower-bound number of day
 *	isAD	: a boolean value to identify AD(true) year or MG(false) year
 */
DateElement.prototype.setLowerBound = function(year, month, day, isAD)
{
	var iYear = getInteger(year, 0);
	var iMonth = getInteger(month, 1);
	var iDay = getInteger(day, 1);

	if (null == isAD)	{ isAD = this.isAD; }

	if ((true == isAD) && (100 > iYear))
	{
		if (50 < iYear)
		{ iYear += 1900; }
		else if (50 >= iYear)
		{ iYear += 2000; }
	}
	else if (false == isAD)
	{
		iYear += 1911;
	}

	if (!isDateValid(iYear, iMonth, iDay))
	{
		 var closedDate = getClosedDateLessThan(iYear, iMonth, iDay);
		 iYear = closedDate.getFullYear();
		 iMonth = closedDate.getMonth() + 1;
		 iDay = closedDate.getDate();

	} //end while

	this.lowerYear = iYear;
	this.lowerMonth = iMonth;
	this.lowerDay = iDay;
}

/*
 * setLowerBoundAsDefault() method --
 *	to set the lower bound date as the default date
 */
DateElement.prototype.setLowerBoundAsDefault = function()
{
	this.setLowerBound(this.getDefaultYear(), this.defaultMonth, this.defaultDay, this.isAD);
}

/*
 * removeUpperBound() method --
 *	to remove the upper bound settings
 */
DateElement.prototype.removeUpperBound = function()
{
	this.upperYear = null;
	this.upperMonth = null;
	this.upperDay = null;
}

/*
 * removeLowerBound() method --
 *	to remove the lower bound settings
 */
DateElement.prototype.removeLowerBound = function()
{
	this.lowerYear = null;
	this.lowerMonth = null;
	this.lowerDay = null;
}

/*
 * hasUpperBound() method --
 *	to check if the upper bound date is set
 */
DateElement.prototype.hasUpperBound = function()
{
	return ((null != this.upperYear) &&
			(null != this.upperMonth) && (null != this.upperDay));
}

/*
 * hasLowerBound() method --
 *	to check if the lower bound date is set
 */
DateElement.prototype.hasLowerBound = function()
{
	return ((null != this.lowerYear) &&
			(null != this.lowerMonth) && (null != this.lowerDay));
}