Calendar_Core.js

Note: Code presented here has been modified from the original source to aid in processing and presentation. This is NOT functional code. To review this component's literal, functional source, please refer to its JavaScript files as provided in the downloadable YUI Library distribution.

Source Code [top]

/*
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>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;