Click here to Skip to main content
15,885,366 members
Articles / Web Development / ASP.NET

Using the ASP.NET Calendar Control and Yahoo.UI.Calendar in ASP.NET

Rate me:
Please Sign up or sign in to vote.
4.73/5 (28 votes)
1 Mar 2007CPOL5 min read 385.2K   2.7K   89  
Step by step guide for using the ASP.NET Calendar control or Yahoo.UI.Calendar in ASP.NET.
/*
Copyright (c) 2006, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
Version 0.11.3
*/

/**
* <p>YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility
* used for adding, subtracting, and comparing dates.</p>
*/
YAHOO.widget.DateMath = new function() {

	/**
	* Constant field representing Day
	* @type String
	*/
	this.DAY = "D";

	/**
	* Constant field representing Week
	* @type String
	*/
	this.WEEK = "W";

	/**
	* Constant field representing Year
	* @type String
	*/
	this.YEAR = "Y";

	/**
	* Constant field representing Month
	* @type String
	*/
	this.MONTH = "M";

	/**
	* Constant field representing one day, in milliseconds
	* @type Integer
	*/
	this.ONE_DAY_MS = 1000*60*60*24;

	/**
	* Adds the specified amount of time to the this instance.
	* @param {Date} date	The JavaScript Date object to perform addition on
	* @param {string} field	The this field constant to be used for performing addition.
	* @param {Integer} amount	The number of units (measured in the field constant) to add to the date.
	*/
	this.add = function(date, field, amount) {
		var d = new Date(date.getTime());
		switch (field) {
			case this.MONTH:
				var newMonth = date.getMonth() + amount;
				var years = 0;


				if (newMonth < 0) {
					while (newMonth < 0) {
						newMonth += 12;
						years -= 1;
					}
				} else if (newMonth > 11) {
					while (newMonth > 11) {
						newMonth -= 12;
						years += 1;
					}
				}
				
				d.setMonth(newMonth);
				d.setFullYear(date.getFullYear() + years);
				break;
			case this.DAY:
				d.setDate(date.getDate() + amount);
				break;
			case this.YEAR:
				d.setFullYear(date.getFullYear() + amount);
				break;
			case this.WEEK:
				d.setDate(date.getDate() + (amount * 7));
				break;
		}
		return d;
	};

	/**
	* Subtracts the specified amount of time from the this instance.
	* @param {Date} date	The JavaScript Date object to perform subtraction on
	* @param {Integer} field	The this field constant to be used for performing subtraction.
	* @param {Integer} amount	The number of units (measured in the field constant) to subtract from the date.
	*/
	this.subtract = function(date, field, amount) {
		return this.add(date, field, (amount*-1));
	};

	/**
	* Determines whether a given date is before another date on the calendar.
	* @param {Date} date		The Date object to compare with the compare argument
	* @param {Date} compareTo	The Date object to use for the comparison
	* @return {Boolean} true if the date occurs before the compared date; false if not.
	*/
	this.before = function(date, compareTo) {
		var ms = compareTo.getTime();
		if (date.getTime() < ms) {
			return true;
		} else {
			return false;
		}
	};

	/**
	* Determines whether a given date is after another date on the calendar.
	* @param {Date} date		The Date object to compare with the compare argument
	* @param {Date} compareTo	The Date object to use for the comparison
	* @return {Boolean} true if the date occurs after the compared date; false if not.
	*/
	this.after = function(date, compareTo) {
		var ms = compareTo.getTime();
		if (date.getTime() > ms) {
			return true;
		} else {
			return false;
		}
	};

	/**
	* Determines whether a given date is between two other dates on the calendar.
	* @param {Date} date		The date to check for
	* @param {Date} dateBegin	The start of the range
	* @param {Date} dateEnd		The end of the range
	* @return {Boolean} true if the date occurs between the compared dates; false if not.
	*/
	this.between = function(date, dateBegin, dateEnd) {
		if (this.after(date, dateBegin) && this.before(date, dateEnd)) {
			return true;
		} else {
			return false;
		}
	};
	
	/**
	* Retrieves a JavaScript Date object representing January 1 of any given year.
	* @param {Integer} calendarYear		The calendar year for which to retrieve January 1
	* @return {Date}	January 1 of the calendar year specified.
	*/
	this.getJan1 = function(calendarYear) {
		return new Date(calendarYear,0,1); 
	};

	/**
	* Calculates the number of days the specified date is from January 1 of the specified calendar year.
	* Passing January 1 to this function would return an offset value of zero.
	* @param {Date}	date	The JavaScript date for which to find the offset
	* @param {Integer} calendarYear	The calendar year to use for determining the offset
	* @return {Integer}	The number of days since January 1 of the given year
	*/
	this.getDayOffset = function(date, calendarYear) {
		var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1.
		
		// Find the number of days the passed in date is away from the calendar year start
		var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS);
		return dayOffset;
	};

	/**
	* Calculates the week number for the given date. This function assumes that week 1 is the
	* week in which January 1 appears, regardless of whether the week consists of a full 7 days.
	* The calendar year can be specified to help find what a the week number would be for a given
	* date if the date overlaps years. For instance, a week may be considered week 1 of 2005, or
	* week 53 of 2004. Specifying the optional calendarYear allows one to make this distinction
	* easily.
	* @param {Date}	date	The JavaScript date for which to find the week number
	* @param {Integer} calendarYear	OPTIONAL - The calendar year to use for determining the week number. Default is
	*											the calendar year of parameter "date".
	* @param {Integer} weekStartsOn	OPTIONAL - The integer (0-6) representing which day a week begins on. Default is 0 (for Sunday).
	* @return {Integer}	The week number of the given date.
	*/
	this.getWeekNumber = function(date, calendarYear, weekStartsOn) {
		date.setHours(12,0,0,0);

		if (! weekStartsOn) {
			weekStartsOn = 0;
		}
		if (! calendarYear) {
			calendarYear = date.getFullYear();
		}
		
		var weekNum = -1;
		
		var jan1 = this.getJan1(calendarYear);

		var jan1Offset = jan1.getDay() - weekStartsOn;
		var jan1DayOfWeek = (jan1Offset >= 0 ? jan1Offset : (7 + jan1Offset));

		var endOfWeek1 = this.add(jan1, this.DAY, (6 - jan1DayOfWeek));
		endOfWeek1.setHours(23,59,59,999);

		var month = date.getMonth();
		var day = date.getDate();
		var year = date.getFullYear();
		
		var dayOffset = this.getDayOffset(date, calendarYear); // Days since Jan 1, Calendar Year
			
		if (dayOffset < 0 || this.before(date, endOfWeek1)) {
			weekNum = 1;
		} else {
			weekNum = 2;
			var weekBegin = new Date(endOfWeek1.getTime() + 1);
			var weekEnd = this.add(weekBegin, this.WEEK, 1);

			while (! this.between(date, weekBegin, weekEnd)) {
				weekBegin = this.add(weekBegin, this.WEEK, 1);
				weekEnd = this.add(weekEnd, this.WEEK, 1);
				weekNum += 1;
			}
		}
		
		return weekNum;
	};

	/**
	* Determines if a given week overlaps two different years.
	* @param {Date}	weekBeginDate	The JavaScript Date representing the first day of the week.
	* @return {Boolean}	true if the date overlaps two different years.
	*/
	this.isYearOverlapWeek = function(weekBeginDate) {
		var overlaps = false;
		var nextWeek = this.add(weekBeginDate, this.DAY, 6);
		if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) {
			overlaps = true;
		}
		return overlaps;
	};

	/**
	* Determines if a given week overlaps two different months.
	* @param {Date}	weekBeginDate	The JavaScript Date representing the first day of the week.
	* @return {Boolean}	true if the date overlaps two different months.
	*/
	this.isMonthOverlapWeek = function(weekBeginDate) {
		var overlaps = false;
		var nextWeek = this.add(weekBeginDate, this.DAY, 6);
		if (nextWeek.getMonth() != weekBeginDate.getMonth()) {
			overlaps = true;
		}
		return overlaps;
	};

	/**
	* Gets the first day of a month containing a given date.
	* @param {Date}	date	The JavaScript Date used to calculate the month start
	* @return {Date}		The JavaScript Date representing the first day of the month
	*/
	this.findMonthStart = function(date) {
		var start = new Date(date.getFullYear(), date.getMonth(), 1);
		return start;
	};

	/**
	* Gets the last day of a month containing a given date.
	* @param {Date}	date	The JavaScript Date used to calculate the month end
	* @return {Date}		The JavaScript Date representing the last day of the month
	*/
	this.findMonthEnd = function(date) {
		var start = this.findMonthStart(date);
		var nextMonth = this.add(start, this.MONTH, 1);
		var end = this.subtract(nextMonth, this.DAY, 1);
		return end;
	};

	/**
	* Clears the time fields from a given date, effectively setting the time to midnight.
	* @param {Date}	date	The JavaScript Date for which the time fields will be cleared
	* @return {Date}		The JavaScript Date cleared of all time fields
	*/
	this.clearTime = function(date) {
		date.setHours(0,0,0,0);
		return date;
	};
}
/**
* <p>Calendar_Core is the base class for the Calendar widget. In its most basic
* implementation, it has the ability to render a calendar widget on the page
* that can be manipulated to select a single date, move back and forth between
* months and years.</p>
* <p>To construct the placeholder for the calendar widget, the code is as
* follows:
*	<xmp>
*		<div id="cal1Container"></div>
*	</xmp>
* Note that the table can be replaced with any kind of element.
* </p>
* @constructor
* @param {String}	id			The id of the table element that will represent the calendar widget
* @param {String}	containerId	The id of the container element that will contain the calendar table
* @param {String}	monthyear	The month/year string used to set the current calendar page
* @param {String}	selected	A string of date values formatted using the date parser. The built-in
								default date format is MM/DD/YYYY. Ranges are defined using
								MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
								Any combination of these can be combined by delimiting the string with
								commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
*/
YAHOO.widget.Calendar_Core = function(id, containerId, monthyear, selected) {
	if (arguments.length > 0) {
		this.init(id, containerId, monthyear, selected);
	}
}

/**
* The path to be used for images loaded for the Calendar
* @type String
*/
YAHOO.widget.Calendar_Core.IMG_ROOT = (window.location.href.toLowerCase().indexOf("https") == 0 ? "https://a248.e.akamai.net/sec.yimg.com/i/" : "http://us.i1.yimg.com/us.yimg.com/i/");

/**
* Type constant used for renderers to represent an individual date (M/D/Y)
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.DATE = "D";

/**
* Type constant used for renderers to represent an individual date across any year (M/D)
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.MONTH_DAY = "MD";

/**
* Type constant used for renderers to represent a weekday
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.WEEKDAY = "WD";

/**
* Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.RANGE = "R";

/**
* Type constant used for renderers to represent a month across any year
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.MONTH = "M";

/**
* Constant that represents the total number of date cells that are displayed in a given month
* including 
* @final
* @type Integer
*/
YAHOO.widget.Calendar_Core.DISPLAY_DAYS = 42;

/**
* Constant used for halting the execution of the remainder of the render stack
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.STOP_RENDER = "S";

YAHOO.widget.Calendar_Core.prototype = {

	/**
	* The configuration object used to set up the calendars various locale and style options.
	* @type Object
	*/
	Config : null,

	/**
	* The parent CalendarGroup, only to be set explicitly by the parent group
	* @type CalendarGroup
	*/	
	parent : null,

	/**
	* The index of this item in the parent group
	* @type Integer
	*/
	index : -1,

	/**
	* The collection of calendar table cells
	* @type HTMLTableCellElement[]
	*/
	cells : null,

	/**
	* The collection of calendar week header cells
	* @type HTMLTableCellElement[]
	*/
	weekHeaderCells : null,

	/**
	* The collection of calendar week footer cells
	* @type HTMLTableCellElement[]
	*/
	weekFooterCells : null,
	
	/**
	* The collection of calendar cell dates that is parallel to the cells collection. The array contains dates field arrays in the format of [YYYY, M, D].
	* @type Array[](Integer[])
	*/
	cellDates : null,

	/**
	* The id that uniquely identifies this calendar. This id should match the id of the placeholder element on the page.
	* @type String
	*/
	id : null,

	/**
	* The DOM element reference that points to this calendar's container element. The calendar will be inserted into this element when the shell is rendered.
	* @type HTMLElement
	*/
	oDomContainer : null,

	/**
	* A Date object representing today's date.
	* @type Date
	*/
	today : null,

	/**
	* The list of render functions, along with required parameters, used to render cells. 
	* @type Array[]
	*/
	renderStack : null,

	/**
	* A copy of the initial render functions created before rendering.
	* @type Array
	* @private
	*/
	_renderStack : null,

	/**
	* A Date object representing the month/year that the calendar is currently set to
	* @type Date
	*/
	pageDate : null,

	/**
	* A Date object representing the month/year that the calendar is initially set to
	* @type Date
	* @private
	*/
	_pageDate : null,
	
	/**
	* A Date object representing the minimum selectable date
	* @type Date
	*/
	minDate : null,
	
	/**
	* A Date object representing the maximum selectable date
	* @type Date
	*/
	maxDate : null,
	
	/**
	* The list of currently selected dates. The data format for this local collection is 
	* an array of date field arrays, e.g:
	* [
	*	[2004,5,25],
	*	[2004,5,26]
	* ]
	* @type Array[](Integer[])
	*/
	selectedDates : null,

	/**
	* The private list of initially selected dates.
	* @type Array
	* @private
	*/
	_selectedDates : null,

	/**
	* A boolean indicating whether the shell of the calendar has already been rendered to the page
	* @type Boolean
	*/	
	shellRendered : false,

	/**
	* The HTML table element that represents this calendar
	* @type HTMLTableElement
	*/	
	table : null,

	/**
	* The HTML cell element that represents the main header cell TH used in the calendar table
	* @type HTMLTableCellElement
	*/	
	headerCell : null
};



/**
* Initializes the calendar widget. This method must be called by all subclass constructors.
* @param {String}	id			The id of the table element that will represent the calendar widget
* @param {String}	containerId	The id of the container element that will contain the calendar table
* @param {String}	monthyear	The month/year string used to set the current calendar page
* @param {String}	selected	A string of date values formatted using the date parser. The built-in
								default date format is MM/DD/YYYY. Ranges are defined using
								MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
								Any combination of these can be combined by delimiting the string with
								commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
*/
YAHOO.widget.Calendar_Core.prototype.init = function(id, containerId, monthyear, selected) {
	this.setupConfig();

	this.id = id;

	this.cellDates = new Array();
	
	this.cells = new Array();
	
	this.renderStack = new Array();
	this._renderStack = new Array();

	this.oDomContainer = document.getElementById(containerId);
	
	this.today = new Date();
	YAHOO.widget.DateMath.clearTime(this.today);

	var month;
	var year;

	if (monthyear) {
		var aMonthYear = monthyear.split(this.Locale.DATE_FIELD_DELIMITER);
		month = parseInt(aMonthYear[this.Locale.MY_MONTH_POSITION-1]);
		year = parseInt(aMonthYear[this.Locale.MY_YEAR_POSITION-1]);
	} else {
		month = this.today.getMonth()+1;
		year = this.today.getFullYear();
	}

	this.pageDate = new Date(year, month-1, 1);
	this._pageDate = new Date(this.pageDate.getTime());

	if (selected) {
		this.selectedDates = this._parseDates(selected);
		this._selectedDates = this.selectedDates.concat();
	} else {
		this.selectedDates = new Array();
		this._selectedDates = new Array();
	}

	this.wireDefaultEvents();
	this.wireCustomEvents();
};


/**
* Wires the local DOM events for the Calendar, including cell selection, hover, and
* default navigation that is used for moving back and forth between calendar pages.
*/
YAHOO.widget.Calendar_Core.prototype.wireDefaultEvents = function() {
	
	/**
	* The default event function that is attached to a date link within a calendar cell
	* when the calendar is rendered. 
	* @param	e		The event
	* @param	cal		A reference to the calendar passed by the Event utility
	*/
	this.doSelectCell = function(e, cal) {		
		var cell = this;
		var index = cell.index;
		var d = cal.cellDates[index];
		var date = new Date(d[0],d[1]-1,d[2]);
		
		if (! cal.isDateOOM(date) && ! YAHOO.util.Dom.hasClass(cell, cal.Style.CSS_CELL_RESTRICTED) && ! YAHOO.util.Dom.hasClass(cell, cal.Style.CSS_CELL_OOB)) {
			if (cal.Options.MULTI_SELECT) {
				var link = cell.getElementsByTagName("A")[0];
				link.blur();
				
				var cellDate = cal.cellDates[index];
				var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
				
				if (cellDateIndex > -1) {	
					cal.deselectCell(index);
				} else {
					cal.selectCell(index);
				}	
				
			} else {
				var link = cell.getElementsByTagName("A")[0];
				link.blur()
				cal.selectCell(index);
			}
		}
	}

	/**
	* The event that is executed when the user hovers over a cell
	* @param	e		The event
	* @param	cal		A reference to the calendar passed by the Event utility
	* @private
	*/
	this.doCellMouseOver = function(e, cal) {
		var cell = this;
		var index = cell.index;
		var d = cal.cellDates[index];
		var date = new Date(d[0],d[1]-1,d[2]);

		if (! cal.isDateOOM(date) && ! YAHOO.util.Dom.hasClass(cell, cal.Style.CSS_CELL_RESTRICTED) && ! YAHOO.util.Dom.hasClass(cell, cal.Style.CSS_CELL_OOB)) {
			YAHOO.util.Dom.addClass(cell, cal.Style.CSS_CELL_HOVER);
		}
	}

	/**
	* The event that is executed when the user moves the mouse out of a cell
	* @param	e		The event
	* @param	cal		A reference to the calendar passed by the Event utility
	* @private
	*/
	this.doCellMouseOut = function(e, cal) {
		YAHOO.util.Dom.removeClass(this, cal.Style.CSS_CELL_HOVER);
	}
	
	/**
	* A wrapper event that executes the nextMonth method through a DOM event
	* @param	e		The event
	* @param	cal		A reference to the calendar passed by the Event utility
	* @private
	*/	
	this.doNextMonth = function(e, cal) {
		cal.nextMonth();
	}

	/**
	* A wrapper event that executes the previousMonth method through a DOM event
	* @param	e		The event
	* @param	cal		A reference to the calendar passed by the Event utility
	* @private
	*/		
	this.doPreviousMonth = function(e, cal) {
		cal.previousMonth();
	}
}

/**
* This function can be extended by subclasses to attach additional DOM events to
* the calendar. By default, this method is unimplemented.
*/
YAHOO.widget.Calendar_Core.prototype.wireCustomEvents = function() { }

/**
This method is called to initialize the widget configuration variables, including
style, localization, and other display and behavioral options.
<p>Config: Container for the CSS style configuration variables.</p>
<p><strong>Config.Style</strong> - Defines the CSS classes used for different calendar elements</p>
<blockquote>
	<div><em>CSS_CALENDAR</em> : Container table</div>
	<div><em>CSS_HEADER</em> : </div>
	<div><em>CSS_HEADER_TEXT</em> : Calendar header</div>
	<div><em>CSS_FOOTER</em> : Calendar footer</div>
	<div><em>CSS_CELL</em> : Calendar day cell</div>
	<div><em>CSS_CELL_OOM</em> : Calendar OOM (out of month) cell</div>
	<div><em>CSS_CELL_SELECTED</em> : Calendar selected cell</div>
	<div><em>CSS_CELL_RESTRICTED</em> : Calendar restricted cell</div>
	<div><em>CSS_CELL_TODAY</em> : Calendar cell for today's date</div>
	<div><em>CSS_ROW_HEADER</em> : The cell preceding a row (used for week number by default)</div>
	<div><em>CSS_ROW_FOOTER</em> : The cell following a row (not implemented by default)</div>
	<div><em>CSS_WEEKDAY_CELL</em> : The cells used for labeling weekdays</div>
	<div><em>CSS_WEEKDAY_ROW</em> : The row containing the weekday label cells</div>
	<div><em>CSS_CONTAINER</em> : The border style used for the default UED rendering</div>
	<div><em>CSS_2UPWRAPPER</em> : Special container class used to properly adjust the sizing and float</div>
	<div><em>CSS_NAV_LEFT</em> : Left navigation arrow</div>
	<div><em>CSS_NAV_RIGHT</em> : Right navigation arrow</div>
	<div><em>CSS_CELL_TOP</em> : Outlying cell along the top row</div>
	<div><em>CSS_CELL_LEFT</em> : Outlying cell along the left row</div>
	<div><em>CSS_CELL_RIGHT</em> : Outlying cell along the right row</div>
	<div><em>CSS_CELL_BOTTOM</em> : Outlying cell along the bottom row</div>
	<div><em>CSS_CELL_HOVER</em> : Cell hover style</div>
	<div><em>CSS_CELL_HIGHLIGHT1</em> : Highlight color 1 for styling cells</div>
	<div><em>CSS_CELL_HIGHLIGHT2</em> : Highlight color 2 for styling cells</div>
	<div><em>CSS_CELL_HIGHLIGHT3</em> : Highlight color 3 for styling cells</div>
	<div><em>CSS_CELL_HIGHLIGHT4</em> : Highlight color 4 for styling cells</div>

</blockquote>
<p><strong>Config.Locale</strong> - Defines the locale string arrays used for localization</p>
<blockquote>
	<div><em>MONTHS_SHORT</em> : Array of 12 months in short format ("Jan", "Feb", etc.)</div>
	<div><em>MONTHS_LONG</em> : Array of 12 months in short format ("Jan", "Feb", etc.)</div>
	<div><em>WEEKDAYS_1CHAR</em> : Array of 7 days in 1-character format ("S", "M", etc.)</div>
	<div><em>WEEKDAYS_SHORT</em> : Array of 7 days in short format ("Su", "Mo", etc.)</div>
	<div><em>WEEKDAYS_MEDIUM</em> : Array of 7 days in medium format ("Sun", "Mon", etc.)</div>
	<div><em>WEEKDAYS_LONG</em> : Array of 7 days in long format ("Sunday", "Monday", etc.)</div>
	<div><em>DATE_DELIMITER</em> : The value used to delimit series of multiple dates (Default: ",")</div>
	<div><em>DATE_FIELD_DELIMITER</em> : The value used to delimit date fields (Default: "/")</div>
	<div><em>DATE_RANGE_DELIMITER</em> : The value used to delimit date fields (Default: "-")</div>
	<div><em>MY_MONTH_POSITION</em> : The value used to determine the position of the month in a month/year combo (e.g. 12/2005) (Default: 1)</div>
	<div><em>MY_YEAR_POSITION</em> : The value used to determine the position of the year in a month/year combo (e.g. 12/2005) (Default: 2)</div>	
	<div><em>MD_MONTH_POSITION</em> : The value used to determine the position of the month in a month/day combo (e.g. 12/25) (Default: 1)</div>
	<div><em>MD_DAY_POSITION</em> : The value used to determine the position of the day in a month/day combo (e.g. 12/25) (Default: 2)</div>
	<div><em>MDY_MONTH_POSITION</em> : The value used to determine the position of the month in a month/day/year combo (e.g. 12/25/2005) (Default: 1)</div>
	<div><em>MDY_DAY_POSITION</em> : The value used to determine the position of the day in a month/day/year combo (e.g. 12/25/2005) (Default: 2)</div>
	<div><em>MDY_YEAR_POSITION</em> : The value used to determine the position of the year in a month/day/year combo (e.g. 12/25/2005) (Default: 3)</div>
</blockquote>
<p><strong>Config.Options</strong> - Defines other configurable calendar widget options</p>
<blockquote>
	<div><em>SHOW_WEEKDAYS</em> : Boolean, determines whether to display the weekday headers (defaults to true)</div>
	<div><em>LOCALE_MONTHS</em> : Array, points to the desired Config.Locale array (defaults to Config.Locale.MONTHS_LONG)</div>
	<div><em>LOCALE_WEEKDAYS</em> : Array, points to the desired Config.Locale array (defaults to Config.Locale.WEEKDAYS_SHORT)</div>
	<div><em>START_WEEKDAY</em> : Integer, 0-6, representing the day that a week begins on</div>
	<div><em>SHOW_WEEK_HEADER</em> : Boolean, determines whether to display row headers</div>
	<div><em>SHOW_WEEK_FOOTER</em> : Boolean, determines whether to display row footers</div>
	<div><em>HIDE_BLANK_WEEKS</em> : Boolean, determines whether to hide extra weeks that are completely OOM</div>
	<div><em>NAV_ARROW_LEFT</em> : String, the image path used for the left navigation arrow</div>
	<div><em>NAV_ARROW_RIGHT</em> : String, the image path used for the right navigation arrow</div>
</blockquote>
*/
YAHOO.widget.Calendar_Core.prototype.setupConfig = function() {
	/**
	* Container for the CSS style configuration variables.
	*/
	this.Config = new Object();
	
	this.Config.Style = {
		// Style variables
		CSS_ROW_HEADER: "calrowhead",
		CSS_ROW_FOOTER: "calrowfoot",
		CSS_CELL : "calcell",
		CSS_CELL_SELECTED : "selected",
		CSS_CELL_RESTRICTED : "restricted",
		CSS_CELL_TODAY : "today",
		CSS_CELL_OOM : "oom",
		CSS_CELL_OOB : "previous",
		CSS_HEADER : "calheader",
		CSS_HEADER_TEXT : "calhead",
		CSS_WEEKDAY_CELL : "calweekdaycell",
		CSS_WEEKDAY_ROW : "calweekdayrow",
		CSS_FOOTER : "calfoot",
		CSS_CALENDAR : "yui-calendar",
		CSS_CONTAINER : "yui-calcontainer",
		CSS_2UPWRAPPER : "yui-cal2upwrapper", 
		CSS_NAV_LEFT : "calnavleft",
		CSS_NAV_RIGHT : "calnavright",
		CSS_CELL_TOP : "calcelltop",
		CSS_CELL_LEFT : "calcellleft",
		CSS_CELL_RIGHT : "calcellright",
		CSS_CELL_BOTTOM : "calcellbottom",
		CSS_CELL_HOVER : "calcellhover",
		CSS_CELL_HIGHLIGHT1 : "highlight1",
		CSS_CELL_HIGHLIGHT2 : "highlight2",
		CSS_CELL_HIGHLIGHT3 : "highlight3",
		CSS_CELL_HIGHLIGHT4 : "highlight4"
	};

	this.Style = this.Config.Style;

	this.Config.Locale = {
		// Locale definition
		MONTHS_SHORT : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
		MONTHS_LONG : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
		WEEKDAYS_1CHAR : ["S", "M", "T", "W", "T", "F", "S"],
		WEEKDAYS_SHORT : ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
		WEEKDAYS_MEDIUM : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
		WEEKDAYS_LONG : ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
		DATE_DELIMITER : ",",
		DATE_FIELD_DELIMITER : "/",
		DATE_RANGE_DELIMITER : "-",
		MY_MONTH_POSITION : 1,
		MY_YEAR_POSITION : 2,
		MD_MONTH_POSITION : 1,
		MD_DAY_POSITION : 2,
		MDY_MONTH_POSITION : 1,
		MDY_DAY_POSITION : 2,
		MDY_YEAR_POSITION : 3
	};

	this.Locale = this.Config.Locale;

	this.Config.Options = {
		// Configuration variables
		MULTI_SELECT : false,
		SHOW_WEEKDAYS : true,
		START_WEEKDAY : 0,
		SHOW_WEEK_HEADER : false,
		SHOW_WEEK_FOOTER : false,
		HIDE_BLANK_WEEKS : false,
		NAV_ARROW_LEFT : YAHOO.widget.Calendar_Core.IMG_ROOT + "us/tr/callt.gif",
		NAV_ARROW_RIGHT : YAHOO.widget.Calendar_Core.IMG_ROOT + "us/tr/calrt.gif"
	};

	this.Options = this.Config.Options;

	this.customConfig();

	if (! this.Options.LOCALE_MONTHS) {
		this.Options.LOCALE_MONTHS=this.Locale.MONTHS_LONG;
	}
	if (! this.Options.LOCALE_WEEKDAYS) {
		this.Options.LOCALE_WEEKDAYS=this.Locale.WEEKDAYS_SHORT;
	}

	// If true, reconfigure weekday arrays to place Mondays first
	if (this.Options.START_WEEKDAY > 0) {
		for (var w=0;w<this.Options.START_WEEKDAY;++w) {
			this.Locale.WEEKDAYS_SHORT.push(this.Locale.WEEKDAYS_SHORT.shift());
			this.Locale.WEEKDAYS_MEDIUM.push(this.Locale.WEEKDAYS_MEDIUM.shift());
			this.Locale.WEEKDAYS_LONG.push(this.Locale.WEEKDAYS_LONG.shift());		
		}
	}
};

/**
* This method is called when subclasses need to override configuration variables
* or create new ones. Values can be explicitly set as follows:
* <blockquote><code>
*	this.Config.Style.CSS_CELL = "newcalcell";
*	this.Config.Locale.MONTHS_SHORT = ["Jan", "Fv", "Mars", "Avr", "Mai", "Juin", "Juil", "Aot", "Sept", "Oct", "Nov", "Dc"];
* </code></blockquote>
*/
YAHOO.widget.Calendar_Core.prototype.customConfig = function() { };

/**
* Builds the date label that will be displayed in the calendar header or
* footer, depending on configuration.
* @return	The formatted calendar month label
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.buildMonthLabel = function() {
	var text = this.Options.LOCALE_MONTHS[this.pageDate.getMonth()] + " " + this.pageDate.getFullYear();
	return text;
};

/**
* Builds the date digit that will be displayed in calendar cells
* @return	The formatted day label
* @type	String
*/
YAHOO.widget.Calendar_Core.prototype.buildDayLabel = function(workingDate) {
	var day = workingDate.getDate();
	return day;
};



/**
* Builds the calendar table shell that will be filled in with dates and formatting.
* This method calls buildShellHeader, buildShellBody, and buildShellFooter (in that order) 
* to construct the pieces of the calendar table. The construction of the shell should
* only happen one time when the calendar is initialized.
*/
YAHOO.widget.Calendar_Core.prototype.buildShell = function() {
	
	this.table = document.createElement("TABLE");
	this.table.cellSpacing = 0;	
	YAHOO.widget.Calendar_Core.setCssClasses(this.table, [this.Style.CSS_CALENDAR]);

	this.table.id = this.id;
	
	this.buildShellHeader();
	this.buildShellBody();
	this.buildShellFooter();
	
	YAHOO.util.Event.addListener(window, "unload", this._unload, this);
};

/**
* Builds the calendar shell header by inserting a THEAD into the local calendar table.
*/
YAHOO.widget.Calendar_Core.prototype.buildShellHeader = function() {
	var head = document.createElement("THEAD");
	var headRow = document.createElement("TR");

	var headerCell = document.createElement("TH");
	
	var colSpan = 7;
	if (this.Config.Options.SHOW_WEEK_HEADER) {
		this.weekHeaderCells = new Array();
		colSpan += 1;
	}
	if (this.Config.Options.SHOW_WEEK_FOOTER) {
		this.weekFooterCells = new Array();
		colSpan += 1;
	}	
	
	headerCell.colSpan = colSpan;
	
	YAHOO.widget.Calendar_Core.setCssClasses(headerCell,[this.Style.CSS_HEADER_TEXT]);

	this.headerCell = headerCell;

	headRow.appendChild(headerCell);
	head.appendChild(headRow);

	// Append day labels, if needed
	if (this.Options.SHOW_WEEKDAYS) {
		var row = document.createElement("TR");
		var fillerCell;

		YAHOO.widget.Calendar_Core.setCssClasses(row,[this.Style.CSS_WEEKDAY_ROW]);
		
		if (this.Config.Options.SHOW_WEEK_HEADER) {
			fillerCell = document.createElement("TH");
			YAHOO.widget.Calendar_Core.setCssClasses(fillerCell,[this.Style.CSS_WEEKDAY_CELL]);
			row.appendChild(fillerCell);
		}
		
		for(var i=0;i<this.Options.LOCALE_WEEKDAYS.length;++i) {
			var cell = document.createElement("TH");
			YAHOO.widget.Calendar_Core.setCssClasses(cell,[this.Style.CSS_WEEKDAY_CELL]);
			cell.innerHTML=this.Options.LOCALE_WEEKDAYS[i];
			row.appendChild(cell);
		}

		if (this.Config.Options.SHOW_WEEK_FOOTER) {
			fillerCell = document.createElement("TH");
			YAHOO.widget.Calendar_Core.setCssClasses(fillerCell,[this.Style.CSS_WEEKDAY_CELL]);
			row.appendChild(fillerCell);
		}
				
		head.appendChild(row);
	}

	this.table.appendChild(head);
};

/**
* Builds the calendar shell body (6 weeks by 7 days)
*/
YAHOO.widget.Calendar_Core.prototype.buildShellBody = function() {
	// This should only get executed once
	this.tbody = document.createElement("TBODY");

	for (var r=0;r<6;++r) {
		var row = document.createElement("TR");
		
		for (var c=0;c<this.headerCell.colSpan;++c) {
			var cell;
			if (this.Config.Options.SHOW_WEEK_HEADER && c===0) { // Row header
				cell = document.createElement("TH");
				this.weekHeaderCells[this.weekHeaderCells.length] = cell;
			} else if (this.Config.Options.SHOW_WEEK_FOOTER && c==(this.headerCell.colSpan-1)){ // Row footer
				cell = document.createElement("TH");
				this.weekFooterCells[this.weekFooterCells.length] = cell;
			} else {
				cell = document.createElement("TD");
				this.cells[this.cells.length] = cell;
				YAHOO.widget.Calendar_Core.setCssClasses(cell, [this.Style.CSS_CELL]);
				YAHOO.util.Event.addListener(cell, "click", this.doSelectCell, this);

				YAHOO.util.Event.addListener(cell, "mouseover", this.doCellMouseOver, this);
				YAHOO.util.Event.addListener(cell, "mouseout", this.doCellMouseOut, this);
			}
			row.appendChild(cell);
		}
		this.tbody.appendChild(row);
	}
	
	this.table.appendChild(this.tbody);
};

/**
* Builds the calendar shell footer. In the default implementation, there is
* no footer.
*/
YAHOO.widget.Calendar_Core.prototype.buildShellFooter = function() { };

/**
* Outputs the calendar shell to the DOM, inserting it into the placeholder element.
*/
YAHOO.widget.Calendar_Core.prototype.renderShell = function() {
	this.oDomContainer.appendChild(this.table);
	this.shellRendered = true;
};

/**
* Renders the calendar after it has been configured. The render() method has a specific call chain that will execute
* when the method is called: renderHeader, renderBody, renderFooter.
* Refer to the documentation for those methods for information on 
* individual render tasks.
*/
YAHOO.widget.Calendar_Core.prototype.render = function() {
	if (! this.shellRendered) {
		this.buildShell();
		this.renderShell();
	}

	this.resetRenderers();

	this.cellDates.length = 0;

	// Find starting day of the current month
	var workingDate = YAHOO.widget.DateMath.findMonthStart(this.pageDate);

	this.renderHeader();
	this.renderBody(workingDate);
	this.renderFooter();

	this.onRender();
};



/**
* Appends the header contents into the widget header.
*/
YAHOO.widget.Calendar_Core.prototype.renderHeader = function() {
	this.headerCell.innerHTML = "";
	
	var headerContainer = document.createElement("DIV");
	headerContainer.className = this.Style.CSS_HEADER;
	
	headerContainer.appendChild(document.createTextNode(this.buildMonthLabel()));
	
	this.headerCell.appendChild(headerContainer);
};

/**
* Appends the calendar body. The default implementation calculates the number of
* OOM (out of month) cells that need to be rendered at the start of the month, renders those, 
* and renders all the day cells using the built-in cell rendering methods.
*
* While iterating through all of the cells, the calendar checks for renderers in the
* local render stack that match the date of the current cell, and then applies styles
* as necessary.
* 
* @param {Date}	workingDate	The current working Date object being used to generate the calendar
*/
YAHOO.widget.Calendar_Core.prototype.renderBody = function(workingDate) {

	this.preMonthDays = workingDate.getDay();
	if (this.Options.START_WEEKDAY > 0) {
		this.preMonthDays -= this.Options.START_WEEKDAY;
	}
	if (this.preMonthDays < 0) {
		this.preMonthDays += 7;
	}
	
	this.monthDays = YAHOO.widget.DateMath.findMonthEnd(workingDate).getDate();
	this.postMonthDays = YAHOO.widget.Calendar_Core.DISPLAY_DAYS-this.preMonthDays-this.monthDays;
	
	workingDate = YAHOO.widget.DateMath.subtract(workingDate, YAHOO.widget.DateMath.DAY, this.preMonthDays);
	
	var weekRowIndex = 0;
	
	for (var c=0;c<this.cells.length;++c) {
		var cellRenderers = new Array();
		
		var cell = this.cells[c];
		this.clearElement(cell);

		cell.index = c;
		cell.id = this.id + "_cell" + c;
		
		this.cellDates[this.cellDates.length]=[workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()]; // Add this date to cellDates

		if (workingDate.getDay() == this.Options.START_WEEKDAY) {
			var rowHeaderCell = null;
			var rowFooterCell = null;
			
			if (this.Options.SHOW_WEEK_HEADER) {
				rowHeaderCell = this.weekHeaderCells[weekRowIndex];
				this.clearElement(rowHeaderCell);
			}
			
			if (this.Options.SHOW_WEEK_FOOTER) {
				rowFooterCell = this.weekFooterCells[weekRowIndex];
				this.clearElement(rowFooterCell);
			}			
			
			if (this.Options.HIDE_BLANK_WEEKS && this.isDateOOM(workingDate) && ! YAHOO.widget.DateMath.isMonthOverlapWeek(workingDate)) {
				// The first day of the week is not in this month, and it's not an overlap week
				continue;
			} else {
				if (rowHeaderCell) {
					this.renderRowHeader(workingDate, rowHeaderCell);
				}
				if (rowFooterCell) {
					this.renderRowFooter(workingDate, rowFooterCell);
				}	
			}
		}

		

		var renderer = null;
		
		if (workingDate.getFullYear()	== this.today.getFullYear() &&
			workingDate.getMonth()		== this.today.getMonth() &&
			workingDate.getDate()		== this.today.getDate()) {
			cellRenderers[cellRenderers.length]=this.renderCellStyleToday;
		}
		
		if (this.isDateOOM(workingDate)) {
			cellRenderers[cellRenderers.length]=this.renderCellNotThisMonth;
		} else {
			for (var r=0;r<this.renderStack.length;++r) {
				var rArray = this.renderStack[r];
				var type = rArray[0];
				
				var month;
				var day;
				var year;

				switch (type) {
					case YAHOO.widget.Calendar_Core.DATE:
						month = rArray[1][1];
						day = rArray[1][2];
						year = rArray[1][0];

						if (workingDate.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == year) {
							renderer = rArray[2];
							this.renderStack.splice(r,1);
						}
						break;
					case YAHOO.widget.Calendar_Core.MONTH_DAY:
						month = rArray[1][0];
						day = rArray[1][1];
						
						if (workingDate.getMonth()+1 == month && workingDate.getDate() == day) {
							renderer = rArray[2];
							this.renderStack.splice(r,1);
						}
						break;
					case YAHOO.widget.Calendar_Core.RANGE:
						var date1 = rArray[1][0];
						var date2 = rArray[1][1];

						var d1month = date1[1];
						var d1day = date1[2];
						var d1year = date1[0];
						
						var d1 = new Date(d1year, d1month-1, d1day);

						var d2month = date2[1];
						var d2day = date2[2];
						var d2year = date2[0];

						var d2 = new Date(d2year, d2month-1, d2day);

						if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime()) {
							renderer = rArray[2];

							if (workingDate.getTime()==d2.getTime()) { 
								this.renderStack.splice(r,1);
							}
						}
						break;
					case YAHOO.widget.Calendar_Core.WEEKDAY:
						
						var weekday = rArray[1][0];
						if (workingDate.getDay()+1 == weekday) {
							renderer = rArray[2];
						}
						break;
					case YAHOO.widget.Calendar_Core.MONTH:
						
						month = rArray[1][0];
						if (workingDate.getMonth()+1 == month) {
							renderer = rArray[2];
						}
						break;
				}
				
				if (renderer) {
					cellRenderers[cellRenderers.length]=renderer;
				}
			}

		}

		if (this._indexOfSelectedFieldArray([workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()]) > -1) {
			cellRenderers[cellRenderers.length]=this.renderCellStyleSelected; 
		}

		if (this.minDate) {
			this.minDate = YAHOO.widget.DateMath.clearTime(this.minDate);
		}
		if (this.maxDate) {
			this.maxDate = YAHOO.widget.DateMath.clearTime(this.maxDate);
		}

		if (
			(this.minDate && (workingDate.getTime() < this.minDate.getTime())) ||
			(this.maxDate && (workingDate.getTime() > this.maxDate.getTime()))
		) {
			cellRenderers[cellRenderers.length]=this.renderOutOfBoundsDate;
		} else {
			cellRenderers[cellRenderers.length]=this.renderCellDefault;	
		}
		
		for (var x=0;x<cellRenderers.length;++x) {
			var ren = cellRenderers[x];
			if (ren.call(this,workingDate,cell) == YAHOO.widget.Calendar_Core.STOP_RENDER) {
				break;
			}
		}
		
		workingDate = YAHOO.widget.DateMath.add(workingDate, YAHOO.widget.DateMath.DAY, 1); // Go to the next day
		if (workingDate.getDay() == this.Options.START_WEEKDAY) {
			weekRowIndex += 1;
		}

		YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL);

		if (c >= 0 && c <= 6) {
			YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_TOP);
		}
		if ((c % 7) == 0) {
			YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_LEFT);
		}
		if (((c+1) % 7) == 0) {
			YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_RIGHT);
		}
		
		var postDays = this.postMonthDays; 
		if (postDays >= 7 && this.Options.HIDE_BLANK_WEEKS) {
			var blankWeeks = Math.floor(postDays/7);
			for (var p=0;p<blankWeeks;++p) {
				postDays -= 7;
			}
		}
		
		if (c >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
			YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM);
		}
	}
		
};

/**
* Appends the contents of the calendar widget footer into the shell. By default, 
* the calendar does not contain a footer, and this method must be implemented by 
* subclassing the widget.
*/
YAHOO.widget.Calendar_Core.prototype.renderFooter = function() { };

/**
* @private
*/
YAHOO.widget.Calendar_Core.prototype._unload = function(e, cal) {
	for (var c in cal.cells) {
		c = null;
	}
	
	cal.cells = null;
	
	cal.tbody = null;
	cal.oDomContainer = null;
	cal.table = null;
	cal.headerCell = null;
	
	cal = null;
};
												  
												  
/****************** BEGIN BUILT-IN TABLE CELL RENDERERS ************************************/

YAHOO.widget.Calendar_Core.prototype.renderOutOfBoundsDate = function(workingDate, cell) {
	YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOB);
	cell.innerHTML = workingDate.getDate();
	return YAHOO.widget.Calendar_Core.STOP_RENDER;
}

/**
* Renders the row header for a week. The date passed in should be
* the first date of the given week.
* @param {Date}					workingDate		The current working Date object (beginning of the week) being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
*/
YAHOO.widget.Calendar_Core.prototype.renderRowHeader = function(workingDate, cell) {
	YAHOO.util.Dom.addClass(cell, this.Style.CSS_ROW_HEADER);
	
	var useYear = this.pageDate.getFullYear();
	
	if (! YAHOO.widget.DateMath.isYearOverlapWeek(workingDate)) {
		useYear = workingDate.getFullYear();
	}
	
	var weekNum = YAHOO.widget.DateMath.getWeekNumber(workingDate, useYear, this.Options.START_WEEKDAY);
	cell.innerHTML = weekNum;
	
	if (this.isDateOOM(workingDate) && ! YAHOO.widget.DateMath.isMonthOverlapWeek(workingDate)) {
		YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM);	
	}
};

/**
* Renders the row footer for a week. The date passed in should be
* the first date of the given week.
* @param {Date}					workingDate		The current working Date object (beginning of the week) being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
*/
YAHOO.widget.Calendar_Core.prototype.renderRowFooter = function(workingDate, cell) {
	YAHOO.util.Dom.addClass(cell, this.Style.CSS_ROW_FOOTER);
	
	if (this.isDateOOM(workingDate) && ! YAHOO.widget.DateMath.isMonthOverlapWeek(workingDate)) {
		YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM);	
	}
};

/**
* Renders a single standard calendar cell in the calendar widget table.
* All logic for determining how a standard default cell will be rendered is 
* encapsulated in this method, and must be accounted for when extending the
* widget class.
* @param {Date}					workingDate		The current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
* @return YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*			should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellDefault = function(workingDate, cell) {
	cell.innerHTML = "";
	var link = document.createElement("a");

	link.href="javascript:void(null);";
	link.name=this.id+"__"+workingDate.getFullYear()+"_"+(workingDate.getMonth()+1)+"_"+workingDate.getDate();

	link.appendChild(document.createTextNode(this.buildDayLabel(workingDate)));
	cell.appendChild(link);
};

/**
* Renders a single standard calendar cell using the CSS hightlight1 style
* @param {Date}					workingDate		The current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
* @return YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*			should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight1 = function(workingDate, cell) {
	YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
};

/**
* Renders a single standard calendar cell using the CSS hightlight2 style
* @param {Date}					workingDate		The current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
* @return YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*			should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight2 = function(workingDate, cell) {
	YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
};

/**
* Renders a single standard calendar cell using the CSS hightlight3 style
* @param {Date}					workingDate		The current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
* @return YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*			should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight3 = function(workingDate, cell) {
	YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
};

/**
* Renders a single standard calendar cell using the CSS hightlight4 style
* @param {Date}					workingDate		The current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
* @return YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*			should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight4 = function(workingDate, cell) {
	YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
};

/**
* Applies the default style used for rendering today's date to the current calendar cell
* @param {Date}					workingDate		The current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
* @return	YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*			should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleToday = function(workingDate, cell) {
	YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_TODAY);
};

/**
* Applies the default style used for rendering selected dates to the current calendar cell
* @param {Date}					workingDate		The current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
* @return	YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*			should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleSelected = function(workingDate, cell) {
	YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_SELECTED);
};

/**
* Applies the default style used for rendering dates that are not a part of the current
* month (preceding or trailing the cells for the current month)
* @param {Date}					workingDate		The current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
* @return	YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*			should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellNotThisMonth = function(workingDate, cell) {
	YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM);
	cell.innerHTML=workingDate.getDate();
	return YAHOO.widget.Calendar_Core.STOP_RENDER;
};

/**
* Renders the current calendar cell as a non-selectable "black-out" date using the default
* restricted style.
* @param {Date}					workingDate		The current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
* @return	YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*			should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderBodyCellRestricted = function(workingDate, cell) {
	YAHOO.widget.Calendar_Core.setCssClasses(cell, [this.Style.CSS_CELL,this.Style.CSS_CELL_RESTRICTED]);
	cell.innerHTML=workingDate.getDate();
	return YAHOO.widget.Calendar_Core.STOP_RENDER;
};
/******************** END BUILT-IN TABLE CELL RENDERERS ************************************/

/******************** BEGIN MONTH NAVIGATION METHODS ************************************/
/**
* Adds the designated number of months to the current calendar month, and sets the current
* calendar page date to the new month.
* @param {Integer}	count	The number of months to add to the current calendar
*/
YAHOO.widget.Calendar_Core.prototype.addMonths = function(count) {
	this.pageDate = YAHOO.widget.DateMath.add(this.pageDate, YAHOO.widget.DateMath.MONTH, count);
	this.resetRenderers();
	this.onChangePage();
};

/**
* Subtracts the designated number of months from the current calendar month, and sets the current
* calendar page date to the new month.
* @param {Integer}	count	The number of months to subtract from the current calendar
*/
YAHOO.widget.Calendar_Core.prototype.subtractMonths = function(count) {
	this.pageDate = YAHOO.widget.DateMath.subtract(this.pageDate, YAHOO.widget.DateMath.MONTH, count);
	this.resetRenderers();
	this.onChangePage();
};

/**
* Adds the designated number of years to the current calendar, and sets the current
* calendar page date to the new month.
* @param {Integer}	count	The number of years to add to the current calendar
*/
YAHOO.widget.Calendar_Core.prototype.addYears = function(count) {
	this.pageDate = YAHOO.widget.DateMath.add(this.pageDate, YAHOO.widget.DateMath.YEAR, count);
	this.resetRenderers();
	this.onChangePage();
};

/**
* Subtcats the designated number of years from the current calendar, and sets the current
* calendar page date to the new month.
* @param {Integer}	count	The number of years to subtract from the current calendar
*/
YAHOO.widget.Calendar_Core.prototype.subtractYears = function(count) {
	this.pageDate = YAHOO.widget.DateMath.subtract(this.pageDate, YAHOO.widget.DateMath.YEAR, count);
	this.resetRenderers();
	this.onChangePage();
};

/**
* Navigates to the next month page in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.nextMonth = function() {
	this.addMonths(1);
};

/**
* Navigates to the previous month page in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.previousMonth = function() {
	this.subtractMonths(1);
};

/**
* Navigates to the next year in the currently selected month in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.nextYear = function() {
	this.addYears(1);
};

/**
* Navigates to the previous year in the currently selected month in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.previousYear = function() {
	this.subtractYears(1);
};

/****************** END MONTH NAVIGATION METHODS ************************************/

/************* BEGIN SELECTION METHODS *************************************************************/

/**
* Resets the calendar widget to the originally selected month and year, and 
* sets the calendar to the initial selection(s).
*/
YAHOO.widget.Calendar_Core.prototype.reset = function() {
	this.selectedDates.length = 0;
	this.selectedDates = this._selectedDates.concat();

	this.pageDate = new Date(this._pageDate.getTime());
	this.onReset();
};

/**
* Clears the selected dates in the current calendar widget and sets the calendar
* to the current month and year.
*/
YAHOO.widget.Calendar_Core.prototype.clear = function() {
	this.selectedDates.length = 0;
	this.pageDate = new Date(this.today.getTime());
	this.onClear();
};

/**
* Selects a date or a collection of dates on the current calendar. This method, by default,
* does not call the render method explicitly. Once selection has completed, render must be 
* called for the changes to be reflected visually.
* @param	{String/Date/Date[]}	date	The date string of dates to select in the current calendar. Valid formats are
*								individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
*								Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
*								This method can also take a JavaScript Date object or an array of Date objects.
* @return						Array of JavaScript Date objects representing all individual dates that are currently selected.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.select = function(date) {
	this.onBeforeSelect();
	
	var aToBeSelected = this._toFieldArray(date);

	for (var a=0;a<aToBeSelected.length;++a) {
		var toSelect = aToBeSelected[a]; // For each date item in the list of dates we're trying to select
		if (this._indexOfSelectedFieldArray(toSelect) == -1) { // not already selected?
			this.selectedDates[this.selectedDates.length]=toSelect;
		}
	}
	
	if (this.parent) {
		this.parent.sync(this);
	}

	this.onSelect(aToBeSelected);

	return this.getSelectedDates();
};

/**
* Selects a date on the current calendar by referencing the index of the cell that should be selected.
* This method is used to easily select a single cell (usually with a mouse click) without having to do
* a full render. The selected style is applied to the cell directly.
* @param	{Integer}	cellIndex	The index of the cell to select in the current calendar. 
* @return							Array of JavaScript Date objects representing all individual dates that are currently selected.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.selectCell = function(cellIndex) {
	this.onBeforeSelect();

	this.cells = this.tbody.getElementsByTagName("TD");

	var cell = this.cells[cellIndex];
	var cellDate = this.cellDates[cellIndex];

	var dCellDate = this._toDate(cellDate);

	var selectDate = cellDate.concat();

	this.selectedDates.push(selectDate);
	
	if (this.parent) {
		this.parent.sync(this);
	}

	this.renderCellStyleSelected(dCellDate,cell);

	this.onSelect([selectDate]);
	this.doCellMouseOut.call(cell, null, this);

	return this.getSelectedDates();
};

/**
* Deselects a date or a collection of dates on the current calendar. This method, by default,
* does not call the render method explicitly. Once deselection has completed, render must be 
* called for the changes to be reflected visually.
* @param	{String/Date/Date[]}	date	The date string of dates to deselect in the current calendar. Valid formats are
*								individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
*								Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
*								This method can also take a JavaScript Date object or an array of Date objects.	
* @return						Array of JavaScript Date objects representing all individual dates that are currently selected.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.deselect = function(date) {
	this.onBeforeDeselect();

	var aToBeSelected = this._toFieldArray(date);

	for (var a=0;a<aToBeSelected.length;++a) {
		var toSelect = aToBeSelected[a]; // For each date item in the list of dates we're trying to select
		var index = this._indexOfSelectedFieldArray(toSelect);
	
		if (index != -1) {	
			this.selectedDates.splice(index,1);
		}
	}

	if (this.parent) {
		this.parent.sync(this);
	} 

	this.onDeselect(aToBeSelected);
	return this.getSelectedDates();
};

/**
* Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
* This method is used to easily deselect a single cell (usually with a mouse click) without having to do
* a full render. The selected style is removed from the cell directly.
* @param	{Integer}	cellIndex	The index of the cell to deselect in the current calendar. 
* @return							Array of JavaScript Date objects representing all individual dates that are currently selected.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.deselectCell = function(i) {
	this.onBeforeDeselect();
	this.cells = this.tbody.getElementsByTagName("TD");

	var cell = this.cells[i];
	var cellDate = this.cellDates[i];
	var cellDateIndex = this._indexOfSelectedFieldArray(cellDate);

	var dCellDate = this._toDate(cellDate);

	var selectDate = cellDate.concat();

	if (cellDateIndex > -1) {
		if (this.pageDate.getMonth() == dCellDate.getMonth() &&
			this.pageDate.getFullYear() == dCellDate.getFullYear()) {
			YAHOO.util.Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
		}

		this.selectedDates.splice(cellDateIndex, 1);
	}


	if (this.parent) {
		this.parent.sync(this);
	}

	this.onDeselect(selectDate);
	return this.getSelectedDates();
};

/**
* Deselects all dates on the current calendar.
* @return				Array of JavaScript Date objects representing all individual dates that are currently selected.
*						Assuming that this function executes properly, the return value should be an empty array.
*						However, the empty array is returned for the sake of being able to check the selection status
*						of the calendar.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.deselectAll = function() {
	this.onBeforeDeselect();
	var count = this.selectedDates.length;
	var sel = this.selectedDates.concat();
	this.selectedDates.length = 0;

	if (this.parent) {
		this.parent.sync(this);
	}
	
	if (count > 0) {
		this.onDeselect(sel);
	}

	return this.getSelectedDates();
};
/************* END SELECTION METHODS *************************************************************/


/************* BEGIN TYPE CONVERSION METHODS ****************************************************/

/**
* Converts a date (either a JavaScript Date object, or a date string) to the internal data structure
* used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]].
* @private
* @param	{String/Date/Date[]}	date	The date string of dates to deselect in the current calendar. Valid formats are
*								individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
*								Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
*								This method can also take a JavaScript Date object or an array of Date objects.	
* @return						Array of date field arrays
* @type Array[](Integer[])
*/
YAHOO.widget.Calendar_Core.prototype._toFieldArray = function(date) {
	var returnDate = new Array();

	if (date instanceof Date) {
		returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]];
	} else if (typeof date == 'string') {
		returnDate = this._parseDates(date);
	} else if (date instanceof Array) {
		for (var i=0;i<date.length;++i) {
			var d = date[i];
			returnDate[returnDate.length] = [d.getFullYear(),d.getMonth()+1,d.getDate()];
		}
	}
	
	return returnDate;
};

/**
* Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
* @private
* @param	{Integer[]}		dateFieldArray	The date field array to convert to a JavaScript Date.
* @return					JavaScript Date object representing the date field array
* @type Date
*/
YAHOO.widget.Calendar_Core.prototype._toDate = function(dateFieldArray) {
	if (dateFieldArray instanceof Date) {
		return dateFieldArray;
	} else {
		return new Date(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]);
	}
};
/************* END TYPE CONVERSION METHODS ******************************************************/


/************* BEGIN UTILITY METHODS ****************************************************/
/**
* Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
* @private
* @param	{Integer[]}	array1	The first date field array to compare
* @param	{Integer[]}	array2	The first date field array to compare
* @return						The boolean that represents the equality of the two arrays
* @type Boolean
*/
YAHOO.widget.Calendar_Core.prototype._fieldArraysAreEqual = function(array1, array2) {
	var match = false;

	if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2]) {
		match=true;	
	}

	return match;
};

/**
* Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates.
* @private
* @param	{Integer[]}		find	The date field array to search for
* @return					The index of the date field array within the collection of selected dates.
*								-1 will be returned if the date is not found.
* @type Integer
*/
YAHOO.widget.Calendar_Core.prototype._indexOfSelectedFieldArray = function(find) {
	var selected = -1;

	for (var s=0;s<this.selectedDates.length;++s) {
		var sArray = this.selectedDates[s];
		if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2]) {
			selected = s;
			break;
		}
	}

	return selected;
};

/**
* Determines whether a given date is OOM (out of month).
* @param	{Date}	date	The JavaScript Date object for which to check the OOM status
* @return	{Boolean}	true if the date is OOM
*/
YAHOO.widget.Calendar_Core.prototype.isDateOOM = function(date) {
	var isOOM = false;
	if (date.getMonth() != this.pageDate.getMonth()) {
		isOOM = true;
	}
	return isOOM;
};

/************* END UTILITY METHODS *******************************************************/

/************* BEGIN EVENT HANDLERS ******************************************************/

/**
* Event executed before a date is selected in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.onBeforeSelect = function() {
	if (! this.Options.MULTI_SELECT) {
		this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED);
		this.deselectAll();
	}
};

/**
* Event executed when a date is selected in the calendar widget.
* @param	{Array}	selected	An array of date field arrays representing which date or dates were selected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ]
*/
YAHOO.widget.Calendar_Core.prototype.onSelect = function(selected) { };

/**
* Event executed before a date is deselected in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.onBeforeDeselect = function() { };

/**
* Event executed when a date is deselected in the calendar widget.
* @param	{Array}	selected	An array of date field arrays representing which date or dates were deselected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ]
*/
YAHOO.widget.Calendar_Core.prototype.onDeselect = function(deselected) { };

/**
* Event executed when the user navigates to a different calendar page.
*/
YAHOO.widget.Calendar_Core.prototype.onChangePage = function() {
		var me = this;

		this.renderHeader();
		if (this.renderProcId) {
			clearTimeout(this.renderProcId);
		}
		this.renderProcId = setTimeout(function() {
											me.render();
											me.renderProcId = null;
										}, 1);
};

/**
* Event executed when the calendar widget is rendered.
*/
YAHOO.widget.Calendar_Core.prototype.onRender = function() { };

/**
* Event executed when the calendar widget is reset to its original state.
*/
YAHOO.widget.Calendar_Core.prototype.onReset = function() { this.render(); };

/**
* Event executed when the calendar widget is completely cleared to the current month with no selections.
*/
YAHOO.widget.Calendar_Core.prototype.onClear = function() { this.render(); };

/**
* Validates the calendar widget. This method has no default implementation
* and must be extended by subclassing the widget.
* @return	Should return true if the widget validates, and false if
* it doesn't.
* @type Boolean
*/
YAHOO.widget.Calendar_Core.prototype.validate = function() { return true; };

/************* END EVENT HANDLERS *********************************************************/


/************* BEGIN DATE PARSE METHODS ***************************************************/


/**
* Converts a date string to a date field array
* @private
* @param	{String}	sDate			Date string. Valid formats are mm/dd and mm/dd/yyyy.
* @return				A date field array representing the string passed to the method
* @type Array[](Integer[])
*/
YAHOO.widget.Calendar_Core.prototype._parseDate = function(sDate) {
	var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER);
	var rArray;

	if (aDate.length == 2) {
		rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
		rArray.type = YAHOO.widget.Calendar_Core.MONTH_DAY;
	} else {
		rArray = [aDate[this.Locale.MDY_YEAR_POSITION-1],aDate[this.Locale.MDY_MONTH_POSITION-1],aDate[this.Locale.MDY_DAY_POSITION-1]];
		rArray.type = YAHOO.widget.Calendar_Core.DATE;
	}
	return rArray;
};

/**
* Converts a multi or single-date string to an array of date field arrays
* @private
* @param	{String}	sDates		Date string with one or more comma-delimited dates. Valid formats are mm/dd, mm/dd/yyyy, mm/dd/yyyy-mm/dd/yyyy
* @return							An array of date field arrays
* @type Array[](Integer[])
*/
YAHOO.widget.Calendar_Core.prototype._parseDates = function(sDates) {
	var aReturn = new Array();

	var aDates = sDates.split(this.Locale.DATE_DELIMITER);
	
	for (var d=0;d<aDates.length;++d) {
		var sDate = aDates[d];

		if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) {
			// This is a range
			var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER);

			var dateStart = this._parseDate(aRange[0]);
			var dateEnd = this._parseDate(aRange[1]);

			var fullRange = this._parseRange(dateStart, dateEnd);
			aReturn = aReturn.concat(fullRange);
		} else {
			// This is not a range
			var aDate = this._parseDate(sDate);
			aReturn.push(aDate);
		}
	}
	return aReturn;
};

/**
* Converts a date range to the full list of included dates
* @private
* @param	{Integer[]}	startDate	Date field array representing the first date in the range
* @param	{Integer[]}	endDate		Date field array representing the last date in the range
* @return							An array of date field arrays
* @type Array[](Integer[])
*/
YAHOO.widget.Calendar_Core.prototype._parseRange = function(startDate, endDate) {
	var dStart   = new Date(startDate[0],startDate[1]-1,startDate[2]);
	var dCurrent = YAHOO.widget.DateMath.add(new Date(startDate[0],startDate[1]-1,startDate[2]),YAHOO.widget.DateMath.DAY,1);
	var dEnd     = new Date(endDate[0],  endDate[1]-1,  endDate[2]);

	var results = new Array();
	results.push(startDate);
	while (dCurrent.getTime() <= dEnd.getTime()) {
		results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
		dCurrent = YAHOO.widget.DateMath.add(dCurrent,YAHOO.widget.DateMath.DAY,1);
	}
	return results;
};

/************* END DATE PARSE METHODS *****************************************************/

/************* BEGIN RENDERER METHODS *****************************************************/

/**
* Resets the render stack of the current calendar to its original pre-render value.
*/
YAHOO.widget.Calendar_Core.prototype.resetRenderers = function() {
	this.renderStack = this._renderStack.concat();
};

/**
* Clears the inner HTML, CSS class and style information from the specified cell.
* @param	{HTMLTableCellElement}	The cell to clear
*/ 
YAHOO.widget.Calendar_Core.prototype.clearElement = function(cell) {
	cell.innerHTML = "&nbsp;";
	cell.className="";
};

/**
* Adds a renderer to the render stack. The function reference passed to this method will be executed
* when a date cell matches the conditions specified in the date string for this renderer.
* @param	{String}	sDates		A date string to associate with the specified renderer. Valid formats
*									include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
* @param	{Function}	fnRender	The function executed to render cells that match the render rules for this renderer.
*/
YAHOO.widget.Calendar_Core.prototype.addRenderer = function(sDates, fnRender) {
	var aDates = this._parseDates(sDates);
	for (var i=0;i<aDates.length;++i) {
		var aDate = aDates[i];
	
		if (aDate.length == 2) { // this is either a range or a month/day combo
			if (aDate[0] instanceof Array) { // this is a range
				this._addRenderer(YAHOO.widget.Calendar_Core.RANGE,aDate,fnRender);
			} else { // this is a month/day combo
				this._addRenderer(YAHOO.widget.Calendar_Core.MONTH_DAY,aDate,fnRender);
			}
		} else if (aDate.length == 3) {
			this._addRenderer(YAHOO.widget.Calendar_Core.DATE,aDate,fnRender);
		}
	}
};

/**
* The private method used for adding cell renderers to the local render stack.
* This method is called by other methods that set the renderer type prior to the method call.
* @private
* @param	{String}	type		The type string that indicates the type of date renderer being added.
*									Values are YAHOO.widget.Calendar_Core.DATE, YAHOO.widget.Calendar_Core.MONTH_DAY, YAHOO.widget.Calendar_Core.WEEKDAY,
*									YAHOO.widget.Calendar_Core.RANGE, YAHOO.widget.Calendar_Core.MONTH
* @param	{Array}		aDates		An array of dates used to construct the renderer. The format varies based
*									on the renderer type
* @param	{Function}	fnRender	The function executed to render cells that match the render rules for this renderer.
*/
YAHOO.widget.Calendar_Core.prototype._addRenderer = function(type, aDates, fnRender) {
	var add = [type,aDates,fnRender];
	this.renderStack.unshift(add);	
	
	this._renderStack = this.renderStack.concat();
};

/**
* Adds a month to the render stack. The function reference passed to this method will be executed
* when a date cell matches the month passed to this method.
* @param	{Integer}	month		The month (1-12) to associate with this renderer
* @param	{Function}	fnRender	The function executed to render cells that match the render rules for this renderer.
*/
YAHOO.widget.Calendar_Core.prototype.addMonthRenderer = function(month, fnRender) {
	this._addRenderer(YAHOO.widget.Calendar_Core.MONTH,[month],fnRender);
};

/**
* Adds a weekday to the render stack. The function reference passed to this method will be executed
* when a date cell matches the weekday passed to this method.
* @param	{Integer}	weekay		The weekday (1-7) to associate with this renderer
* @param	{Function}	fnRender	The function executed to render cells that match the render rules for this renderer.
*/
YAHOO.widget.Calendar_Core.prototype.addWeekdayRenderer = function(weekday, fnRender) {
	this._addRenderer(YAHOO.widget.Calendar_Core.WEEKDAY,[weekday],fnRender);
};
//// END RENDERER METHODS ////

//// BEGIN CSS METHODS ////

/**
* Sets the specified array of CSS classes into the referenced element
* @param	{HTMLElement}	element		The element to set the CSS classes into
* @param	{String[]}		aStyles		An array of CSS class names
*/
YAHOO.widget.Calendar_Core.setCssClasses = function(element, aStyles) {
	element.className = "";
	var className = aStyles.join(" ");
	element.className = className;
};

/**
* Removes all styles from all body cells in the current calendar table.
* @param	{style}		The CSS class name to remove from all calendar body cells
*/
YAHOO.widget.Calendar_Core.prototype.clearAllBodyCellStyles = function(style) {
	for (var c=0;c<this.cells.length;++c) {
		YAHOO.util.Dom.removeClass(this.cells[c],style);
	}
};

//// END CSS METHODS ////

//// BEGIN GETTER/SETTER METHODS ////
/**
* Sets the calendar's month explicitly.
* @param {Integer}	month		The numeric month, from 1 (January) to 12 (December)
*/
YAHOO.widget.Calendar_Core.prototype.setMonth = function(month) {
	this.pageDate.setMonth(month);
};

/**
* Sets the calendar's year explicitly.
* @param {Integer}	year		The numeric 4-digit year
*/
YAHOO.widget.Calendar_Core.prototype.setYear = function(year) {
	this.pageDate.setFullYear(year);
};

/**
* Gets the list of currently selected dates from the calendar.
* @return	An array of currently selected JavaScript Date objects.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.getSelectedDates = function() {
	var returnDates = new Array();

	for (var d=0;d<this.selectedDates.length;++d) {
		var dateArray = this.selectedDates[d];

		var date = new Date(dateArray[0],dateArray[1]-1,dateArray[2]);
		returnDates.push(date);
	}

	returnDates.sort();
	return returnDates;
};

/// END GETTER/SETTER METHODS ///

/**
* Returns a string representing the current browser.
* @type String
*/
YAHOO.widget.Calendar_Core._getBrowser = function() {
  /**
   * UserAgent
   * @private
   * @type String
   */
  var ua = navigator.userAgent.toLowerCase();
  
  if (ua.indexOf('opera')!=-1) // Opera (check first in case of spoof)
	 return 'opera';
  else if (ua.indexOf('msie')!=-1) // IE
	 return 'ie';
  else if (ua.indexOf('safari')!=-1) // Safari (check before Gecko because it includes "like Gecko")
	 return 'safari';
  else if (ua.indexOf('gecko') != -1) // Gecko
	 return 'gecko';
 else
  return false;
}

/**
* Returns a string representation of the object.
* @type string
*/
YAHOO.widget.Calendar_Core.prototype.toString = function() {
	return "Calendar_Core " + this.id;
}

YAHOO.widget.Cal_Core = YAHOO.widget.Calendar_Core;
/**
* Calendar is the default implementation of the YAHOO.widget.Calendar_Core base class.
* This class is the UED-approved version of the calendar selector widget. For all documentation
* on the implemented methods listed here, see the documentation for YAHOO.widget.Calendar_Core.
* @constructor
* @param {String}	id			The id of the table element that will represent the calendar widget
* @param {String}	containerId	The id of the container element that will contain the calendar table
* @param {String}	monthyear	The month/year string used to set the current calendar page
* @param {String}	selected	A string of date values formatted using the date parser. The built-in
								default date format is MM/DD/YYYY. Ranges are defined using
								MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
								Any combination of these can be combined by delimiting the string with
								commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
* @extends YAHOO.widget.Calendar_Core
*/
YAHOO.widget.Calendar = function(id, containerId, monthyear, selected) {
	if (arguments.length > 0) {
		this.init(id, containerId, monthyear, selected);
	}
}

YAHOO.widget.Calendar.prototype = new YAHOO.widget.Calendar_Core();

YAHOO.widget.Calendar.prototype.buildShell = function() {
	this.border = document.createElement("DIV");
	this.border.className =  this.Style.CSS_CONTAINER;
	
	this.table = document.createElement("TABLE");
	this.table.cellSpacing = 0;	
	
	YAHOO.widget.Calendar_Core.setCssClasses(this.table, [this.Style.CSS_CALENDAR]);

	this.border.id = this.id;
	
	this.buildShellHeader();
	this.buildShellBody();
	this.buildShellFooter();
};

YAHOO.widget.Calendar.prototype.renderShell = function() {
	this.border.appendChild(this.table);
	this.oDomContainer.appendChild(this.border);
	this.shellRendered = true;
};

YAHOO.widget.Calendar.prototype.renderHeader = function() {
	this.headerCell.innerHTML = "";
	
	var headerContainer = document.createElement("DIV");
	headerContainer.className = this.Style.CSS_HEADER;
	
	if (this.linkLeft) {
		YAHOO.util.Event.removeListener(this.linkLeft, "mousedown", this.previousMonth);
	}
	this.linkLeft = document.createElement("A");
	this.linkLeft.innerHTML = "&nbsp;";
	YAHOO.util.Event.addListener(this.linkLeft, "mousedown", this.previousMonth, this, true);
	this.linkLeft.style.backgroundImage =  "url(" + this.Options.NAV_ARROW_LEFT + ")";
	this.linkLeft.className = this.Style.CSS_NAV_LEFT;
	
	if (this.linkRight) {
		YAHOO.util.Event.removeListener(this.linkRight, "mousedown", this.nextMonth);
	}
	this.linkRight = document.createElement("A");
	this.linkRight.innerHTML = "&nbsp;";
	YAHOO.util.Event.addListener(this.linkRight, "mousedown", this.nextMonth, this, true);
	this.linkRight.style.backgroundImage = "url(" + this.Options.NAV_ARROW_RIGHT + ")";
	this.linkRight.className = this.Style.CSS_NAV_RIGHT;
	
	headerContainer.appendChild(this.linkLeft);
	headerContainer.appendChild(document.createTextNode(this.buildMonthLabel()));
	headerContainer.appendChild(this.linkRight);
	
	this.headerCell.appendChild(headerContainer);
};

YAHOO.widget.Cal = YAHOO.widget.Calendar;
/**
* <p>YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar_Core. This class facilitates
* the ability to have multi-page calendar views that share a single dataset and are
* dependent on each other.</p>
* 
* <p>The calendar group instance will refer to each of its elements using a 0-based index.
* For example, to construct the placeholder for a calendar group widget with id "cal1" and
* containerId of "cal1Container", the markup would be as follows:
*	<xmp>
*		<div id="cal1Container_0"></div>
*		<div id="cal1Container_1"></div>
*	</xmp>
* The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers.
* </p>
* @constructor
* @param {Integer}		pageCount	The number of pages that this calendar should display.
* @param {String}		id			The id of the element that will be inserted into the DOM.
* @param {String}	containerId	The id of the container element that the calendar will be inserted into.
* @param {String}		monthyear	The month/year string used to set the current calendar page
* @param {String}		selected	A string of date values formatted using the date parser. The built-in
									default date format is MM/DD/YYYY. Ranges are defined using
									MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
									Any combination of these can be combined by delimiting the string with
									commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
*/
YAHOO.widget.CalendarGroup = function(pageCount, id, containerId, monthyear, selected) {
	if (arguments.length > 0) {
		this.init(pageCount, id, containerId, monthyear, selected);
	}
}

/**
* Initializes the calendar group. All subclasses must call this method in order for the
* group to be initialized properly.
* @param {Integer}		pageCount	The number of pages that this calendar should display.
* @param {String}		id			The id of the element that will be inserted into the DOM.
* @param {String}		containerId	The id of the container element that the calendar will be inserted into.
* @param {String}		monthyear	The month/year string used to set the current calendar page
* @param {String}		selected	A string of date values formatted using the date parser. The built-in
									default date format is MM/DD/YYYY. Ranges are defined using
									MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
									Any combination of these can be combined by delimiting the string with
									commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
*/
YAHOO.widget.CalendarGroup.prototype.init = function(pageCount, id, containerId, monthyear, selected) {
	this.id = id;
	this.selectedDates = new Array();
	this.containerId = containerId;
	
	this.pageCount = pageCount;

	this.pages = new Array();

	for (var p=0;p<pageCount;++p) {
		var cal = this.constructChild(id + "_" + p, this.containerId + "_" + p , monthyear, selected);
				
		cal.parent = this;
		
		cal.index = p;

		cal.pageDate.setMonth(cal.pageDate.getMonth()+p);
		cal._pageDate = new Date(cal.pageDate.getFullYear(),cal.pageDate.getMonth(),cal.pageDate.getDate());
		this.pages.push(cal);
	}
	
	this.sync();

	this.doNextMonth = function(e, calGroup) {
		calGroup.nextMonth();
	};
	
	this.doPreviousMonth = function(e, calGroup) {
		calGroup.previousMonth();
	};
};

/**
* Adds a function to all child Calendars within this CalendarGroup.
* @param {String}		fnName		The name of the function
* @param {Function}		fn			The function to apply to each Calendar page object
*/
YAHOO.widget.CalendarGroup.prototype.setChildFunction = function(fnName, fn) {
	for (var p=0;p<this.pageCount;++p) {
		this.pages[p][fnName] = fn;
	}
}

/**
* Calls a function within all child Calendars within this CalendarGroup.
* @param {String}		fnName		The name of the function
* @param {Array}		args		The arguments to pass to the function
*/
YAHOO.widget.CalendarGroup.prototype.callChildFunction = function(fnName, args) {
	for (var p=0;p<this.pageCount;++p) {
		var page = this.pages[p];
		if (page[fnName]) {
			var fn = page[fnName];
			fn.call(page, args);
		}
	}	
}

/**
* Constructs a child calendar. This method can be overridden if a subclassed version of the default
* calendar is to be used.
* @param {String}		id			The id of the element that will be inserted into the DOM.
* @param {String}		containerId	The id of the container element that the calendar will be inserted into.
* @param {String}		monthyear	The month/year string used to set the current calendar page
* @param {String}		selected	A string of date values formatted using the date parser. The built-in
									default date format is MM/DD/YYYY. Ranges are defined using
									MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
									Any combination of these can be combined by delimiting the string with
									commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
* @return							The YAHOO.widget.Calendar_Core instance that is constructed
* @type YAHOO.widget.Calendar_Core
*/
YAHOO.widget.CalendarGroup.prototype.constructChild = function(id,containerId,monthyear,selected) {
	return new YAHOO.widget.Calendar_Core(id,containerId,monthyear,selected);
};


/**
* Sets the calendar group's month explicitly. This month will be set into the first
* page of the multi-page calendar, and all other months will be iterated appropriately.
* @param {Integer}	month		The numeric month, from 1 (January) to 12 (December)
*/
YAHOO.widget.CalendarGroup.prototype.setMonth = function(month) {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.setMonth(month+p);
	}
};

/**
* Sets the calendar group's year explicitly. This year will be set into the first
* page of the multi-page calendar, and all other months will be iterated appropriately.
* @param {Integer}	year		The numeric 4-digit year
*/
YAHOO.widget.CalendarGroup.prototype.setYear = function(year) {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		if ((cal.pageDate.getMonth()+1) == 1 && p>0)
		{
			year+=1;
		}
		cal.setYear(year);
	}
};

/**
* Calls the render function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.render = function() {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.render();
	}
};

/**
* Calls the select function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.select = function(date) {
	var ret;
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		ret = cal.select(date);
	}
	return ret;
};

/**
* Calls the selectCell function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.selectCell = function(cellIndex) {
	var ret;
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		ret = cal.selectCell(cellIndex);
	}
	return ret;
};

/**
* Calls the deselect function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.deselect = function(date) {
	var ret;
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		ret = cal.deselect(date);
	}
	return ret;
};

/**
* Calls the deselectAll function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.deselectAll = function() {
	var ret;
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		ret = cal.deselectAll();
	}
	return ret;
};

/**
* Calls the deselectAll function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.deselectCell = function(cellIndex) {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.deselectCell(cellIndex);
	}
	return this.getSelectedDates();
};

/**
* Calls the reset function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.reset = function() {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.reset();
	}
};

/**
* Calls the clear function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.clear = function() {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.clear();
	}
};

/**
* Calls the nextMonth function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.nextMonth = function() {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.nextMonth();
	}
};

/**
* Calls the previousMonth function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.previousMonth = function() {
	for (var p=this.pages.length-1;p>=0;--p)
	{
		var cal = this.pages[p];
		cal.previousMonth();
	}
};

/**
* Calls the nextYear function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.nextYear = function() {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.nextYear();
	}
};

/**
* Calls the previousYear function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.previousYear = function() {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.previousYear();
	}
};

/**
* Synchronizes the data values for all child calendars within the group. If the sync
* method is called passing in the caller object, the values of all children will be set
* to the values of the caller. If the argument is ommitted, the values from all children
* will be combined into one distinct list and set into each child.
* @param	{YAHOO.widget.Calendar_Core}	caller		The YAHOO.widget.Calendar_Core that is initiating the call to sync().
* @return								Array of selected dates, in JavaScript Date object form.
* @type Date[]
*/
YAHOO.widget.CalendarGroup.prototype.sync = function(caller) {
	var calendar;

	if (caller)
	{
		this.selectedDates = caller.selectedDates.concat();
	} else {
		var hash = new Object();
		var combinedDates = new Array();

		for (var p=0;p<this.pages.length;++p)
		{
			calendar = this.pages[p];

			var values = calendar.selectedDates;

			for (var v=0;v<values.length;++v)
			{
				var valueArray = values[v];
				hash[valueArray.toString()] = valueArray;
			}
		}

		for (var val in hash)
		{
			combinedDates[combinedDates.length]=hash[val];
		}
		
		this.selectedDates = combinedDates.concat();
	}

	// Set all the values into the children
	for (p=0;p<this.pages.length;++p)
	{
		calendar = this.pages[p];
		if (! calendar.Options.MULTI_SELECT) {
			calendar.clearAllBodyCellStyles(calendar.Config.Style.CSS_CELL_SELECTED);
		}
		calendar.selectedDates = this.selectedDates.concat();
		
	}
	
	return this.getSelectedDates();
};

/**
* Gets the list of currently selected dates from the calendar.
* @return			An array of currently selected JavaScript Date objects.
* @type Date[]
*/
YAHOO.widget.CalendarGroup.prototype.getSelectedDates = function() { 
	var returnDates = new Array();
    
	for (var d=0;d<this.selectedDates.length;++d)
	{
		var dateArray = this.selectedDates[d];

		var date = new Date(dateArray[0],dateArray[1]-1,dateArray[2]);
		returnDates.push(date);
	}

	returnDates.sort();
	return returnDates;
};

/**
* Calls the addRenderer function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.addRenderer = function(sDates, fnRender) {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.addRenderer(sDates, fnRender);
	}
};

/**
* Calls the addMonthRenderer function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.addMonthRenderer = function(month, fnRender) {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.addMonthRenderer(month, fnRender);
	}
};

/**
* Calls the addWeekdayRenderer function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.addWeekdayRenderer = function(weekday, fnRender) {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal.addWeekdayRenderer(weekday, fnRender);
	}
};

/**
* Sets an event handler universally across all child calendars within the group. For instance,
* to set the onSelect handler for all child calendars to a function called fnSelect, the call would be:
* <code>
* calGroup.wireEvent("onSelect", fnSelect);
* </code>
* @param	{String}	eventName	The name of the event to handler to set within all child calendars.
* @param	{Function}	fn			The function to set into the specified event handler.
*/
YAHOO.widget.CalendarGroup.prototype.wireEvent = function(eventName, fn) {
	for (var p=0;p<this.pages.length;++p)
	{
		var cal = this.pages[p];
		cal[eventName] = fn;
	}
};

/**
* Returns a string representation of the object.
* @type string
*/ 
YAHOO.widget.CalendarGroup.prototype.toString = function() {
	return "CalendarGroup " + this.id;
}

YAHOO.widget.CalGrp = YAHOO.widget.CalendarGroup;
/**
* Calendar2up_Cal is the default implementation of the Calendar_Core base class, when used
* in a 2-up view. This class is the UED-approved version of the calendar selector widget. For all documentation
* on the implemented methods listed here, see the documentation for Calendar_Core. This class
* has some special attributes that only apply to calendars rendered within the calendar group implementation. 
* There should be no reason to instantiate this class directly.
* @constructor
* @param {String}	id			The id of the table element that will represent the calendar widget
* @param {String}	containerId	The id of the container element that will contain the calendar table
* @param {String}	monthyear	The month/year string used to set the current calendar page
* @param {String}	selected	A string of date values formatted using the date parser. The built-in
								default date format is MM/DD/YYYY. Ranges are defined using
								MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
								Any combination of these can be combined by delimiting the string with
								commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
* @extends YAHOO.widget.Calendar_Core
*/
YAHOO.widget.Calendar2up_Cal = function(id, containerId, monthyear, selected) {
	if (arguments.length > 0)
	{
		this.init(id, containerId, monthyear, selected);
	}
}

YAHOO.widget.Calendar2up_Cal.prototype = new YAHOO.widget.Calendar_Core();

/**
* Renders the header for each individual calendar in the 2-up view. More
* specifically, this method handles the placement of left and right arrows for
* navigating between calendar pages.
*/
YAHOO.widget.Calendar2up_Cal.prototype.renderHeader = function() {
	this.headerCell.innerHTML = "";
	
	var headerContainer = document.createElement("DIV");
	headerContainer.className = this.Style.CSS_HEADER;
	
	if (this.index == 0) {

		if (this.linkLeft) {
			YAHOO.util.Event.removeListener(this.linkLeft, "mousedown", this.parent.doPreviousMonth);
		}
		this.linkLeft = document.createElement("A");
		this.linkLeft.innerHTML = "&nbsp;";
		this.linkLeft.style.backgroundImage =  "url(" + this.Options.NAV_ARROW_LEFT + ")";
		this.linkLeft.className = this.Style.CSS_NAV_LEFT;
		
		YAHOO.util.Event.addListener(this.linkLeft, "mousedown", this.parent.doPreviousMonth, this.parent);
		headerContainer.appendChild(this.linkLeft);
	}
	
	headerContainer.appendChild(document.createTextNode(this.buildMonthLabel()));
	
	if (this.index == 1) {

		if (this.linkRight) {
			YAHOO.util.Event.removeListener(this.linkRight, "mousedown", this.parent.doNextMonth);
		}
		this.linkRight = document.createElement("A");
		this.linkRight.innerHTML = "&nbsp;";
		this.linkRight.style.backgroundImage = "url(" + this.Options.NAV_ARROW_RIGHT + ")";
		this.linkRight.className = this.Style.CSS_NAV_RIGHT;

		YAHOO.util.Event.addListener(this.linkRight, "mousedown", this.parent.doNextMonth, this.parent);
		headerContainer.appendChild(this.linkRight);
	}
	
	this.headerCell.appendChild(headerContainer);
};




/**
* Calendar2up is the default implementation of the CalendarGroup base class, when used
* in a 2-up view. This class is the UED-approved version of the 2-up calendar selector widget. For all documentation
* on the implemented methods listed here, see the documentation for CalendarGroup. 
* @constructor
* @param {String}	id			The id of the table element that will represent the calendar widget
* @param {String}	containerId	The id of the container element that will contain the calendar table
* @param {String}	monthyear	The month/year string used to set the current calendar page
* @param {String}	selected	A string of date values formatted using the date parser. The built-in
								default date format is MM/DD/YYYY. Ranges are defined using
								MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
								Any combination of these can be combined by delimiting the string with
								commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
* @extends YAHOO.widget.CalendarGroup
*/
YAHOO.widget.Calendar2up = function(id, containerId, monthyear, selected) {
	if (arguments.length > 0)
	{	
		this.buildWrapper(containerId);
		this.init(2, id, containerId, monthyear, selected);
	}
}

YAHOO.widget.Calendar2up.prototype = new YAHOO.widget.CalendarGroup();

/**
* CSS class representing the wrapper for the 2-up calendar
* @type string
*/
YAHOO.widget.Calendar2up.CSS_2UPWRAPPER = "yui-cal2upwrapper";

/**
* CSS class representing the container for the calendar
* @type string
*/
YAHOO.widget.Calendar2up.CSS_CONTAINER = "yui-calcontainer";

/**
* CSS class representing the container for the 2-up calendar
* @type string
*/
YAHOO.widget.Calendar2up.CSS_2UPCONTAINER = "cal2up";

/**
* CSS class representing the title for the 2-up calendar
* @type string
*/
YAHOO.widget.Calendar2up.CSS_2UPTITLE = "title";

/**
* CSS class representing the close icon for the 2-up calendar
* @type string
*/
YAHOO.widget.Calendar2up.CSS_2UPCLOSE = "close-icon";

/**
* Implementation of CalendarGroup.constructChild that ensures that child calendars of 
* Calendar2up will be of type Calendar2up_Cal.
*/
YAHOO.widget.Calendar2up.prototype.constructChild = function(id,containerId,monthyear,selected) {
	var cal = new YAHOO.widget.Calendar2up_Cal(id,containerId,monthyear,selected);
	return cal;
};

/**
* Builds the wrapper container for the 2-up calendar.
* @param {String} containerId	The id of the outer container element.
*/
YAHOO.widget.Calendar2up.prototype.buildWrapper = function(containerId) {
	var outerContainer = document.getElementById(containerId);
	
	outerContainer.className = YAHOO.widget.Calendar2up.CSS_2UPWRAPPER;
	
	var innerContainer = document.createElement("DIV");
	innerContainer.className = YAHOO.widget.Calendar2up.CSS_CONTAINER;
	innerContainer.id = containerId + "_inner";
	
	var cal1Container = document.createElement("DIV");
	cal1Container.id = containerId + "_0";
	cal1Container.className = YAHOO.widget.Calendar2up.CSS_2UPCONTAINER;
	cal1Container.style.marginRight = "10px";
	
	var cal2Container = document.createElement("DIV");
	cal2Container.id = containerId + "_1"; 
	cal2Container.className = YAHOO.widget.Calendar2up.CSS_2UPCONTAINER;
	
	outerContainer.appendChild(innerContainer);
	innerContainer.appendChild(cal1Container);
	innerContainer.appendChild(cal2Container);
	
	this.innerContainer = innerContainer;
	this.outerContainer = outerContainer;
}

/**
* Renders the 2-up calendar.
*/
YAHOO.widget.Calendar2up.prototype.render = function() {
	this.renderHeader();
	YAHOO.widget.CalendarGroup.prototype.render.call(this);
	this.renderFooter();
};

/**
* Renders the header located at the top of the container for the 2-up calendar.
*/
YAHOO.widget.Calendar2up.prototype.renderHeader = function() {
	if (! this.title) {
		this.title = "";
	}
	if (! this.titleDiv)
	{
		this.titleDiv = document.createElement("DIV");
		if (this.title == "")
		{
			this.titleDiv.style.display="none";
		}
	}

	this.titleDiv.className = YAHOO.widget.Calendar2up.CSS_2UPTITLE;
	this.titleDiv.innerHTML = this.title;

	if (this.outerContainer.style.position == "absolute")
	{
		var linkClose = document.createElement("A");
		linkClose.href = "javascript:void(null)";
		YAHOO.util.Event.addListener(linkClose, "click", this.hide, this);

		var imgClose = document.createElement("IMG");
		imgClose.src = YAHOO.widget.Calendar_Core.IMG_ROOT + "us/my/bn/x_d.gif";
		imgClose.className = YAHOO.widget.Calendar2up.CSS_2UPCLOSE;

		linkClose.appendChild(imgClose);

		this.linkClose = linkClose;

		this.titleDiv.appendChild(linkClose);
	}

	if (this.titleDiv != this.innerContainer.firstChild) {
		this.innerContainer.insertBefore(this.titleDiv, this.innerContainer.firstChild);
	}
}

/**
* Hides the 2-up calendar's outer container from view.
*/
YAHOO.widget.Calendar2up.prototype.hide = function(e, cal) {
	if (! cal)
	{
		cal = this;
	}
	cal.outerContainer.style.display = "none";
}

/**
* Renders a footer for the 2-up calendar container. By default, this method is
* unimplemented.
*/
YAHOO.widget.Calendar2up.prototype.renderFooter = function() {}

YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up;

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Singapore Singapore
Michael Sync is a Microsoft MVP for Silverlight and a member of Microsoft WPF/Silverlight Insider Team.

Please find more details about me in my blog.

Comments and Discussions