/* ======================================================================= */
/* Class SmartCalendar Implementation Code - START                         */
/*                                                                         */
/* NOTE: the object definition code is at the bottom of this section.      */
/* ======================================================================= */

// Array of all the SmartCalendar objects instantiated within the page.
var SmartCalendars = [];
var SmartCalendarWidth	= 260;
var SmartCalendarHeight = 210;


// Define constance to define data being absolute.
// Example:  2002/10/28  for a date limit.
var SMART_CALENDAR_ABSOLUTE = 0;

// Define constance to define data being relative.
// Example:  60 for a date limit 60 days out from today.
var SMART_CALENDAR_RELATIVE = 1;


// Month constants.
var SMART_CALENDAR_JANUARY		= 0;
var SMART_CALENDAR_FEBRUARY		= 1;
var SMART_CALENDAR_MARCH		= 2;
var SMART_CALENDAR_APRIL		= 3;
var SMART_CALENDAR_MAY			= 4;
var SMART_CALENDAR_JUNE			= 5;
var SMART_CALENDAR_JULY			= 6;
var SMART_CALENDAR_AUGUST		= 7;
var SMART_CALENDAR_SEPTEMBER	= 8;
var SMART_CALENDAR_OCTOBER		= 9;
var SMART_CALENDAR_NOVEMBER		= 10;
var SMART_CALENDAR_DECEMBER		= 12;

var SMART_CALENDAR_MONTHS = ["January", "February", "March", "April", "May", "June", 
	"July", "August", "September", "October", "November", "December"];


// Day of the week constants.
var SMART_CALENDAR_SUNDAY		= 0;
var SMART_CALENDAR_MONDAY		= 1;
var SMART_CALENDAR_TUESDAY		= 2;
var SMART_CALENDAR_WEDNESDAY	= 3;
var SMART_CALENDAR_THURSDAY		= 4;
var SMART_CALENDAR_FRIDAY		= 5;
var SMART_CALENDAR_SATURDAY		= 6;

var SMART_CALENDAR_WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];


// -----------------------------------------------------------------------
// -----------------------------------------------------------------------


/**
 * Gets the user defined ID for this class instance.
 *
 * @return Returns the class ID that was specified by the user.
 */
function SmartCalendar_getID() {
	return this.id;
}


/**
 * Gets the date (result) form field reference.
 *
 * @return Returns a reference to the form (result) date field.
 */
function SmartCalendar_getDateFormField() {
	return this.dateFormField;
}


/**
 * Sets the date (result) form field reference.
 * This should be something like document.MyForm.searchDate.
 *
 * @param dateFormField Reference to the form (result) date field.
 */
function SmartCalendar_setDateFormField(pDateFormField) {
	if (pDateFormField == null) {
		this.error( "setDateFormFiel", "Received a null reference for the parameter \"dateFormField\".\n" );
		return;
	}
	
	if (typeof pDateFormField != "object") {
		this.error( "setDateFormFiel", "Received a value for the parameter \"dateFormField\" that is not of type Object." );
		return;
	}
	
	this.dateFormField = pDateFormField;
}



/**
 * Gets the year scrollable flag.  
 * This field identifies whether the calendar allows year scrolling 
 * or if it's restricted to the currently displayed year.
 *
 * @return	Returns TRUE if the calendar allows year scrolling.
 *			or FALSE if it is restricted to currently displayed year.
 */
function SmartCalendar_isYearScrollable() {
	return this.yearScrollable;
}


/**
 * Sets the year scrollable flag.  
 * This field identifies whether the calendar allows year scrolling 
 * or if it's restricted to the currently displayed year.
 *
 * @param yearScrollable TRUE if the calendar allows year scrolling.
 *			or FALSE if it is restricted to currently displayed year.
 */
function SmartCalendar_setYearScrollable(pYearScrollable) {
	if (pYearScrollable == null) {
		this.error( "setYearScrollable", "Received a null value for the parameter \"yearScrollable\"." );
		return;
	}
	
	if (typeof pYearScrollable != "boolean") {
		this.error( "setYearScrollable", "Received a value for the parameter \"yearScrollable\" that is not a boolean." );
		return;
	}
	
	this.yearScrollable = pYearScrollable
}


/**
 * Gets the month scrollable flag.  
 * This field identifies whether the calendar allows month scrolling 
 * or if it's restricted to the currently displayed month.
 *
 * @return	Returns TRUE if the calendar allows month scrolling.
 *			or FALSE if it is restricted to currently displayed month.
 */
function SmartCalendar_isMonthScrollable() {
	return this.monthScrollable;
}


/**
 * Sets the month scrollable flag.  
 * This field identifies whether the calendar allows month scrolling 
 * or if it's restricted to the currently displayed month.
 *
 * @param monthScrollable Returns TRUE if the calendar allows month scrolling.
 *			or FALSE if it is restricted to currently displayed month.
 */
function SmartCalendar_setMonthScrollable(pMonthScrollable) {
	if (pMonthScrollable == null) {
		this.error( "setMonthScrollable", "Received a null value for the parameter \"monthScrollable\"." );
		return;
	}
	
	if (typeof pMonthScrollable != "boolean") {
		this.error( "setMonthScrollable", "Received a value for the parameter \"monthScrollable\" that is not a boolean." );
		return;
	}
	
	this.monthScrollable = pMonthScrollable
}


/**
 * Gets the Date object for the lower date limit assign
 * to this SmartCalendar object.
 *
 * @return Returns the Date object matchin the lower date limit.
 */
function SmartCalendar_getLowerDateLimit() {
	return this.lowerDateLimit;
}


/**
 * Gets the Date object for the lower date limit assign to this SmartCalendar object.
 * Note: if using SMART_CALENDAR_ABSOLUTE, the lowerDateLimit parameter must be a Date object.
 * 		 if using SMART_CALENDAR_RELATIVE, the lowerDateLimit parameter must be number of days
 *		 to substract from the initial display date.  HENCE, make sure you have already set
 *		 the initial display date else today's date will be used.
 *
 * @param lowerDateLimit The Date object matching the lower date limit.
 * @param lowerDateLimitType  Either one of these constants: SMART_CALENDAR_ABSOLUTE or SMART_CALENDAR_RELATIVE.
 */
function SmartCalendar_setLowerDateLimit( pLowerDateLimit, pLowerDateLimitType ) {
	if (pLowerDateLimit == null) {
		this.error( "setLowerDateLimit", "Received a null value for the parameter \"lowerDateLimit\"." ); 
		return;
	}
	
	if (pLowerDateLimitType != SMART_CALENDAR_ABSOLUTE && pLowerDateLimitType != SMART_CALENDAR_RELATIVE) {
		this.error( "setLowerDateLimit", "Received a value for the parameter \"lowerDateLimitType\" that does not match the available constants' value." );
		return;
	}

	if (pLowerDateLimitType == SMART_CALENDAR_ABSOLUTE && typeof pLowerDateLimit != "object") {
		this.error( "setLowerDateLimit", "Received a value for the parameter \"lowerDateLimitType\", with type SMART_CALENDAR_ABSOLUTE, that is not a Date object." );
		return;
	}
	
	if (pLowerDateLimitType == SMART_CALENDAR_RELATIVE && typeof pLowerDateLimit != "number") {
		this.error( "setLowerDateLimit", "Received a value for the parameter \"lowerDateLimitType\", with type SMART_CALENDAR_RELATIVE, that is not a number." );
		return;
	}
	

	if (pLowerDateLimitType == SMART_CALENDAR_ABSOLUTE) {
		this.lowerDateLimit = pLowerDateLimit;
	} else if(pLowerDateLimitType == SMART_CALENDAR_RELATIVE) {
		// The date limit is to be set relative to the initial date by adding N days to it.
		// To do this we will set the Date (day of month) to be N + initialDate.getDate().
		// Hence if today is the 15th of the month and we want a 30 day limit then we
		// will set out date limit day of month to be 45.  JavaScript will take care to
		// convert 45 days to the appropriate number of year, month, days and adjust the date to it.
		this.lowerDateLimit = new Date( this.displayDate.valueOf() );
		this.lowerDateLimit.setDate( pLowerDateLimit + this.lowerDateLimit.getDate() );
	}
}


/**
 * Gets the Date object for the upper date limit assign
 * to this SmartCalendar object.
 *
 * @return Returns the Date object matchin the upper date limit.
 */
function SmartCalendar_getUpperDateLimit() {
	return this.upperDateLimit;
}


/**
 * Gets the Date object for the upper date limit assign to this SmartCalendar object.
 * Note: if using SMART_CALENDAR_ABSOLUTE, the upperDateLimit parameter must be a Date object.
 * 		 if using SMART_CALENDAR_RELATIVE, the upperDateLimit parameter must be number of days
 *		 to substract from the initial display date.  HENCE, make sure you have already set
 *		 the initial display date else today's date will be used.
 *
 * @param upperDateLimit The Date object matching the upper date limit.
 * @param upperDateLimitType  Either one of these constants: SMART_CALENDAR_ABSOLUTE or SMART_CALENDAR_RELATIVE.
 */
function SmartCalendar_setUpperDateLimit( pUpperDateLimit, pUpperDateLimitType ) {
	if (pUpperDateLimit == null) {
		this.error( "setUpperDateLimit", "Received a null value for the parameter \"upperDateLimit\"." ); 
		return;
	}
	
	if (pUpperDateLimitType != SMART_CALENDAR_ABSOLUTE && pUpperDateLimitType != SMART_CALENDAR_RELATIVE) {
		this.error( "setUpperDateLimit", "Received a value for the parameter \"upperDateLimitType\" that does not match the available constants' value." );
		return;
	}

	if (pUpperDateLimitType == SMART_CALENDAR_ABSOLUTE && typeof pUpperDateLimit != "object") {
		this.error( "setUpperDateLimit", "Received a value for the parameter \"upperDateLimitType\", with type SMART_CALENDAR_ABSOLUTE, that is not a Date object." );
		return;
	}
	
	if (pUpperDateLimitType == SMART_CALENDAR_RELATIVE && typeof pUpperDateLimit != "number") {
		this.error( "setUpperDateLimit", "Received a value for the parameter \"upperDateLimitType\", with type SMART_CALENDAR_RELATIVE, that is not a number." );
		return;
	}
	

	if (pUpperDateLimitType == SMART_CALENDAR_ABSOLUTE) {
		this.upperDateLimit = pUpperDateLimit;
	} else if(pUpperDateLimitType == SMART_CALENDAR_RELATIVE) {
		// The date limit is to be set relative to the initial date by adding N days to it.
		// To do this we will set the Date (day of month) to be N + initialDate.getDate().
		// Hence if today is the 15th of the month and we want a 30 day limit then we
		// will set out date limit day of month to be 45.  JavaScript will take care to
		// convert 45 days to the appropriate number of year, month, days and adjust the date to it.
		this.upperDateLimit = new Date( this.displayDate.valueOf() );
		this.upperDateLimit.setDate( pUpperDateLimit + this.upperDateLimit.getDate() );
	}
}


/**
 * Gets the calendar displayed date.
 *
 * @return Returns the calendar's displayed date value.
 */
function SmartCalendar_getDisplayDate() {
	return this.displayDate;
}


/**
 * Sets the calendar displayed date.
 *
 * @param displayDate The calendar displayed date value.
 */
function SmartCalendar_setDisplayDate(pDisplayDate) {
	if (pDisplayDate == null) {
		this.error( "setDisplayDate", "Received a null value for the parameter \"displayDate\"." );
		return;
	}
	
	if (typeof pDisplayDate != "object") {
		this.error( "setDisplayDate", "Received a value for the parameter \"displayDate\" that is not a Date object." );
		return;
	}
	
	this.displayDate = new Date( pDisplayDate.valueOf() );
}


/**
 * Gets the calendar initial displayed date.
 *
 * @return Returns the calendar's initial displayed date value.
 */
function SmartCalendar_getInitialDisplayDate() {
	return this.initialDisplayDate;
}


/**
 * Sets the calendar initial displayed date.
 *
 * @param initialDisplayDate The calendar initial displayed date value.
 */
function SmartCalendar_setInitialDisplayDate(pInitialDisplayDate) {
	if (pInitialDisplayDate == null) {
		this.error( "setInitialDisplayDate", "Received a null value for the parameter \"initialDisplayDate\"." );
		return;
	}
	
	if (typeof pInitialDisplayDate != "object") {
		this.error( "setInitialDisplayDate", "Received a value for the parameter \"initialDisplayDate\" that is not a Date object." );
		return;
	}
	
	this.initialDisplayDate = new Date( pInitialDisplayDate.valueOf() );
	this.displayDate = new Date( pInitialDisplayDate.valueOf() );
}


/**
 * Gets an array of all the selectable days.
 * Each array index should contain an integer constant such as SMART_CALENDAR_SATURDAY.
 *
 * @return Returns an array of all the days are selectable.  The values are number constants.
 */
function SmartCalendar_getSelectableDays() {
	return this.selectableDays;
}


/**
 * Sets the array of all selectable days.
 * Each array index entry should contain an integer constant such as SMART_CALENDAR_SATURDAY.
 *
 * @param selectableDays Array of all the day constants that are selectable.
 */
function SmartCalendar_setSelectableDays(pSelectableDays) {
	if (pSelectableDays == null) {
		this.error( "setSelectableDays", "Received a null value for the parameter \"selectableDays\"." );
		return;
	}
	
	if (typeof pSelectableDays != "object") {
		this.error( "setSelectableDays", "Received a value for the parameter \"selectableDays\" that is not an Array object." );
		return;
	}
	
	for (var i = 0; i < pSelectableDays.length; i++) {
		if (pSelectableDays[i] < SMART_CALENDAR_SUNDAY || pSelectableDays[i] > SMART_CALENDAR_SATURDAY) {
			this.error( "setSelectableDays", "Received a value out of range for the parameter \"selectableDays[" + i + "]\"." );
			return;
		}
	}
	
	
	this.selectableDays = pSelectableDays;
}


/**
 * Determines if the given day of week constant is found within 
 * the object's selectableDays array.
 *
 * @param	selectableDay The day of week to look for.  
 *			Use class constants such as SMART_CALENDAR_SATURDAY
 *
 * @return	Returns TRUE if it was located within the selectableDays array.
 *			FALSE if it was not located.
 */
function SmartCalendar_containsSelectableDay(pSelectableDay) {
	if (pSelectableDay == null) {
		this.error( "containsSelectableDay", "Received a null value for the parameter \"selectableDay\"." );
		return;
	}

	if (typeof pSelectableDay != "number") {
		this.error( "containsSelectableDay", "Received a value for the parameter \"selectableDay\" that is not a number." );
		return;
	}

	if (pSelectableDay < SMART_CALENDAR_SUNDAY || pSelectableDay > SMART_CALENDAR_SATURDAY) {
		this.error( "containsSelectableDay", "Received a value out of range for the parameter \"selectableDay\"." );
		return;
	}
	
	
	if (this.selectableDays == null) {
		return false;
	} else {
		for (var i = 0; i < this.selectableDays.length; i++) {
			if (this.selectableDays[i] == pSelectableDay) {
				return true;
			}
		}
		
		return false;
	}
}


/**
 * Gets the output date format string.
 * The string will contain format string tokens such as 
 * yy, yyyy, m, mm, d, and dd.  Each token will be seperated
 * by either a "-" or "/".  There can be no more than 3 tokens.
 *
 * @return Returns the output date format string.
 */
function SmartCalendar_getOutputDateFormat() {
	return this.outputDateFormat;
}


/**
 * Sets the output date format string.
 * The string will contain format string tokens such as 
 * yy, yyyy, m, mm, d, and dd.  Each token will be seperated
 * by either a "-" or "/".  There can be no more than 3 tokens.
 *
 * @param outputDateFormat The output date format string.
 */
function SmartCalendar_setOutputDateFormat(pOutputDateFormat) {
	var seperator = "/";
	var datePartTokens = ["yy", "yyyy", "m", "mm", "d", "dd"];


	// Extract the date format parts
	var dateParts = this.getOutputDateFormatParts( false, pOutputDateFormat );
	
	if (dateParts == null || dateParts.length <= 0) {
		this.error( "setOutputDateFormat", "Could not extract the various output date format parts." );
		return;
	}
	
	
	// Validate that each date part from the format string is valid.
	for (var i = 0; i < dateParts.length; i++) {
		var found = false;
		
		for (var j = 0; j < datePartTokens.length; j++) {
			if (dateParts[i] == datePartTokens[j]) {
				found = true;
				break;
			}
		}
		
		if (found == false) {
			this.error( "setOutputDateFormat", "The output date format token \"" + dateParts[i] + "\" does not match one of the valid tokens." );
			return;
		}
	}
	
	this.outputDateFormat = pOutputDateFormat;
}


/**
 * Gets the year navigable flag.  
 * This field identifies whether the calendar will display or not
 * the year navigation form.
 *
 * @return	Returns TRUE if the calendar will display year navigation form.
 *			or FALSE if it will not display it.
 */
function SmartCalendar_isYearNavigable() {
	return this.yearNavigable;
}


/**
 * Sets the year navigable flag.  
 * This field identifies whether the calendar will display or not
 * the year navigation form.
 *
 * @param yearNavigable TRUE if the calendar will display year navigation form.
 *			or FALSE if it will not display it.
 */
function SmartCalendar_setYearNavigable(pYearNavigable) {
	if (pYearNavigable == null) {
		this.error( "setYearNavigable", "Received a null value for the parameter \"yearNavigable\"." );
		return;
	}
	
	if (typeof pYearNavigable != "boolean") {
		this.error( "setYearNavigable", "Received a value for the parameter \"yearNavigable\" that is not a boolean." );
		return;
	}
	
	this.yearNavigable = pYearNavigable
}


/**
 * Gets the month navigable flag.  
 * This field identifies whether the calendar will display or not
 * the month navigation form.
 *
 * @return	Returns TRUE if the calendar will display month navigation form.
 *			or FALSE if it will not display it.
 */
function SmartCalendar_isMonthNavigable() {
	return this.monthNavigable;
}


/**
 * Sets the month navigable flag.  
 * This field identifies whether the calendar will display or not
 * the month navigation form.
 *
 * @param monthNavigable TRUE if the calendar will display month navigation form.
 *			or FALSE if it will not display it.
 */
function SmartCalendar_setMonthNavigable(pMonthNavigable) {
	if (pMonthNavigable == null) {
		this.error( "setMonthNavigable", "Received a null value for the parameter \"monthNavigable\"." );
		return;
	}
	
	if (typeof pMonthNavigable != "boolean") {
		this.error( "setMonthNavigable", "Received a value for the parameter \"monthNavigable\" that is not a boolean." );
		return;
	}
	
	this.monthNavigable = pMonthNavigable
}


/**
 * Gets the pre display function name.
 * The function is defined within the code of opener ofthe calendar. 
 * It serves to notify the opener when the calendar is about to be opened.
 * Allowing it, for example, to set the SmartCalendar object's initialDisplayDate
 * to the result of gathering date values from form select objects.
 *
 * The callback method should expect to receive 1 input parameter.
 * The parameter is the SmartCalendar reference.
 *
 * @return Returns the pre display function name
 */
function SmartCalendar_getPreDisplayFunctionName() {
	return this.preDisplayFunctionName;
}


/**
 * Sets the pre display function name.
 * The function is defined within the code of opener ofthe calendar. 
 * It serves to notify the opener when the calendar is about to be opened.
 * Allowing it, for example, to set the SmartCalendar object's initialDisplayDate
 * to the result of gathering date values from form select objects.
 *
 * The callback method should expect to receive 1 input parameter.
 * The parameter is the SmartCalendar reference.
 *
 * Note: If the parameter passed in is a Function object reference, it's source
 * will be parsed to extract only the function's name part.  We do this so that
 * we can invoke the method in a different manner than the usual Function.call()
 * which does NOT work in IE 5.0.  The work-around is to use eval(), and so we
 * need only the function's name.
 *
 * @param preDisplayFunctionName The post display function name or reference
 */
function SmartCalendar_setPreDisplayFunctionName(pPreDisplayFunctionName) {
	if (pPreDisplayFunctionName == null) {
		this.error( "setPreDisplayFunctionName", "Received a null value for the parameter \"preDisplayFunctionName\"." );
		return;
	}
	
	if (typeof pPreDisplayFunctionName != "string" && typeof pPreDisplayFunctionName != "function") {
		this.error( "setPreDisplayFunctionName", "Received a value for the parameter \"preDisplayFunctionName\" that is not a string nor function." );
		return;
	}
	
	
	if (typeof pPreDisplayFunctionName == "string") {
		this.preDisplayFunctionName = pPreDisplayFunctionName;
	} else if (typeof pPreDisplayFunctionName == "function") {
		this.preDisplayFunctionName = this.getFunctionName( pPreDisplayFunctionName );
	}	
}


/**
 * Gets the post display function name.
 * The function is defined within the code of opener ofthe calendar. 
 * It serves to notify the opener when the user has selected a date.  
 * Allowing it, for example, to parse the date and set the value within 
 * select form field.
 *
 * The callback method should expect to receive 2 input parameter.
 * The first parameter is the SmartCalendar reference.
 * The second parameter will be a Date object matching the user's selection.
 *
 * @return Returns the post display function name
 */
function SmartCalendar_getPostDisplayFunctionName() {
	return this.postDisplayFunctionName;
}


/**
 * Sets the post display function name.
 * The function is defined within the code of opener ofthe calendar. 
 * It serves to notify the opener when the user has selected a date.  
 * Allowing it, for example, to parse the date and set the value within 
 * select form field.
 *
 * The callback method should expect to receive 2 input parameter.
 * The first parameter is the SmartCalendar reference.
 * The second parameter will be a Date object matching the user's selection.
 *
 * Note: If the parameter passed in is a Function object reference, it's source
 * will be parsed to extract only the function's name part.  We do this so that
 * we can invoke the method in a different manner than the usual Function.call()
 * which does NOT work in IE 5.0.  The work-around is to use eval(), and so we
 * need only the function's name.
 *
 * @param postDisplayFunctionName The post display function name or reference
 */
function SmartCalendar_setPostDisplayFunctionName(pPostDisplayFunctionName) {
	if (pPostDisplayFunctionName == null) {
		this.error( "setPostDisplayFunctionName", "Received a null value for the parameter \"postDisplayFunctionName\"." );
		return;
	}
	
	if (typeof pPostDisplayFunctionName != "string" && typeof pPostDisplayFunctionName != "function") {
		this.error( "setPostDisplayFunctionName", "Received a value for the parameter \"postDisplayFunctionName\" that is not a string nor function." );
		return;
	}
	
	
	if (typeof pPostDisplayFunctionName == "string") {
		this.postDisplayFunctionName = pPostDisplayFunctionName;
	} else if (typeof pPostDisplayFunctionName == "function") {
		this.postDisplayFunctionName = this.getFunctionName( pPostDisplayFunctionName );
	}	
}


/**
 * Gets the function name for a given function object reference.
 *
 * @param functionReference The Function object reference
 *
 * @return Returns the function name.
 */
function SmartCalendar_getFunctionName( pFunctionReference ) {
	var code			= pFunctionReference.toString();
	var functionName	= null;
	var startPos		= -1;
	var endPos			= -1;

	// Go to the index after the word "function".
	var startPos = code.indexOf("function");
	if (startPos == -1) {
		return null;
	}
	startPos = startPos + 8;


	// Find the index where the "(" is located
	endPos = code.indexOf( "(", startPos );
	if (endPos == -1) {
		return null;
	}
	
	functionName = code.substring( startPos, endPos );
	if (functionName != null) {
		var re = new RegExp( "\\s*", "g" );
		functionName = functionName.replace( re, "" );
	}
	
	return functionName;
}


/**
 * Gets the window title text.
 *
 * @return Returns the window title.
 */
function SmartCalendar_getWindowTitle() {
	return this.windowTitle;
}


/**
 * Gets the window title text.
 *
 * @param windowTitle The window title.
 */
function SmartCalendar_setWindowTitle(pWindowTitle) {
	if (pWindowTitle == null) {
		this.error( "setWindowTitle", "Received a null value for the parameter \"windowTitle\"." );
		return;
	}
	
	if (typeof pWindowTitle != "string") {
		this.error( "setWindowTitle", "Received a value for the parameter \"windowTitle\" that is not a string." );
		return;
	}
	
	this.windowTitle = pWindowTitle
}


/**
 * Displays the Smart Calendar window centered.
 *
 * @param windowSettings  The window settings to pass along to window.open().
 */
function SmartCalendar_displayCentered(pWindowSettings, pSkipPreDisplayCallback, pUrlContext) {
	var x = 0;
	var y = 0;

	// Since we can't show the legend as a pull-down (hidden) tab in Netscape 4.7
	// we will show it as a static table (no DHTML involved). So we need to resize window in that case.
	if (is_ie == true || (is_nav == true && parseInt(navigator.appVersion) >= 5)) {
		y = (screen.height - SmartCalendarHeight) / 2;
	} else {	
		y = (screen.height - (SmartCalendarHeight + 100)) / 2;
	}
	x = (screen.width - SmartCalendarWidth) / 2;
			
	this.display( x, y, pWindowSettings, pSkipPreDisplayCallback, pUrlContext );
}


/**
 * Displays the Smart Calendar window at the given location.
 *
 * @param windowLocationX The x (horizontal) position of the calendar window.
 * @param windowLocationY The y (vertical) position of the calendar window.
 * @param windowSettings The window settings to give when invoking window.open().
 */
function SmartCalendar_display(pWindowLocationX, pWindowLocationY, pWindowSettings, pSkipPreDisplayCallback, pUrlContext) {

	if (pWindowLocationX == null) {
		pWindowLocationX = 0;
	}
	
	if (pWindowLocationY == null) {
		pWindowLocationY = 0;
	}
	
	if (pWindowSettings == null || typeof pWindowSettings != "string") {
		pWindowSettings = "dependent,alwaysRaised";
	}

	
	if (typeof pWindowLocationX != "number") {
		this.error( "display", "Received a value for the parameter \"windowLocationX\" that is not a number." );
		return;
	}
	
	if (typeof pWindowLocationY != "number") {
		this.error( "display", "Received a value for the parameter \"windowLocationY\" that is not a number." );
		return;
	}
	
	if (pSkipPreDisplayCallback == null || typeof pSkipPreDisplayCallback != "boolean") {
		pSkipPreDisplayCallback = false;
	}


	// To avoid calling the pre display function when navigating in the calendar,
	// check that the flag is not true.
	if (this.preDisplayFunctionName != null && pSkipPreDisplayCallback == false) {
	
		eval( this.preDisplayFunctionName + "(this)" );
		
	// Set the initial display date if there's no pre display function and skip flag is false
	// as it would be when clicking the icon to open calendar.  That way multiple click to the icon
	// (without reloading the page) will always start the calendar from the same initial display date.
	} else if (this.preDisplayFunctionName == null && pSkipPreDisplayCallback == false) {
	
		this.setInitialDisplayDate( ((this.getInitialDisplayDate() == null) ? new Date() : this.getInitialDisplayDate()) );
		
	}
	

	// For some reason, if you open calendar window 1 and then open calendar window 2
	// (you have 2 different calendar window at this point) and click the icon that open window 1
	// to have it open a new window, it would give and error.  To remedy this, we will close it.
	// BUT we have another problem... window would not stay visible when navigating from inside 
	// the calendar window (don't know why) plus we don't want to close it needlessly, so only do
	// so if the skip pre display callback flag is false.  Navigating from calendar sends true.	
	if (this.windowReference != null && pSkipPreDisplayCallback == false) {
		if (this.windowReference.closed == false) {
			this.windowReference.close();
		}
	}

	

	
	// Set the window location... needed so that the calendar.jsp can reopen itself when navigating months/years
	this.windowLocationX	= pWindowLocationX;
	this.windowLocationY	= pWindowLocationY;
	this.windowSettings		= pWindowSettings;


	// Since we can't show the legend as a pull-down (hidden) tab in Netscape 4.7
	// we will show it as a static table (no DHTML involved). So we need to resize window in that case.
	var width = 0;
	var height = 0;
	if (is_nav == true && is_nav7up == false) {
		height = SmartCalendarHeight + 100;
	} else {
		height = SmartCalendarHeight;
	}
	width = SmartCalendarWidth;

	if (pUrlContext != null) {
		pUrlContext = trim(pUrlContext);
		
		if (pUrlContext.length > 0) {
			if (pUrlContext.charAt(0) != "/") {
				pUrlContext = "/" + pUrlContext;
			}
			
			if (pUrlContext.charAt(pUrlContext.length - 1) != "/") {
				pUrlContext = pUrlContext + "/";
			}
		}
	} else {
		pUrlContext = "";
	}

	//alert( "opening w/ settings: " + pWindowSettings );
	this.windowReference = window.open(	pUrlContext + "smartcalendar.jsp?calendarID=" + this.SmartCalendarsID, 
										"SmartCalendar_" + this.SmartCalendarsID,
										"width=" + width + 
										",height=" + height + 
										",top=" + pWindowLocationY +
										",left=" + pWindowLocationX +
										"," + pWindowSettings );

	if (this.windowReference != null) {
		//alert( this.windowReference.name );
		this.windowReference.opener = window;
		this.windowReference.focus();
	} else {
	  alert(popupBlockWarningMessage);
	}
}


function SmartCalendar_getWindowLocationX() {
	return this.windowLocationX;
}


function SmartCalendar_getWindowLocationY() {
	return this.windowLocationY;
}


function SmartCalendar_getWindowSettings() {
	return this.windowSettings;
}


function SmartCalendar_getOutputDateFormatParts(pFullDetail, pOutputDateFormat) {
	var seperator = "/";
	var datePartTokens = ["yy", "yyyy", "m", "mm", "d", "dd"];


	// IF pFullDetail == true then an array with 2 elements will be create.
	//	[0] will contain the array of outputDateFormat parts
	//	[1] will contain the outputDateFormat seperator char found
	//
	// IF pFullDetail == false then only the array of outputDateFormat parts is returned
	if (pFullDetail == null) {
		pFullDetail = false;
	}
	
	if (pOutputDateFormat == null || pOutputDateFormat.length == 0) {
		pOutputDateFormat = this.getOutputDateFormat();
	} 
	
	if (pOutputDateFormat == null || pOutputDateFormat.length == 0) {
		this.error( "getOutputDateFormatParts", "Could not find the outputFormatDate string." );
		return null;
	}
		
	
	// Extract the date format parts
	var outputDateFormatParts = pOutputDateFormat.split( seperator );	

	// If the string isn't seperated by "/" then try "-"
	if (outputDateFormatParts.length <= 1) {
		seperator = "-";
		outputDateFormatParts = pOutputDateFormat.split( seperator );

		// It wasn't using "-" either then return error
		if (outputDateFormatParts.length <= 1) {
			this.error( "getOutputDateFormatParts", "The output date format string did not contain the standard \"-\" or \"\\\" date part seperator." );
			return null;
		}
	}
	
	
	// Do not continue if they provided more than 3 tokens
	if (outputDateFormatParts.length > 3) {
		this.error( "getOutputDateFormatParts", "More than 3 output date format string tokens were found when using serpator \"" + seperator + "\"." );
		return null;
	}


	if (pFullDetail == false) {	
		return outputDateFormatParts;
	} else {
		var fullDetailArray = new Array(2);
		fullDetailArray[0] = outputDateFormatParts;
		fullDetailArray[1] = seperator;
	
		return fullDetailArray;
	}
}


function SmartCalendar_getFormFieldDate( pYearDigitBase ) {
	var field = this.getDateFormField();
	var dateValue = field.value;
	var returnDate = new Date();
	var outputFormatParts = this.getOutputDateFormatParts( true, null );
	
	if (pYearDigitBase == null) {
		pYearDigitBase = 2000;
	}
	
	
	if (dateValue == null || dateValue.length == 0) {
		return null;
	} else if( outputFormatParts.length != 2) {
		this.err( "getFormFieldDate", "Could not retrieve full detail on the outputDateFormat field - did not contain 2 element array." );
		return null;
	}
	

	var dateParts = dateValue.split( outputFormatParts[1] );
	if (dateParts == null) {
		this.error( "getFormFieldDate", "Could not retrieve the date form field value parts." );
		return null;
	} else if (dateParts.length != outputFormatParts[0].length) {
		this.error ("getFormFieldDate", "The dateParts array length did not match that of the outputDateFormat parts array length." );
		return null;	
	}


	var temp = 0;	
	var maskParts = outputFormatParts[0];
	for (var i = 0; i < maskParts.length; i++) {
		
		if (maskParts[i] == "yy" || maskParts[i] == "yyyy") {
			
			temp = new Number( dateParts[i] );
			
			if (maskParts[i] == "yy" ) {
				temp = temp + pYearDigitBase;
			}
			
			// added by sdawson
			// if the date format is yyyy format but the user entered yy format,
			// add the year digit base
			if (maskParts[i] == "yyyy") {
				if (temp < 100) {
					temp = temp + pYearDigitBase;
				}
			}
			
			returnDate.setYear( temp );			
			
		} else if (maskParts[i] == "m" || maskParts[i] == "mm") {
							
			temp = new Number( dateParts[i] );
			
			// Months are 0-based :  January = 0, December = 11
			returnDate.setMonth( temp - 1 );
								
		} else if (maskParts[i] == "d" || maskParts[i] == "dd") {

			temp = new Number( dateParts[i] );
			returnDate.setDate( temp );
		}
	}
	
	return returnDate;
}


// -----------------------------------------------------------------------
// -----------------------------------------------------------------------


/**
 * Protectec method to display errors that occured while executing.
 * The text that will show up will be something like this:
 *
 *   SmartCalendar Error:  Constructor:  this is my message.
 *
 * @param callerName The name of the function that raised the error.
 * @param message The error message to be displayed.
 */
function SmartCalendar_error(callerName, message) {
	alert( "SmartCalendar" + ((this.id != null) ? "(" + this.id + ")" : "") + "  Error:  " + callerName + "():  " + message );
	window.status = "Encountered JavaScript errors while executing SmartCalendar.";
}


/**
 * Protectec method to display errors that occured while executing.
 * The text that will show up will be something like this:
 *
 *   SmartCalendar Warning:  Constructor:  this is my message.
 *
 * @param callerName The name of the function that raised the warning.
 * @param message The warning message to be displayed.
 */
function SmartCalendar_warn(message) {
	alert( "SmartCalendar" + ((this.id != null) ? "(" + this.id + ")" : "") + "  Warning:  " + callerName + "():  " + message );
}

 
/**
 * Display a text representation of the SmartCalendar class.
 *
 * @return String Text representation of the SmartCalendar object.
 */
function SmartCalendar_toString() {
	var sb = "";
	
	sb = sb + "SmartCalendar[";
	sb = sb + "id=" + this.id;
	sb = sb + ", SmartCalendarsID=" + this.SmartCalendarsID;
	sb = sb + ", dateFormField=" + this.dateFormField.form.name + "." + this.dateFormField.name;
	sb = sb + ", monthScrollable=" + this.yearScrollable;
	sb = sb + ", yearScrollable=" + this.monthScrollable;
	sb = sb + ", initialDisplayDate=" + this.initialDisplayDate;
	sb = sb + ", displayDate=" + this.displayDate;
	sb = sb + ", lowerDateLimit=" + this.lowerDateLimit;
	sb = sb + ", upperDateLimit=" + this.upperDateLimit;
	sb = sb + ", selectableDays={" + this.selectableDays + "}";
	sb = sb + ", outputDateFromat=" + this.outputDateFormat;
	sb = sb + ", yearNavigable=" + this.yearNavigable;
	sb = sb + ", monthNavigable=" + this.monthNavigable;
	sb = sb + ", preDisplayFunctionName=" + this.preDisplayFunctionName;
	sb = sb + ", postDisplayFunctionName=" + this.postDisplayFunctionName;
	sb = sb + ", windowTitle=" + this.windowTitle;
	sb = sb + "]"
	
	return sb;
}




/**
 * This class contains all pertaining information for a SmartCalendar
 * along with method mappings that are to be exposed to the smartcalendar.jsp code..
 *
 * @param ID The SmartCalendar object (user assigned) ID.
 * @param dateFormField  The result date form field reference.
 * @param initialDisplayDate The date to be initially displayed in the calendar.
 *
 * @return	Returns an instance of the SmartCalendar if all is OK.
 *			If an error is encountered, it will return null.
 */
function SmartCalendar( pID, pDateFormField, pInitialDisplayDate ) {

	//////////////////////////////////////////////////////////////////////
	// Define class methods.
	//
	// Note that the method code MUST be defined before this object's
	// constructor for it to be visible.
	
	this.getID							= SmartCalendar_getID;
	
	this.getDateFormField				= SmartCalendar_getDateFormField;
	this.setDateFormField				= SmartCalendar_setDateFormField;
	
	this.isYearScrollable				= SmartCalendar_isYearScrollable;
	this.setYearScrollable				= SmartCalendar_setYearScrollable;
	
	this.isMonthScrollable				= SmartCalendar_isMonthScrollable;
	this.setMonthScrollable				= SmartCalendar_setMonthScrollable;
	
	this.getLowerDateLimit				= SmartCalendar_getLowerDateLimit;
	this.setLowerDateLimit				= SmartCalendar_setLowerDateLimit;
	
	this.getUpperDateLimit				= SmartCalendar_getUpperDateLimit;
	this.setUpperDateLimit				= SmartCalendar_setUpperDateLimit;
	
	this.getDisplayDate					= SmartCalendar_getDisplayDate;
	this.setDisplayDate					= SmartCalendar_setDisplayDate;
	
	this.getInitialDisplayDate			= SmartCalendar_getInitialDisplayDate;
	this.setInitialDisplayDate			= SmartCalendar_setInitialDisplayDate;
	
	this.getSelectableDays				= SmartCalendar_getSelectableDays;
	this.setSelectableDays				= SmartCalendar_setSelectableDays;
	this.containsSelectableDay			= SmartCalendar_containsSelectableDay;
	
	this.getOutputDateFormat			= SmartCalendar_getOutputDateFormat;
	this.setOutputDateFormat			= SmartCalendar_setOutputDateFormat;
	
	this.isYearNavigable				= SmartCalendar_isYearNavigable;
	this.setYearNavigable				= SmartCalendar_setYearNavigable;
	
	this.isMonthNavigable				= SmartCalendar_isMonthNavigable;
	this.setMonthNavigable				= SmartCalendar_setMonthNavigable;
	
	this.getPreDisplayFunctionName		= SmartCalendar_getPreDisplayFunctionName;
	this.setPreDisplayFunctionName		= SmartCalendar_setPreDisplayFunctionName;
	
	this.getPostDisplayFunctionName		= SmartCalendar_getPostDisplayFunctionName;
	this.setPostDisplayFunctionName		= SmartCalendar_setPostDisplayFunctionName;
	this.getFunctionName				= SmartCalendar_getFunctionName;
	
	this.getWindowTitle					= SmartCalendar_getWindowTitle;
	this.setWindowTitle					= SmartCalendar_setWindowTitle;
	
	this.displayCentered				= SmartCalendar_displayCentered;
	this.display						= SmartCalendar_display;

	this.getWindowLocationX				= SmartCalendar_getWindowLocationX;
	this.getWindowLocationY				= SmartCalendar_getWindowLocationY;
	this.getWindowSettings				= SmartCalendar_getWindowSettings;
	
	this.getOutputDateFormatParts		= SmartCalendar_getOutputDateFormatParts;
	this.getFormFieldDate				= SmartCalendar_getFormFieldDate;	
	
	this.error							= SmartCalendar_error;
	this.warn							= SmartCalendar_warn;
	this.toString						= SmartCalendar_toString;
  


	//////////////////////////////////////////////////////////////////////
	// Declare the class fields
	
	this.dateFormField					= null;
	this.id								= null;
	this.displayDate					= null;
	this.initialDisplayDate				= null;
	this.yearScrollable					= null;
	this.monthScrollable				= null;
	this.lowerDateLimit					= null;
	this.upperDateLimit					= null;
	this.selectableDays					= null;
	this.outputDateFormat				= null;
	this.yearNavigable					= null;
	this.monthNavigable					= null;
	this.preDisplayFunctionName			= null;
	this.postDisplayFunctionName		= null;
	this.windowTitle					= null;
	this.windowReference				= null;
	this.windowLocationX				= null;
	this.windowLocationY				= null;
	this.windowSettings					= null;


	//////////////////////////////////////////////////////////////////////
	// Validate that the parameters we got are good for use.

	if (pID == null) {
		this.error( "Contructor", "The parameter \"id\" may not be null." );
		return null;
	}
	
	if (typeof pID != "number") {
		this.error( "Contructor", "The parameter \"id\" must be a number type value." );
		return null;
	}
	
	
	// Set the user object id now so that this.error() can show that id in its messages.
	this.id = pID;
	
	
	if (pDateFormField == null) {
		this.error( "Contructor", "The parameter \"dateFormField\" may not be null." );
		return null;
	}
	
	if (typeof pDateFormField != "object") {
		this.error( "Contructor", "The parameter \"dateFormField\" must be an Object reference." );
		return null;
	}
	
	if (pInitialDisplayDate != null && pInitialDisplayDate != "object") {
		this.error( "Contructor", "The parameter \"initialDisplayDate\" must be a Date object." );
		return null;
	}
	
	
	
	//////////////////////////////////////////////////////////////////////
	// Initialize the class field values
	
	this.dateFormField					= pDateFormField;
	this.initialDisplayDate				= (pInitialDisplayDate != null) ? pInitialDisplayDate : new Date();
	this.displayDate					= new Date( this.initialDisplayDate.valueOf() );
	this.yearScrollable					= true;
	this.monthScrollable				= true;
	this.lowerDateLimit					= null;
	this.upperDateLimit					= null;
	this.selectableDays					= null;
	this.outputDateFormat				= "yyyy/mm/dd";
	this.yearNavigable					= true;
	this.monthNavigable					= true;
	this.preDisplayFunctionName			= null;
	this.postDisplayFunctionName		= null;
	this.windowTitle					= "Smart Calendar";
	this.windowLocationX				= 0;
	this.windowLocationY				= 0;
	this.windowSettings					= "";


	
	//////////////////////////////////////////////////////////////////////
	// Put the calendar in the page list of SmartCalendar so that it may
	// be accessed by the smartcalendar.html page.
	
	this.SmartCalendarsID = SmartCalendars.length;                            
	SmartCalendars[this.SmartCalendarsID] = this;

}


/* ======================================================================= */
/* Class SmartCalendar Implementation Code - END                           */
/* ======================================================================= */