text: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License. You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied. See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  (function () {
  
      /**
      * Config is a utility used within an Object to allow the implementer to
      * maintain a list of local configuration properties and listen for changes 
      * to those properties dynamically using CustomEvent. The initial values are 
      * also maintained so that the configuration can be reset at any given point 
      * to its initial state.
      * @namespace YAHOO.util
      * @class Config
      * @constructor
      * @param {Object} owner The owner Object to which this Config Object belongs
      */
      YAHOO.util.Config = function (owner) {
  
          if (owner) {
              this.init(owner);
          }
  
  
      };
  
  
      var Lang = YAHOO.lang,
          CustomEvent = YAHOO.util.CustomEvent,
          Config = YAHOO.util.Config;
  
  
      /**
       * Constant representing the CustomEvent type for the config changed event.
       * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT
       * @private
       * @static
       * @final
       */
      Config.CONFIG_CHANGED_EVENT = "configChanged";
      
      /**
       * Constant representing the boolean type string
       * @property YAHOO.util.Config.BOOLEAN_TYPE
       * @private
       * @static
       * @final
       */
      Config.BOOLEAN_TYPE = "boolean";
      
      Config.prototype = {
       
          /**
          * Object reference to the owner of this Config Object
          * @property owner
          * @type Object
          */
          owner: null,
          
          /**
          * Boolean flag that specifies whether a queue is currently 
          * being executed
          * @property queueInProgress
          * @type Boolean
          */
          queueInProgress: false,
          
          /**
          * Maintains the local collection of configuration property objects and 
          * their specified values
          * @property config
          * @private
          * @type Object
          */ 
          config: null,
          
          /**
          * Maintains the local collection of configuration property objects as 
          * they were initially applied.
          * This object is used when resetting a property.
          * @property initialConfig
          * @private
          * @type Object
          */ 
          initialConfig: null,
          
          /**
          * Maintains the local, normalized CustomEvent queue
          * @property eventQueue
          * @private
          * @type Object
          */ 
          eventQueue: null,
          
          /**
          * Custom Event, notifying subscribers when Config properties are set 
          * (setProperty is called without the silent flag
          * @event configChangedEvent
          */
          configChangedEvent: null,
      
          /**
          * Initializes the configuration Object and all of its local members.
          * @method init
          * @param {Object} owner The owner Object to which this Config 
          * Object belongs
          */
          init: function (owner) {
      
              this.owner = owner;
      
              this.configChangedEvent = 
                  this.createEvent(Config.CONFIG_CHANGED_EVENT);
      
              this.configChangedEvent.signature = CustomEvent.LIST;
              this.queueInProgress = false;
              this.config = {};
              this.initialConfig = {};
              this.eventQueue = [];
          
          },
          
          /**
          * Validates that the value passed in is a Boolean.
          * @method checkBoolean
          * @param {Object} val The value to validate
          * @return {Boolean} true, if the value is valid
          */ 
          checkBoolean: function (val) {
              return (typeof val == Config.BOOLEAN_TYPE);
          },
          
          /**
          * Validates that the value passed in is a number.
          * @method checkNumber
          * @param {Object} val The value to validate
          * @return {Boolean} true, if the value is valid
          */
          checkNumber: function (val) {
              return (!isNaN(val));
          },
          
          /**
          * Fires a configuration property event using the specified value. 
          * @method fireEvent
          * @private
          * @param {String} key The configuration property's name
          * @param {value} Object The value of the correct type for the property
          */ 
          fireEvent: function ( key, value ) {
              var property = this.config[key];
          
              if (property && property.event) {
                  property.event.fire(value);
              } 
          },
          
          /**
          * Adds a property to the Config Object's private config hash.
          * @method addProperty
          * @param {String} key The configuration property's name
          * @param {Object} propertyObject The Object containing all of this 
          * property's arguments
          */
          addProperty: function ( key, propertyObject ) {
              key = key.toLowerCase();
          
              this.config[key] = propertyObject;
          
              propertyObject.event = this.createEvent(key, { scope: this.owner });
              propertyObject.event.signature = CustomEvent.LIST;
              
              
              propertyObject.key = key;
          
              if (propertyObject.handler) {
                  propertyObject.event.subscribe(propertyObject.handler, 
                      this.owner);
              }
          
              this.setProperty(key, propertyObject.value, true);
              
              if (! propertyObject.suppressEvent) {
                  this.queueProperty(key, propertyObject.value);
              }
              
          },
          
          /**
          * Returns a key-value configuration map of the values currently set in  
          * the Config Object.
          * @method getConfig
          * @return {Object} The current config, represented in a key-value map
          */
          getConfig: function () {
          
              var cfg = {},
                  currCfg = this.config,
                  prop,
                  property;
                  
              for (prop in currCfg) {
                  if (Lang.hasOwnProperty(currCfg, prop)) {
                      property = currCfg[prop];
                      if (property && property.event) {
                          cfg[prop] = property.value;
                      }
                  }
              }
  
              return cfg;
          },
          
          /**
          * Returns the value of specified property.
          * @method getProperty
          * @param {String} key The name of the property
          * @return {Object}  The value of the specified property
          */
          getProperty: function (key) {
              var property = this.config[key.toLowerCase()];
              if (property && property.event) {
                  return property.value;
              } else {
                  return undefined;
              }
          },
          
          /**
          * Resets the specified property's value to its initial value.
          * @method resetProperty
          * @param {String} key The name of the property
          * @return {Boolean} True is the property was reset, false if not
          */
          resetProperty: function (key) {
      
              key = key.toLowerCase();
          
              var property = this.config[key];
      
              if (property && property.event) {
      
                  if (this.initialConfig[key] && 
                      !Lang.isUndefined(this.initialConfig[key])) {
      
                      this.setProperty(key, this.initialConfig[key]);
  
                      return true;
      
                  }
      
              } else {
      
                  return false;
              }
      
          },
          
          /**
          * Sets the value of a property. If the silent property is passed as 
          * true, the property's event will not be fired.
          * @method setProperty
          * @param {String} key The name of the property
          * @param {String} value The value to set the property to
          * @param {Boolean} silent Whether the value should be set silently, 
          * without firing the property event.
          * @return {Boolean} True, if the set was successful, false if it failed.
          */
          setProperty: function (key, value, silent) {
          
              var property;
          
              key = key.toLowerCase();
          
              if (this.queueInProgress && ! silent) {
                  // Currently running through a queue... 
                  this.queueProperty(key,value);
                  return true;
      
              } else {
                  property = this.config[key];
                  if (property && property.event) {
                      if (property.validator && !property.validator(value)) {
                          return false;
                      } else {
                          property.value = value;
                          if (! silent) {
                              this.fireEvent(key, value);
                              this.configChangedEvent.fire([key, value]);
                          }
                          return true;
                      }
                  } else {
                      return false;
                  }
              }
          },
          
          /**
          * Sets the value of a property and queues its event to execute. If the 
          * event is already scheduled to execute, it is
          * moved from its current position to the end of the queue.
          * @method queueProperty
          * @param {String} key The name of the property
          * @param {String} value The value to set the property to
          * @return {Boolean}  true, if the set was successful, false if 
          * it failed.
          */ 
          queueProperty: function (key, value) {
          
              key = key.toLowerCase();
          
              var property = this.config[key],
                  foundDuplicate = false,
                  iLen,
                  queueItem,
                  queueItemKey,
                  queueItemValue,
                  sLen,
                  supercedesCheck,
                  qLen,
                  queueItemCheck,
                  queueItemCheckKey,
                  queueItemCheckValue,
                  i,
                  s,
                  q;
                                  
              if (property && property.event) {
      
                  if (!Lang.isUndefined(value) && property.validator && 
                      !property.validator(value)) { // validator
                      return false;
                  } else {
          
                      if (!Lang.isUndefined(value)) {
                          property.value = value;
                      } else {
                          value = property.value;
                      }
          
                      foundDuplicate = false;
                      iLen = this.eventQueue.length;
          
                      for (i = 0; i < iLen; i++) {
                          queueItem = this.eventQueue[i];
          
                          if (queueItem) {
                              queueItemKey = queueItem[0];
                              queueItemValue = queueItem[1];
  
                              if (queueItemKey == key) {
      
                                  /*
                                      found a dupe... push to end of queue, null 
                                      current item, and break
                                  */
      
                                  this.eventQueue[i] = null;
      
                                  this.eventQueue.push(
                                      [key, (!Lang.isUndefined(value) ? 
                                      value : queueItemValue)]);
      
                                  foundDuplicate = true;
                                  break;
                              }
                          }
                      }
                      
                      // this is a refire, or a new property in the queue
      
                      if (! foundDuplicate && !Lang.isUndefined(value)) { 
                          this.eventQueue.push([key, value]);
                      }
                  }
          
                  if (property.supercedes) {
  
                      sLen = property.supercedes.length;
  
                      for (s = 0; s < sLen; s++) {
  
                          supercedesCheck = property.supercedes[s];
                          qLen = this.eventQueue.length;
  
                          for (q = 0; q < qLen; q++) {
                              queueItemCheck = this.eventQueue[q];
  
                              if (queueItemCheck) {
                                  queueItemCheckKey = queueItemCheck[0];
                                  queueItemCheckValue = queueItemCheck[1];
  
                                  if (queueItemCheckKey == 
                                      supercedesCheck.toLowerCase() ) {
  
                                      this.eventQueue.push([queueItemCheckKey, 
                                          queueItemCheckValue]);
  
                                      this.eventQueue[q] = null;
                                      break;
  
                                  }
                              }
                          }
                      }
                  }
  
  
                  return true;
              } else {
                  return false;
              }
          },
          
          /**
          * Fires the event for a property using the property's current value.
          * @method refireEvent
          * @param {String} key The name of the property
          */
          refireEvent: function (key) {
      
              key = key.toLowerCase();
          
              var property = this.config[key];
      
              if (property && property.event && 
      
                  !Lang.isUndefined(property.value)) {
      
                  if (this.queueInProgress) {
      
                      this.queueProperty(key);
      
                  } else {
      
                      this.fireEvent(key, property.value);
      
                  }
      
              }
          },
          
          /**
          * Applies a key-value Object literal to the configuration, replacing  
          * any existing values, and queueing the property events.
          * Although the values will be set, fireQueue() must be called for their 
          * associated events to execute.
          * @method applyConfig
          * @param {Object} userConfig The configuration Object literal
          * @param {Boolean} init  When set to true, the initialConfig will 
          * be set to the userConfig passed in, so that calling a reset will 
          * reset the properties to the passed values.
          */
          applyConfig: function (userConfig, init) {
          
              var sKey,
                  oConfig;
  
              if (init) {
                  oConfig = {};
                  for (sKey in userConfig) {
                      if (Lang.hasOwnProperty(userConfig, sKey)) {
                          oConfig[sKey.toLowerCase()] = userConfig[sKey];
                      }
                  }
                  this.initialConfig = oConfig;
              }
  
              for (sKey in userConfig) {
                  if (Lang.hasOwnProperty(userConfig, sKey)) {
                      this.queueProperty(sKey, userConfig[sKey]);
                  }
              }
          },
          
          /**
          * Refires the events for all configuration properties using their 
          * current values.
          * @method refresh
          */
          refresh: function () {
  
              var prop;
  
              for (prop in this.config) {
                  if (Lang.hasOwnProperty(this.config, prop)) {
                      this.refireEvent(prop);
                  }
              }
          },
          
          /**
          * Fires the normalized list of queued property change events
          * @method fireQueue
          */
          fireQueue: function () {
          
              var i, 
                  queueItem,
                  key,
                  value,
                  property;
          
              this.queueInProgress = true;
              for (i = 0;i < this.eventQueue.length; i++) {
                  queueItem = this.eventQueue[i];
                  if (queueItem) {
          
                      key = queueItem[0];
                      value = queueItem[1];
                      property = this.config[key];
          
                      property.value = value;
          
                      this.fireEvent(key,value);
                  }
              }
              
              this.queueInProgress = false;
              this.eventQueue = [];
          },
          
          /**
          * Subscribes an external handler to the change event for any 
          * given property. 
          * @method subscribeToConfigEvent
          * @param {String} key The property name
          * @param {Function} handler The handler function to use subscribe to 
          * the property's event
          * @param {Object} obj The Object to use for scoping the event handler 
          * (see CustomEvent documentation)
          * @param {Boolean} override Optional. If true, will override "this"  
          * within the handler to map to the scope Object passed into the method.
          * @return {Boolean} True, if the subscription was successful, 
          * otherwise false.
          */ 
          subscribeToConfigEvent: function (key, handler, obj, override) {
      
              var property = this.config[key.toLowerCase()];
      
              if (property && property.event) {
                  if (!Config.alreadySubscribed(property.event, handler, obj)) {
                      property.event.subscribe(handler, obj, override);
                  }
                  return true;
              } else {
                  return false;
              }
      
          },
          
          /**
          * Unsubscribes an external handler from the change event for any 
          * given property. 
          * @method unsubscribeFromConfigEvent
          * @param {String} key The property name
          * @param {Function} handler The handler function to use subscribe to 
          * the property's event
          * @param {Object} obj The Object to use for scoping the event 
          * handler (see CustomEvent documentation)
          * @return {Boolean} True, if the unsubscription was successful, 
          * otherwise false.
          */
          unsubscribeFromConfigEvent: function (key, handler, obj) {
              var property = this.config[key.toLowerCase()];
              if (property && property.event) {
                  return property.event.unsubscribe(handler, obj);
              } else {
                  return false;
              }
          },
          
          /**
          * Returns a string representation of the Config object
          * @method toString
          * @return {String} The Config object in string format.
          */
          toString: function () {
              var output = "Config";
              if (this.owner) {
                  output += " [" + this.owner.toString() + "]";
              }
              return output;
          },
          
          /**
          * Returns a string representation of the Config object's current 
          * CustomEvent queue
          * @method outputEventQueue
          * @return {String} The string list of CustomEvents currently queued 
          * for execution
          */
          outputEventQueue: function () {
  
              var output = "",
                  queueItem,
                  q,
                  nQueue = this.eventQueue.length;
                
              for (q = 0; q < nQueue; q++) {
                  queueItem = this.eventQueue[q];
                  if (queueItem) {
                      output += queueItem[0] + "=" + queueItem[1] + ", ";
                  }
              }
              return output;
          },
  
          /**
          * Sets all properties to null, unsubscribes all listeners from each 
          * property's change event and all listeners from the configChangedEvent.
          * @method destroy
          */
          destroy: function () {
  
              var oConfig = this.config,
                  sProperty,
                  oProperty;
  
  
              for (sProperty in oConfig) {
              
                  if (Lang.hasOwnProperty(oConfig, sProperty)) {
  
                      oProperty = oConfig[sProperty];
  
                      oProperty.event.unsubscribeAll();
                      oProperty.event = null;
  
                  }
              
              }
              
              this.configChangedEvent.unsubscribeAll();
              
              this.configChangedEvent = null;
              this.owner = null;
              this.config = null;
              this.initialConfig = null;
              this.eventQueue = null;
          
          }
  
      };
      
      
      
      /**
      * Checks to determine if a particular function/Object pair are already 
      * subscribed to the specified CustomEvent
      * @method YAHOO.util.Config.alreadySubscribed
      * @static
      * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check 
      * the subscriptions
      * @param {Function} fn The function to look for in the subscribers list
      * @param {Object} obj The execution scope Object for the subscription
      * @return {Boolean} true, if the function/Object pair is already subscribed 
      * to the CustomEvent passed in
      */
      Config.alreadySubscribed = function (evt, fn, obj) {
      
          var nSubscribers = evt.subscribers.length,
              subsc,
              i;
  
          if (nSubscribers > 0) {
              i = nSubscribers - 1;
              do {
                  subsc = evt.subscribers[i];
                  if (subsc && subsc.obj == obj && subsc.fn == fn) {
                      return true;
                  }
              }
              while (i--);
          }
  
          return false;
  
      };
  
      YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider);
  
  }());
  
  /**
  * YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility
  * used for adding, subtracting, and comparing dates.
  * @namespace YAHOO.widget
  * @class DateMath
  */
  YAHOO.widget.DateMath = {
  	/**
  	* Constant field representing Day
  	* @property DAY
  	* @static
  	* @final
  	* @type String
  	*/
  	DAY : "D",
  
  	/**
  	* Constant field representing Week
  	* @property WEEK
  	* @static
  	* @final
  	* @type String
  	*/
  	WEEK : "W",
  
  	/**
  	* Constant field representing Year
  	* @property YEAR
  	* @static
  	* @final
  	* @type String
  	*/
  	YEAR : "Y",
  
  	/**
  	* Constant field representing Month
  	* @property MONTH
  	* @static
  	* @final
  	* @type String
  	*/
  	MONTH : "M",
  
  	/**
  	* Constant field representing one day, in milliseconds
  	* @property ONE_DAY_MS
  	* @static
  	* @final
  	* @type Number
  	*/
  	ONE_DAY_MS : 1000*60*60*24,
  	
  	/**
  	 * Constant field representing the date in first week of January
  	 * which identifies the first week of the year.
  	 * <p>
  	 * In the U.S, Jan 1st is normally used based on a Sunday start of week.
  	 * ISO 8601, used widely throughout Europe, uses Jan 4th, based on a Monday start of week.
  	 * </p>
  	 * @property WEEK_ONE_JAN_DATE
  	 * @static
  	 * @type Number
  	 */
  	WEEK_ONE_JAN_DATE : 1,
  
  	/**
  	* Adds the specified amount of time to the this instance.
  	* @method add
  	* @param {Date} date	The JavaScript Date object to perform addition on
  	* @param {String} field	The field constant to be used for performing addition.
  	* @param {Number} amount	The number of units (measured in the field constant) to add to the date.
  	* @return {Date} The resulting Date object
  	*/
  	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:
  				this._addDays(d, amount);
  				// d.setDate(date.getDate() + amount);
  				break;
  			case this.YEAR:
  				d.setFullYear(date.getFullYear() + amount);
  				break;
  			case this.WEEK:
  				this._addDays(d, (amount * 7));
  				// d.setDate(date.getDate() + (amount * 7));
  				break;
  		}
  		return d;
  	},
  
  	/**
  	 * Private helper method to account for bug in Safari 2 (webkit < 420)
  	 * when Date.setDate(n) is called with n less than -128 or greater than 127.
  	 * <p>
  	 * Fix approach and original findings are available here:
  	 * http://brianary.blogspot.com/2006/03/safari-date-bug.html
  	 * </p>
  	 * @method _addDays
  	 * @param {Date} d JavaScript date object
  	 * @param {Number} nDays The number of days to add to the date object (can be negative)
  	 * @private
  	 */
  	_addDays : function(d, nDays) {
  		if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420) {
  			if (nDays < 0) {
  				// Ensure we don't go below -128 (getDate() is always 1 to 31, so we won't go above 127)
  				for(var min = -128; nDays < min; nDays -= min) {
  					d.setDate(d.getDate() + min);
  				}
  			} else {
  				// Ensure we don't go above 96 + 31 = 127
  				for(var max = 96; nDays > max; nDays -= max) {
  					d.setDate(d.getDate() + max);
  				}
  			}
  			// nDays should be remainder between -128 and 96
  		}
  		d.setDate(d.getDate() + nDays);
  	},
  
  	/**
  	* Subtracts the specified amount of time from the this instance.
  	* @method subtract
  	* @param {Date} date	The JavaScript Date object to perform subtraction on
  	* @param {Number} field	The this field constant to be used for performing subtraction.
  	* @param {Number} amount	The number of units (measured in the field constant) to subtract from the date.
  	* @return {Date} The resulting Date object
  	*/
  	subtract : function(date, field, amount) {
  		return this.add(date, field, (amount*-1));
  	},
  
  	/**
  	* Determines whether a given date is before another date on the calendar.
  	* @method before
  	* @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.
  	*/
  	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.
  	* @method after
  	* @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.
  	*/
  	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.
  	* @method between
  	* @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.
  	*/
  	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.
  	* @method getJan1
  	* @param {Number} calendarYear		The calendar year for which to retrieve January 1
  	* @return {Date}	January 1 of the calendar year specified.
  	*/
  	getJan1 : function(calendarYear) {
  		return this.getDate(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.
  	* @method getDayOffset
  	* @param {Date}	date	The JavaScript date for which to find the offset
  	* @param {Number} calendarYear	The calendar year to use for determining the offset
  	* @return {Number}	The number of days since January 1 of the given year
  	*/
  	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. Can currently support standard
  	* U.S. week numbers, based on Jan 1st defining the 1st week of the year, and 
  	* ISO8601 week numbers, based on Jan 4th defining the 1st week of the year.
  	* 
  	* @method getWeekNumber
  	* @param {Date}	date The JavaScript date for which to find the week number
  	* @param {Number} firstDayOfWeek The index of the first day of the week (0 = Sun, 1 = Mon ... 6 = Sat).
  	* Defaults to 0
  	* @param {Number} janDate The date in the first week of January which defines week one for the year
  	* Defaults to the value of YAHOO.widget.DateMath.WEEK_ONE_JAN_DATE, which is 1 (Jan 1st). 
  	* For the U.S, this is normally Jan 1st. ISO8601 uses Jan 4th to define the first week of the year.
  	* 
  	* @return {Number} The number of the week containing the given date.
  	*/
  	getWeekNumber : function(date, firstDayOfWeek, janDate) {
  
  		// Setup Defaults
  		firstDayOfWeek = firstDayOfWeek || 0;
  		janDate = janDate || this.WEEK_ONE_JAN_DATE;
  
  		var targetDate = this.clearTime(date),
  			startOfWeek,
  			endOfWeek;
  
  		if (targetDate.getDay() === firstDayOfWeek) { 
  			startOfWeek = targetDate;
  		} else {
  			startOfWeek = this.getFirstDayOfWeek(targetDate, firstDayOfWeek);
  		}
  
  		var startYear = startOfWeek.getFullYear(),
  			startTime = startOfWeek.getTime();
  
  		// DST shouldn't be a problem here, math is quicker than setDate();
  		endOfWeek = new Date(startOfWeek.getTime() + 6*this.ONE_DAY_MS);
  
  		var weekNum;
  		if (startYear !== endOfWeek.getFullYear() && endOfWeek.getDate() >= janDate) {
  			// If years don't match, endOfWeek is in Jan. and if the 
  			// week has WEEK_ONE_JAN_DATE in it, it's week one by definition.
  			weekNum = 1;
  		} else {
  			// Get the 1st day of the 1st week, and 
  			// find how many days away we are from it.
  			var weekOne = this.clearTime(this.getDate(startYear, 0, janDate)),
  				weekOneDayOne = this.getFirstDayOfWeek(weekOne, firstDayOfWeek);
  
  			// Round days to smoothen out 1 hr DST diff
  			var daysDiff  = Math.round((targetDate.getTime() - weekOneDayOne.getTime())/this.ONE_DAY_MS);
  
  			// Calc. Full Weeks
  			var rem = daysDiff % 7;
  			var weeksDiff = (daysDiff - rem)/7;
  			weekNum = weeksDiff + 1;
  		}
  		return weekNum;
  	},
  
  	/**
  	 * Get the first day of the week, for the give date. 
  	 * @param {Date} dt The date in the week for which the first day is required.
  	 * @param {Number} startOfWeek The index for the first day of the week, 0 = Sun, 1 = Mon ... 6 = Sat (defaults to 0)
  	 * @return {Date} The first day of the week
  	 */
  	getFirstDayOfWeek : function (dt, startOfWeek) {
  		startOfWeek = startOfWeek || 0;
  		var dayOfWeekIndex = dt.getDay(),
  			dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7;
  
  		return this.subtract(dt, this.DAY, dayOfWeek);
  	},
  
  	/**
  	* Determines if a given week overlaps two different years.
  	* @method isYearOverlapWeek
  	* @param {Date}	weekBeginDate	The JavaScript Date representing the first day of the week.
  	* @return {Boolean}	true if the date overlaps two different years.
  	*/
  	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.
  	* @method isMonthOverlapWeek
  	* @param {Date}	weekBeginDate	The JavaScript Date representing the first day of the week.
  	* @return {Boolean}	true if the date overlaps two different months.
  	*/
  	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.
  	* @method findMonthStart
  	* @param {Date}	date	The JavaScript Date used to calculate the month start
  	* @return {Date}		The JavaScript Date representing the first day of the month
  	*/
  	findMonthStart : function(date) {
  		var start = this.getDate(date.getFullYear(), date.getMonth(), 1);
  		return start;
  	},
  
  	/**
  	* Gets the last day of a month containing a given date.
  	* @method findMonthEnd
  	* @param {Date}	date	The JavaScript Date used to calculate the month end
  	* @return {Date}		The JavaScript Date representing the last day of the month
  	*/
  	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 12 noon.
  	* @method clearTime
  	* @param {Date}	date	The JavaScript Date for which the time fields will be cleared
  	* @return {Date}		The JavaScript Date cleared of all time fields
  	*/
  	clearTime : function(date) {
  		date.setHours(12,0,0,0);
  		return date;
  	},
  
  	/**
  	 * Returns a new JavaScript Date object, representing the given year, month and date. Time fields (hr, min, sec, ms) on the new Date object
  	 * are set to 0. The method allows Date instances to be created with the a year less than 100. "new Date(year, month, date)" implementations 
  	 * set the year to 19xx if a year (xx) which is less than 100 is provided.
  	 * <p>
  	 * <em>NOTE:</em>Validation on argument values is not performed. It is the caller's responsibility to ensure
  	 * arguments are valid as per the ECMAScript-262 Date object specification for the new Date(year, month[, date]) constructor.
  	 * </p>
  	 * @method getDate
  	 * @param {Number} y Year.
  	 * @param {Number} m Month index from 0 (Jan) to 11 (Dec).
  	 * @param {Number} d (optional) Date from 1 to 31. If not provided, defaults to 1.
  	 * @return {Date} The JavaScript date object with year, month, date set as provided.
  	 */
  	getDate : function(y, m, d) {
  		var dt = null;
  		if (YAHOO.lang.isUndefined(d)) {
  			d = 1;
  		}
  		if (y >= 100) {
  			dt = new Date(y, m, d);
  		} else {
  			dt = new Date();
  			dt.setFullYear(y);
  			dt.setMonth(m);
  			dt.setDate(d);
  			dt.setHours(0,0,0,0);
  		}
  		return dt;
  	}
  };
  
  /**
  * The Calendar component is a UI control that enables users to choose one or more dates from a graphical calendar presented in a one-month or
  * multi-month interface. Calendars are generated entirely via script and can be navigated without any page refreshes.
  * @module    calendar
  * @title    Calendar
  * @namespace  YAHOO.widget
  * @requires  yahoo,dom,event
  */
  (function(){
  
  	var Dom = YAHOO.util.Dom,
  		Event = YAHOO.util.Event,
  		Lang = YAHOO.lang,
  		DateMath = YAHOO.widget.DateMath;
  
  /**
  * Calendar 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>To construct the placeholder for the calendar widget, the code is as
  * follows:
  *	<xmp>
  *		<div id="calContainer"></div>
  *	</xmp>
  * </p>
  * <p>
  * <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
  * The Calendar can be constructed by simply providing a container ID string, 
  * or a reference to a container DIV HTMLElement (the element needs to exist 
  * in the document).
  * 
  * E.g.:
  *	<xmp>
  *		var c = new YAHOO.widget.Calendar("calContainer", configOptions);
  *	</xmp>
  * or:
  *   <xmp>
  *       var containerDiv = YAHOO.util.Dom.get("calContainer");
  *		var c = new YAHOO.widget.Calendar(containerDiv, configOptions);
  *	</xmp>
  * </p>
  * <p>
  * If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
  * For example if an ID is not provided, and the container's ID is "calContainer", the Calendar's ID will be set to "calContainer_t".
  * </p>
  * 
  * @namespace YAHOO.widget
  * @class Calendar
  * @constructor
  * @param {String} id optional The id of the table element that will represent the Calendar widget. As of 2.4.0, this argument is optional.
  * @param {String | HTMLElement} container The id of the container div element that will wrap the Calendar table, or a reference to a DIV element which exists in the document.
  * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
  */
  function Calendar(id, containerId, config) {
  	this.init.apply(this, arguments);
  }
  
  /**
  * The path to be used for images loaded for the Calendar
  * @property YAHOO.widget.Calendar.IMG_ROOT
  * @static
  * @deprecated	You can now customize images by overriding the calclose, calnavleft and calnavright default CSS classes for the close icon, left arrow and right arrow respectively
  * @type String
  */
  Calendar.IMG_ROOT = null;
  
  /**
  * Type constant used for renderers to represent an individual date (M/D/Y)
  * @property YAHOO.widget.Calendar.DATE
  * @static
  * @final
  * @type String
  */
  Calendar.DATE = "D";
  
  /**
  * Type constant used for renderers to represent an individual date across any year (M/D)
  * @property YAHOO.widget.Calendar.MONTH_DAY
  * @static
  * @final
  * @type String
  */
  Calendar.MONTH_DAY = "MD";
  
  /**
  * Type constant used for renderers to represent a weekday
  * @property YAHOO.widget.Calendar.WEEKDAY
  * @static
  * @final
  * @type String
  */
  Calendar.WEEKDAY = "WD";
  
  /**
  * Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
  * @property YAHOO.widget.Calendar.RANGE
  * @static
  * @final
  * @type String
  */
  Calendar.RANGE = "R";
  
  /**
  * Type constant used for renderers to represent a month across any year
  * @property YAHOO.widget.Calendar.MONTH
  * @static
  * @final
  * @type String
  */
  Calendar.MONTH = "M";
  
  /**
  * Constant that represents the total number of date cells that are displayed in a given month
  * @property YAHOO.widget.Calendar.DISPLAY_DAYS
  * @static
  * @final
  * @type Number
  */
  Calendar.DISPLAY_DAYS = 42;
  
  /**
  * Constant used for halting the execution of the remainder of the render stack
  * @property YAHOO.widget.Calendar.STOP_RENDER
  * @static
  * @final
  * @type String
  */
  Calendar.STOP_RENDER = "S";
  
  /**
  * Constant used to represent short date field string formats (e.g. Tu or Feb)
  * @property YAHOO.widget.Calendar.SHORT
  * @static
  * @final
  * @type String
  */
  Calendar.SHORT = "short";
  
  /**
  * Constant used to represent long date field string formats (e.g. Monday or February)
  * @property YAHOO.widget.Calendar.LONG
  * @static
  * @final
  * @type String
  */
  Calendar.LONG = "long";
  
  /**
  * Constant used to represent medium date field string formats (e.g. Mon)
  * @property YAHOO.widget.Calendar.MEDIUM
  * @static
  * @final
  * @type String
  */
  Calendar.MEDIUM = "medium";
  
  /**
  * Constant used to represent single character date field string formats (e.g. M, T, W)
  * @property YAHOO.widget.Calendar.ONE_CHAR
  * @static
  * @final
  * @type String
  */
  Calendar.ONE_CHAR = "1char";
  
  /**
  * The set of default Config property keys and values for the Calendar
  * @property YAHOO.widget.Calendar._DEFAULT_CONFIG
  * @final
  * @static
  * @private
  * @type Object
  */
  Calendar._DEFAULT_CONFIG = {
  	// Default values for pagedate and selected are not class level constants - they are set during instance creation 
  	PAGEDATE : {key:"pagedate", value:null},
  	SELECTED : {key:"selected", value:null},
  	TITLE : {key:"title", value:""},
  	CLOSE : {key:"close", value:false},
  	IFRAME : {key:"iframe", value:(YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) ? true : false},
  	MINDATE : {key:"mindate", value:null},
  	MAXDATE : {key:"maxdate", value:null},
  	MULTI_SELECT : {key:"multi_select", value:false},
  	START_WEEKDAY : {key:"start_weekday", value:0},
  	SHOW_WEEKDAYS : {key:"show_weekdays", value:true},
  	SHOW_WEEK_HEADER : {key:"show_week_header", value:false},
  	SHOW_WEEK_FOOTER : {key:"show_week_footer", value:false},
  	HIDE_BLANK_WEEKS : {key:"hide_blank_weeks", value:false},
  	NAV_ARROW_LEFT: {key:"nav_arrow_left", value:null} ,
  	NAV_ARROW_RIGHT : {key:"nav_arrow_right", value:null} ,
  	MONTHS_SHORT : {key:"months_short", value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]},
  	MONTHS_LONG: {key:"months_long", value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]},
  	WEEKDAYS_1CHAR: {key:"weekdays_1char", value:["S", "M", "T", "W", "T", "F", "S"]},
  	WEEKDAYS_SHORT: {key:"weekdays_short", value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]},
  	WEEKDAYS_MEDIUM: {key:"weekdays_medium", value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]},
  	WEEKDAYS_LONG: {key:"weekdays_long", value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]},
  	LOCALE_MONTHS:{key:"locale_months", value:"long"},
  	LOCALE_WEEKDAYS:{key:"locale_weekdays", value:"short"},
  	DATE_DELIMITER:{key:"date_delimiter", value:","},
  	DATE_FIELD_DELIMITER:{key:"date_field_delimiter", value:"/"},
  	DATE_RANGE_DELIMITER:{key:"date_range_delimiter", value:"-"},
  	MY_MONTH_POSITION:{key:"my_month_position", value:1},
  	MY_YEAR_POSITION:{key:"my_year_position", value:2},
  	MD_MONTH_POSITION:{key:"md_month_position", value:1},
  	MD_DAY_POSITION:{key:"md_day_position", value:2},
  	MDY_MONTH_POSITION:{key:"mdy_month_position", value:1},
  	MDY_DAY_POSITION:{key:"mdy_day_position", value:2},
  	MDY_YEAR_POSITION:{key:"mdy_year_position", value:3},
  	MY_LABEL_MONTH_POSITION:{key:"my_label_month_position", value:1},
  	MY_LABEL_YEAR_POSITION:{key:"my_label_year_position", value:2},
  	MY_LABEL_MONTH_SUFFIX:{key:"my_label_month_suffix", value:" "},
  	MY_LABEL_YEAR_SUFFIX:{key:"my_label_year_suffix", value:""},
  	NAV: {key:"navigator", value: null},
  	STRINGS : { 
  		key:"strings",
  		value: {
  			previousMonth : "Previous Month",
  			nextMonth : "Next Month",
  			close: "Close"
  		},
  		supercedes : ["close", "title"]
  	}
  };
  
  var DEF_CFG = Calendar._DEFAULT_CONFIG;
  
  /**
  * The set of Custom Event types supported by the Calendar
  * @property YAHOO.widget.Calendar._EVENT_TYPES
  * @final
  * @static
  * @private
  * @type Object
  */
  Calendar._EVENT_TYPES = {
  	BEFORE_SELECT : "beforeSelect", 
  	SELECT : "select",
  	BEFORE_DESELECT : "beforeDeselect",
  	DESELECT : "deselect",
  	CHANGE_PAGE : "changePage",
  	BEFORE_RENDER : "beforeRender",
  	RENDER : "render",
  	BEFORE_DESTROY : "beforeDestroy",
  	DESTROY : "destroy",
  	RESET : "reset",
  	CLEAR : "clear",
  	BEFORE_HIDE : "beforeHide",
  	HIDE : "hide",
  	BEFORE_SHOW : "beforeShow",
  	SHOW : "show",
  	BEFORE_HIDE_NAV : "beforeHideNav",
  	HIDE_NAV : "hideNav",
  	BEFORE_SHOW_NAV : "beforeShowNav",
  	SHOW_NAV : "showNav",
  	BEFORE_RENDER_NAV : "beforeRenderNav",
  	RENDER_NAV : "renderNav"
  };
  
  /**
  * The set of default style constants for the Calendar
  * @property YAHOO.widget.Calendar._STYLES
  * @final
  * @static
  * @private
  * @type Object
  */
  Calendar._STYLES = {
  	CSS_ROW_HEADER: "calrowhead",
  	CSS_ROW_FOOTER: "calrowfoot",
  	CSS_CELL : "calcell",
  	CSS_CELL_SELECTOR : "selector",
  	CSS_CELL_SELECTED : "selected",
  	CSS_CELL_SELECTABLE : "selectable",
  	CSS_CELL_RESTRICTED : "restricted",
  	CSS_CELL_TODAY : "today",
  	CSS_CELL_OOM : "oom",
  	CSS_CELL_OOB : "previous",
  	CSS_HEADER : "calheader",
  	CSS_HEADER_TEXT : "calhead",
  	CSS_BODY : "calbody",
  	CSS_WEEKDAY_CELL : "calweekdaycell",
  	CSS_WEEKDAY_ROW : "calweekdayrow",
  	CSS_FOOTER : "calfoot",
  	CSS_CALENDAR : "yui-calendar",
  	CSS_SINGLE : "single",
  	CSS_CONTAINER : "yui-calcontainer",
  	CSS_NAV_LEFT : "calnavleft",
  	CSS_NAV_RIGHT : "calnavright",
  	CSS_NAV : "calnav",
  	CSS_CLOSE : "calclose",
  	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"
  };
  
  Calendar.prototype = {
  
  	/**
  	* The configuration object used to set up the calendars various locale and style options.
  	* @property Config
  	* @private
  	* @deprecated Configuration properties should be set by calling Calendar.cfg.setProperty.
  	* @type Object
  	*/
  	Config : null,
  
  	/**
  	* The parent CalendarGroup, only to be set explicitly by the parent group
  	* @property parent
  	* @type CalendarGroup
  	*/	
  	parent : null,
  
  	/**
  	* The index of this item in the parent group
  	* @property index
  	* @type Number
  	*/
  	index : -1,
  
  	/**
  	* The collection of calendar table cells
  	* @property cells
  	* @type HTMLTableCellElement[]
  	*/
  	cells : 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].
  	* @property cellDates
  	* @type Array[](Number[])
  	*/
  	cellDates : null,
  
  	/**
  	* The id that uniquely identifies this Calendar.
  	* @property id
  	* @type String
  	*/
  	id : null,
  
  	/**
  	* The unique id associated with the Calendar's container
  	* @property containerId
  	* @type String
  	*/
  	containerId: 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.
  	* @property oDomContainer
  	* @type HTMLElement
  	*/
  	oDomContainer : null,
  
  	/**
  	* A Date object representing today's date.
  	* @property today
  	* @type Date
  	*/
  	today : null,
  
  	/**
  	* The list of render functions, along with required parameters, used to render cells. 
  	* @property renderStack
  	* @type Array[]
  	*/
  	renderStack : null,
  
  	/**
  	* A copy of the initial render functions created before rendering.
  	* @property _renderStack
  	* @private
  	* @type Array
  	*/
  	_renderStack : null,
  
  	/**
  	* A reference to the CalendarNavigator instance created for this Calendar.
  	* Will be null if the "navigator" configuration property has not been set
  	* @property oNavigator
  	* @type CalendarNavigator
  	*/
  	oNavigator : null,
  
  	/**
  	* The private list of initially selected dates.
  	* @property _selectedDates
  	* @private
  	* @type Array
  	*/
  	_selectedDates : null,
  
  	/**
  	* A map of DOM event handlers to attach to cells associated with specific CSS class names
  	* @property domEventMap
  	* @type Object
  	*/
  	domEventMap : null,
  
  	/**
  	 * Protected helper used to parse Calendar constructor/init arguments.
  	 *
  	 * As of 2.4.0, Calendar supports a simpler constructor 
  	 * signature. This method reconciles arguments
  	 * received in the pre 2.4.0 and 2.4.0 formats.
  	 * 
  	 * @protected
  	 * @method _parseArgs
  	 * @param {Array} Function "arguments" array
  	 * @return {Object} Object with id, container, config properties containing
  	 * the reconciled argument values.
  	 **/
  	_parseArgs : function(args) {
  		/*
  		   2.4.0 Constructors signatures
  
  		   new Calendar(String)
  		   new Calendar(HTMLElement)
  		   new Calendar(String, ConfigObject)
  		   new Calendar(HTMLElement, ConfigObject)
  
  		   Pre 2.4.0 Constructor signatures
  
  		   new Calendar(String, String)
  		   new Calendar(String, HTMLElement)
  		   new Calendar(String, String, ConfigObject)
  		   new Calendar(String, HTMLElement, ConfigObject)
  		 */
  		var nArgs = {id:null, container:null, config:null};
  
  		if (args && args.length && args.length > 0) {
  			switch (args.length) {
  				case 1:
  					nArgs.id = null;
  					nArgs.container = args[0];
  					nArgs.config = null;
  					break;
  				case 2:
  					if (Lang.isObject(args[1]) && !args[1].tagName && !(args[1] instanceof String)) {
  						nArgs.id = null;
  						nArgs.container = args[0];
  						nArgs.config = args[1];
  					} else {
  						nArgs.id = args[0];
  						nArgs.container = args[1];
  						nArgs.config = null;
  					}
  					break;
  				default: // 3+
  					nArgs.id = args[0];
  					nArgs.container = args[1];
  					nArgs.config = args[2];
  					break;
  			}
  		} else {
  		}
  		return nArgs;
  	},
  
  	/**
  	* Initializes the Calendar widget.
  	* @method init
  	*
  	* @param {String} id optional The id of the table element that will represent the Calendar widget. As of 2.4.0, this argument is optional.
  	* @param {String | HTMLElement} container The id of the container div element that will wrap the Calendar table, or a reference to a DIV element which exists in the document.
  	* @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
  	*/
  	init : function(id, container, config) {
  		// Normalize 2.4.0, pre 2.4.0 args
  		var nArgs = this._parseArgs(arguments);
  
  		id = nArgs.id;
  		container = nArgs.container;
  		config = nArgs.config;
  
  		this.oDomContainer = Dom.get(container);
  
  		if (!this.oDomContainer.id) {
  			this.oDomContainer.id = Dom.generateId();
  		}
  		if (!id) {
  			id = this.oDomContainer.id + "_t";
  		}
  
  		this.id = id;
  		this.containerId = this.oDomContainer.id;
  
  		this.initEvents();
  
  		this.today = new Date();
  		DateMath.clearTime(this.today);
  
  		/**
  		* The Config object used to hold the configuration variables for the Calendar
  		* @property cfg
  		* @type YAHOO.util.Config
  		*/
  		this.cfg = new YAHOO.util.Config(this);
  
  		/**
  		* The local object which contains the Calendar's options
  		* @property Options
  		* @type Object
  		*/
  		this.Options = {};
  
  		/**
  		* The local object which contains the Calendar's locale settings
  		* @property Locale
  		* @type Object
  		*/
  		this.Locale = {};
  
  		this.initStyles();
  
  		Dom.addClass(this.oDomContainer, this.Style.CSS_CONTAINER);
  		Dom.addClass(this.oDomContainer, this.Style.CSS_SINGLE);
  
  		this.cellDates = [];
  		this.cells = [];
  		this.renderStack = [];
  		this._renderStack = [];
  
  		this.setupConfig();
  
  		if (config) {
  			this.cfg.applyConfig(config, true);
  		}
  
  		this.cfg.fireQueue();
  	},
  
  	/**
  	* Default Config listener for the iframe property. If the iframe config property is set to true, 
  	* renders the built-in IFRAME shim if the container is relatively or absolutely positioned.
  	* 
  	* @method configIframe
  	*/
  	configIframe : function(type, args, obj) {
  		var useIframe = args[0];
  	
  		if (!this.parent) {
  			if (Dom.inDocument(this.oDomContainer)) {
  				if (useIframe) {
  					var pos = Dom.getStyle(this.oDomContainer, "position");
  					
  					if (pos == "absolute" || pos == "relative") {
  						
  						if (!Dom.inDocument(this.iframe)) {
  							this.iframe = document.createElement("iframe");
  							this.iframe.src = "javascript:false;";
  	
  							Dom.setStyle(this.iframe, "opacity", "0");
  	
  							if (YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) {
  								Dom.addClass(this.iframe, "fixedsize");
  							}
  	
  							this.oDomContainer.insertBefore(this.iframe, this.oDomContainer.firstChild);
  						}
  					}
  				} else {
  					if (this.iframe) {
  						if (this.iframe.parentNode) {
  							this.iframe.parentNode.removeChild(this.iframe);
  						}
  						this.iframe = null;
  					}
  				}
  			}
  		}
  	},
  
  	/**
  	* Default handler for the "title" property
  	* @method configTitle
  	*/
  	configTitle : function(type, args, obj) {
  		var title = args[0];
  
  		// "" disables title bar
  		if (title) {
  			this.createTitleBar(title);
  		} else {
  			var close = this.cfg.getProperty(DEF_CFG.CLOSE.key);
  			if (!close) {
  				this.removeTitleBar();
  			} else {
  				this.createTitleBar("&#160;");
  			}
  		}
  	},
  	
  	/**
  	* Default handler for the "close" property
  	* @method configClose
  	*/
  	configClose : function(type, args, obj) {
  		var close = args[0],
  			title = this.cfg.getProperty(DEF_CFG.TITLE.key);
  	
  		if (close) {
  			if (!title) {
  				this.createTitleBar("&#160;");
  			}
  			this.createCloseButton();
  		} else {
  			this.removeCloseButton();
  			if (!title) {
  				this.removeTitleBar();
  			}
  		}
  	},
  
  	/**
  	* Initializes Calendar's built-in CustomEvents
  	* @method initEvents
  	*/
  	initEvents : function() {
  
  		var defEvents = Calendar._EVENT_TYPES,
  			CE = YAHOO.util.CustomEvent,
  			cal = this; // To help with minification
  
  		/**
  		* Fired before a selection is made
  		* @event beforeSelectEvent
  		*/
  		cal.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT); 
  
  		/**
  		* Fired when a selection is made
  		* @event selectEvent
  		* @param {Array}	Array of Date field arrays in the format [YYYY, MM, DD].
  		*/
  		cal.selectEvent = new CE(defEvents.SELECT);
  	
  		/**
  		* Fired before a selection is made
  		* @event beforeDeselectEvent
  		*/
  		cal.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
  	
  		/**
  		* Fired when a selection is made
  		* @event deselectEvent
  		* @param {Array}	Array of Date field arrays in the format [YYYY, MM, DD].
  		*/
  		cal.deselectEvent = new CE(defEvents.DESELECT);
  	
  		/**
  		* Fired when the Calendar page is changed
  		* @event changePageEvent
  		*/
  		cal.changePageEvent = new CE(defEvents.CHANGE_PAGE);
  	
  		/**
  		* Fired before the Calendar is rendered
  		* @event beforeRenderEvent
  		*/
  		cal.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
  	
  		/**
  		* Fired when the Calendar is rendered
  		* @event renderEvent
  		*/
  		cal.renderEvent = new CE(defEvents.RENDER);
  
  		/**
  		* Fired just before the Calendar is to be destroyed
  		* @event beforeDestroyEvent
  		*/
  		cal.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
  
  		/**
  		* Fired after the Calendar is destroyed. This event should be used
  		* for notification only. When this event is fired, important Calendar instance
  		* properties, dom references and event listeners have already been 
  		* removed/dereferenced, and hence the Calendar instance is not in a usable 
  		* state.
  		*
  		* @event destroyEvent
  		*/
  		cal.destroyEvent = new CE(defEvents.DESTROY);
  
  		/**
  		* Fired when the Calendar is reset
  		* @event resetEvent
  		*/
  		cal.resetEvent = new CE(defEvents.RESET);
  
  		/**
  		* Fired when the Calendar is cleared
  		* @event clearEvent
  		*/
  		cal.clearEvent = new CE(defEvents.CLEAR);
  
  		/**
  		* Fired just before the Calendar is to be shown
  		* @event beforeShowEvent
  		*/
  		cal.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
  
  		/**
  		* Fired after the Calendar is shown
  		* @event showEvent
  		*/
  		cal.showEvent = new CE(defEvents.SHOW);
  
  		/**
  		* Fired just before the Calendar is to be hidden
  		* @event beforeHideEvent
  		*/
  		cal.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
  
  		/**
  		* Fired after the Calendar is hidden
  		* @event hideEvent
  		*/
  		cal.hideEvent = new CE(defEvents.HIDE);
  
  		/**
  		* Fired just before the CalendarNavigator is to be shown
  		* @event beforeShowNavEvent
  		*/
  		cal.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
  	
  		/**
  		* Fired after the CalendarNavigator is shown
  		* @event showNavEvent
  		*/
  		cal.showNavEvent = new CE(defEvents.SHOW_NAV);
  	
  		/**
  		* Fired just before the CalendarNavigator is to be hidden
  		* @event beforeHideNavEvent
  		*/
  		cal.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
  	
  		/**
  		* Fired after the CalendarNavigator is hidden
  		* @event hideNavEvent
  		*/
  		cal.hideNavEvent = new CE(defEvents.HIDE_NAV);
  
  		/**
  		* Fired just before the CalendarNavigator is to be rendered
  		* @event beforeRenderNavEvent
  		*/
  		cal.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
  
  		/**
  		* Fired after the CalendarNavigator is rendered
  		* @event renderNavEvent
  		*/
  		cal.renderNavEvent = new CE(defEvents.RENDER_NAV);
  
  		cal.beforeSelectEvent.subscribe(cal.onBeforeSelect, this, true);
  		cal.selectEvent.subscribe(cal.onSelect, this, true);
  		cal.beforeDeselectEvent.subscribe(cal.onBeforeDeselect, this, true);
  		cal.deselectEvent.subscribe(cal.onDeselect, this, true);
  		cal.changePageEvent.subscribe(cal.onChangePage, this, true);
  		cal.renderEvent.subscribe(cal.onRender, this, true);
  		cal.resetEvent.subscribe(cal.onReset, this, true);
  		cal.clearEvent.subscribe(cal.onClear, this, true);
  	},
  
  	/**
  	* The default event handler for clicks on the "Previous Month" navigation UI
  	*
  	* @method doPreviousMonthNav
  	* @param {DOMEvent} e	The DOM event
  	* @param {Calendar} cal	A reference to the calendar
  	*/
  	doPreviousMonthNav : function(e, cal) {
  		Event.preventDefault(e);
  		// previousMonth invoked in a timeout, to allow
  		// event to bubble up, with correct target. Calling
  		// previousMonth, will call render which will remove 
  		// HTML which generated the event, resulting in an 
  		// invalid event target in certain browsers.
  		setTimeout(function() {
  			cal.previousMonth();
  			var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_LEFT, "a", cal.oDomContainer);
  			if (navs && navs[0]) {
  				try {
  					navs[0].focus();
  				} catch (e) {
  					// ignore
  				}
  			}
  		}, 0);
  	},
  
  	/**
  	 * The default event handler for clicks on the "Next Month" navigation UI
  	 *
  	 * @method doNextMonthNav
  	 * @param {DOMEvent} e	The DOM event
  	 * @param {Calendar} cal	A reference to the calendar
  	 */
  	doNextMonthNav : function(e, cal) {
  		Event.preventDefault(e);
  		setTimeout(function() {
  			cal.nextMonth();
  			var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_RIGHT, "a", cal.oDomContainer);
  			if (navs && navs[0]) {
  				try {
  					navs[0].focus();
  				} catch (e) {
  					// ignore
  				}
  			}
  		}, 0);
  	},
  
  	/**
  	* The default event handler for date cell selection. Currently attached to 
  	* the Calendar's bounding box, referenced by it's <a href="#property_oDomContainer">oDomContainer</a> property.
  	*
  	* @method doSelectCell
  	* @param {DOMEvent} e	The DOM event
  	* @param {Calendar} cal	A reference to the calendar
  	*/
  	doSelectCell : function(e, cal) {
  		var cell, d, date, index;
  
  		var target = Event.getTarget(e),
  			tagName = target.tagName.toLowerCase(),
  			defSelector = false;
  
  		while (tagName != "td" && !Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
  
  			if (!defSelector && tagName == "a" && Dom.hasClass(target, cal.Style.CSS_CELL_SELECTOR)) {
  				defSelector = true;
  			}
  
  			target = target.parentNode;
  			tagName = target.tagName.toLowerCase();
  
  			if (target == this.oDomContainer || tagName == "html") {
  				return;
  			}
  		}
  
  		if (defSelector) {
  			// Stop link href navigation for default renderer
  			Event.preventDefault(e);
  		}
  	
  		cell = target;
  
  		if (Dom.hasClass(cell, cal.Style.CSS_CELL_SELECTABLE)) {
  			index = cal.getIndexFromId(cell.id);
  			if (index > -1) {
  				d = cal.cellDates[index];
  				if (d) {
  					date = DateMath.getDate(d[0],d[1]-1,d[2]);
  				
  					var link;
  
  					if (cal.Options.MULTI_SELECT) {
  						link = cell.getElementsByTagName("a")[0];
  						if (link) {
  							link.blur();
  						}
  
  						var cellDate = cal.cellDates[index];
  						var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
  
  						if (cellDateIndex > -1) {	
  							cal.deselectCell(index);
  						} else {
  							cal.selectCell(index);
  						}	
  
  					} else {
  						link = cell.getElementsByTagName("a")[0];
  						if (link) {
  							link.blur();
  						}
  						cal.selectCell(index);
  					}
  				}
  			}
  		}
  	},
  
  	/**
  	* The event that is executed when the user hovers over a cell
  	* @method doCellMouseOver
  	* @param {DOMEvent} e	The event
  	* @param {Calendar} cal	A reference to the calendar passed by the Event utility
  	*/
  	doCellMouseOver : function(e, cal) {
  		var target;
  		if (e) {
  			target = Event.getTarget(e);
  		} else {
  			target = this;
  		}
  
  		while (target.tagName && target.tagName.toLowerCase() != "td") {
  			target = target.parentNode;
  			if (!target.tagName || target.tagName.toLowerCase() == "html") {
  				return;
  			}
  		}
  
  		if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
  			Dom.addClass(target, cal.Style.CSS_CELL_HOVER);
  		}
  	},
  
  	/**
  	* The event that is executed when the user moves the mouse out of a cell
  	* @method doCellMouseOut
  	* @param {DOMEvent} e	The event
  	* @param {Calendar} cal	A reference to the calendar passed by the Event utility
  	*/
  	doCellMouseOut : function(e, cal) {
  		var target;
  		if (e) {
  			target = Event.getTarget(e);
  		} else {
  			target = this;
  		}
  
  		while (target.tagName && target.tagName.toLowerCase() != "td") {
  			target = target.parentNode;
  			if (!target.tagName || target.tagName.toLowerCase() == "html") {
  				return;
  			}
  		}
  
  		if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
  			Dom.removeClass(target, cal.Style.CSS_CELL_HOVER);
  		}
  	},
  
  	setupConfig : function() {
  		var cfg = this.cfg;
  
  		/**
  		* The month/year representing the current visible Calendar date (mm/yyyy)
  		* @config pagedate
  		* @type String | Date
  		* @default today's date
  		*/
  		cfg.addProperty(DEF_CFG.PAGEDATE.key, { value:new Date(), handler:this.configPageDate } );
  
  		/**
  		* The date or range of dates representing the current Calendar selection
  		* @config selected
  		* @type String
  		* @default []
  		*/
  		cfg.addProperty(DEF_CFG.SELECTED.key, { value:[], handler:this.configSelected } );
  
  		/**
  		* The title to display above the Calendar's month header
  		* @config title
  		* @type String
  		* @default ""
  		*/
  		cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
  
  		/**
  		* Whether or not a close button should be displayed for this Calendar
  		* @config close
  		* @type Boolean
  		* @default false
  		*/
  		cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
  
  		/**
  		* Whether or not an iframe shim should be placed under the Calendar to prevent select boxes from bleeding through in Internet Explorer 6 and below.
  		* This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be 
  		* enabled if required.
  		* 
  		* @config iframe
  		* @type Boolean
  		* @default true for IE6 and below, false for all other browsers
  		*/
  		cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
  
  		/**
  		* The minimum selectable date in the current Calendar (mm/dd/yyyy)
  		* @config mindate
  		* @type String | Date
  		* @default null
  		*/
  		cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.configMinDate } );
  
  		/**
  		* The maximum selectable date in the current Calendar (mm/dd/yyyy)
  		* @config maxdate
  		* @type String | Date
  		* @default null
  		*/
  		cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.configMaxDate } );
  	
  	
  		// Options properties
  	
  		/**
  		* True if the Calendar should allow multiple selections. False by default.
  		* @config MULTI_SELECT
  		* @type Boolean
  		* @default false
  		*/
  		cfg.addProperty(DEF_CFG.MULTI_SELECT.key,	{ value:DEF_CFG.MULTI_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } );
  
  		/**
  		* The weekday the week begins on. Default is 0 (Sunday = 0, Monday = 1 ... Saturday = 6).
  		* @config START_WEEKDAY
  		* @type number
  		* @default 0
  		*/
  		cfg.addProperty(DEF_CFG.START_WEEKDAY.key,	{ value:DEF_CFG.START_WEEKDAY.value, handler:this.configOptions, validator:cfg.checkNumber  } );
  	
  		/**
  		* True if the Calendar should show weekday labels. True by default.
  		* @config SHOW_WEEKDAYS
  		* @type Boolean
  		* @default true
  		*/
  		cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key,	{ value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.configOptions, validator:cfg.checkBoolean  } );
  	
  		/**
  		* True if the Calendar should show week row headers. False by default.
  		* @config SHOW_WEEK_HEADER
  		* @type Boolean
  		* @default false
  		*/
  		cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key, { value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
  	
  		/**
  		* True if the Calendar should show week row footers. False by default.
  		* @config SHOW_WEEK_FOOTER
  		* @type Boolean
  		* @default false
  		*/	
  		cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
  	
  		/**
  		* True if the Calendar should suppress weeks that are not a part of the current month. False by default.
  		* @config HIDE_BLANK_WEEKS
  		* @type Boolean
  		* @default false
  		*/	
  		cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key, { value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
  		
  		/**
  		* The image that should be used for the left navigation arrow.
  		* @config NAV_ARROW_LEFT
  		* @type String
  		* @deprecated	You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"  
  		* @default null
  		*/	
  		cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key,	{ value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.configOptions } );
  	
  		/**
  		* The image that should be used for the right navigation arrow.
  		* @config NAV_ARROW_RIGHT
  		* @type String
  		* @deprecated	You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
  		* @default null
  		*/	
  		cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.configOptions } );
  	
  		// Locale properties
  	
  		/**
  		* The short month labels for the current locale.
  		* @config MONTHS_SHORT
  		* @type String[]
  		* @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  		*/
  		cfg.addProperty(DEF_CFG.MONTHS_SHORT.key,	{ value:DEF_CFG.MONTHS_SHORT.value, handler:this.configLocale } );
  		
  		/**
  		* The long month labels for the current locale.
  		* @config MONTHS_LONG
  		* @type String[]
  		* @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
  		*/	
  		cfg.addProperty(DEF_CFG.MONTHS_LONG.key,		{ value:DEF_CFG.MONTHS_LONG.value, handler:this.configLocale } );
  
  		/**
  		* The 1-character weekday labels for the current locale.
  		* @config WEEKDAYS_1CHAR
  		* @type String[]
  		* @default ["S", "M", "T", "W", "T", "F", "S"]
  		*/	
  		cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key,	{ value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.configLocale } );
  		
  		/**
  		* The short weekday labels for the current locale.
  		* @config WEEKDAYS_SHORT
  		* @type String[]
  		* @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
  		*/	
  		cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key,	{ value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.configLocale } );
  		
  		/**
  		* The medium weekday labels for the current locale.
  		* @config WEEKDAYS_MEDIUM
  		* @type String[]
  		* @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
  		*/	
  		cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key,	{ value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.configLocale } );
  		
  		/**
  		* The long weekday labels for the current locale.
  		* @config WEEKDAYS_LONG
  		* @type String[]
  		* @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
  		*/	
  		cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key,	{ value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.configLocale } );
  	
  		/**
  		* Refreshes the locale values used to build the Calendar.
  		* @method refreshLocale
  		* @private
  		*/
  		var refreshLocale = function() {
  			cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
  			cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
  		};
  	
  		cfg.subscribeToConfigEvent(DEF_CFG.START_WEEKDAY.key, refreshLocale, this, true);
  		cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_SHORT.key, refreshLocale, this, true);
  		cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_LONG.key, refreshLocale, this, true);
  		cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_1CHAR.key, refreshLocale, this, true);
  		cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_SHORT.key, refreshLocale, this, true);
  		cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_MEDIUM.key, refreshLocale, this, true);
  		cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_LONG.key, refreshLocale, this, true);
  		
  		/**
  		* The setting that determines which length of month labels should be used. Possible values are "short" and "long".
  		* @config LOCALE_MONTHS
  		* @type String
  		* @default "long"
  		*/	
  		cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key,	{ value:DEF_CFG.LOCALE_MONTHS.value, handler:this.configLocaleValues } );
  		
  		/**
  		* The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
  		* @config LOCALE_WEEKDAYS
  		* @type String
  		* @default "short"
  		*/	
  		cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key,	{ value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.configLocaleValues } );
  	
  		/**
  		* The value used to delimit individual dates in a date string passed to various Calendar functions.
  		* @config DATE_DELIMITER
  		* @type String
  		* @default ","
  		*/	
  		cfg.addProperty(DEF_CFG.DATE_DELIMITER.key,		{ value:DEF_CFG.DATE_DELIMITER.value, handler:this.configLocale } );
  	
  		/**
  		* The value used to delimit date fields in a date string passed to various Calendar functions.
  		* @config DATE_FIELD_DELIMITER
  		* @type String
  		* @default "/"
  		*/	
  		cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key, { value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.configLocale } );
  	
  		/**
  		* The value used to delimit date ranges in a date string passed to various Calendar functions.
  		* @config DATE_RANGE_DELIMITER
  		* @type String
  		* @default "-"
  		*/
  		cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key, { value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.configLocale } );
  	
  		/**
  		* The position of the month in a month/year date string
  		* @config MY_MONTH_POSITION
  		* @type Number
  		* @default 1
  		*/
  		cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key,	{ value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
  	
  		/**
  		* The position of the year in a month/year date string
  		* @config MY_YEAR_POSITION
  		* @type Number
  		* @default 2
  		*/
  		cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key,	{ value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
  	
  		/**
  		* The position of the month in a month/day date string
  		* @config MD_MONTH_POSITION
  		* @type Number
  		* @default 1
  		*/
  		cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key,	{ value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
  	
  		/**
  		* The position of the day in a month/year date string
  		* @config MD_DAY_POSITION
  		* @type Number
  		* @default 2
  		*/
  		cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key,		{ value:DEF_CFG.MD_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
  	
  		/**
  		* The position of the month in a month/day/year date string
  		* @config MDY_MONTH_POSITION
  		* @type Number
  		* @default 1
  		*/
  		cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key,	{ value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
  	
  		/**
  		* The position of the day in a month/day/year date string
  		* @config MDY_DAY_POSITION
  		* @type Number
  		* @default 2
  		*/
  		cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key,	{ value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
  	
  		/**
  		* The position of the year in a month/day/year date string
  		* @config MDY_YEAR_POSITION
  		* @type Number
  		* @default 3
  		*/
  		cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key,	{ value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
  		
  		/**
  		* The position of the month in the month year label string used as the Calendar header
  		* @config MY_LABEL_MONTH_POSITION
  		* @type Number
  		* @default 1
  		*/
  		cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key,	{ value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
  	
  		/**
  		* The position of the year in the month year label string used as the Calendar header
  		* @config MY_LABEL_YEAR_POSITION
  		* @type Number
  		* @default 2
  		*/
  		cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key,	{ value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
  		
  		/**
  		* The suffix used after the month when rendering the Calendar header
  		* @config MY_LABEL_MONTH_SUFFIX
  		* @type String
  		* @default " "
  		*/
  		cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key,	{ value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.configLocale } );
  		
  		/**
  		* The suffix used after the year when rendering the Calendar header
  		* @config MY_LABEL_YEAR_SUFFIX
  		* @type String
  		* @default ""
  		*/
  		cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.configLocale } );
  
  		/**
  		* Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a 
  		* specific Month/Year without having to scroll sequentially through months.
  		* <p>
  		* Setting this property to null (default value) or false, will disable the CalendarNavigator UI.
  		* </p>
  		* <p>
  		* Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values.
  		* </p>
  		* <p>
  		* This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI.
  		* The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object.
  		* Any properties which are not provided will use the default values (defined in the CalendarNavigator class).
  		* </p>
  		* <dl>
  		* <dt>strings</dt>
  		* <dd><em>Object</em> :  An object with the properties shown below, defining the string labels to use in the Navigator's UI
  		*     <dl>
  		*         <dt>month</dt><dd><em>String</em> : The string to use for the month label. Defaults to "Month".</dd>
  		*         <dt>year</dt><dd><em>String</em> : The string to use for the year label. Defaults to "Year".</dd>
  		*         <dt>submit</dt><dd><em>String</em> : The string to use for the submit button label. Defaults to "Okay".</dd>
  		*         <dt>cancel</dt><dd><em>String</em> : The string to use for the cancel button label. Defaults to "Cancel".</dd>
  		*         <dt>invalidYear</dt><dd><em>String</em> : The string to use for invalid year values. Defaults to "Year needs to be a number".</dd>
  		*     </dl>
  		* </dd>
  		* <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd>
  		* <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
  		* </dl>
  		* <p>E.g.</p>
  		* <pre>
  		* var navConfig = {
  		*	  strings: {
  		*		  month:"Calendar Month",
  		*		  year:"Calendar Year",
  		*		  submit: "Submit",
  		*		  cancel: "Cancel",
  		*		  invalidYear: "Please enter a valid year"
  		*	  },
  		*	  monthFormat: YAHOO.widget.Calendar.SHORT,
  		*	  initialFocus: "month"
  		* }
  		* </pre>
  		* @config navigator
  		* @type {Object|Boolean}
  		* @default null
  		*/
  		cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
  
  		/**
  		 * The map of UI strings which the Calendar UI uses.
  		 *
  		 * @config strings
  		 * @type {Object}
  		 * @default An object with the properties shown below:
  		 *     <dl>
  		 *         <dt>previousMonth</dt><dd><em>String</em> : The string to use for the "Previous Month" navigation UI. Defaults to "Previous Month".</dd>
  		 *         <dt>nextMonth</dt><dd><em>String</em> : The string to use for the "Next Month" navigation UI. Defaults to "Next Month".</dd>
  		 *         <dt>close</dt><dd><em>String</em> : The string to use for the close button label. Defaults to "Close".</dd>
  		 *     </dl>
  		 */
  		cfg.addProperty(DEF_CFG.STRINGS.key, { 
  			value:DEF_CFG.STRINGS.value,
  			handler:this.configStrings,
  			validator: function(val) {
  				return Lang.isObject(val);
  			},
  			supercedes:DEF_CFG.STRINGS.supercedes
  		});
  	},
  
  	/**
  	* The default handler for the "strings" property
  	* @method configStrings
  	*/
  	configStrings : function(type, args, obj) {
  		var val = Lang.merge(DEF_CFG.STRINGS.value, args[0]);
  		this.cfg.setProperty(DEF_CFG.STRINGS.key, val, true);
  	},
  
  	/**
  	* The default handler for the "pagedate" property
  	* @method configPageDate
  	*/
  	configPageDate : function(type, args, obj) {
  		this.cfg.setProperty(DEF_CFG.PAGEDATE.key, this._parsePageDate(args[0]), true);
  	},
  
  	/**
  	* The default handler for the "mindate" property
  	* @method configMinDate
  	*/
  	configMinDate : function(type, args, obj) {
  		var val = args[0];
  		if (Lang.isString(val)) {
  			val = this._parseDate(val);
  			this.cfg.setProperty(DEF_CFG.MINDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
  		}
  	},
  
  	/**
  	* The default handler for the "maxdate" property
  	* @method configMaxDate
  	*/
  	configMaxDate : function(type, args, obj) {
  		var val = args[0];
  		if (Lang.isString(val)) {
  			val = this._parseDate(val);
  			this.cfg.setProperty(DEF_CFG.MAXDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
  		}
  	},
  
  	/**
  	* The default handler for the "selected" property
  	* @method configSelected
  	*/
  	configSelected : function(type, args, obj) {
  		var selected = args[0],
  			cfgSelected = DEF_CFG.SELECTED.key;
  		
  		if (selected) {
  			if (Lang.isString(selected)) {
  				this.cfg.setProperty(cfgSelected, this._parseDates(selected), true);
  			} 
  		}
  		if (! this._selectedDates) {
  			this._selectedDates = this.cfg.getProperty(cfgSelected);
  		}
  	},
  	
  	/**
  	* The default handler for all configuration options properties
  	* @method configOptions
  	*/
  	configOptions : function(type, args, obj) {
  		this.Options[type.toUpperCase()] = args[0];
  	},
  
  	/**
  	* The default handler for all configuration locale properties
  	* @method configLocale
  	*/
  	configLocale : function(type, args, obj) {
  		this.Locale[type.toUpperCase()] = args[0];
  
  		this.cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
  		this.cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
  	},
  	
  	/**
  	* The default handler for all configuration locale field length properties
  	* @method configLocaleValues
  	*/
  	configLocaleValues : function(type, args, obj) {
  
  		type = type.toLowerCase();
  
  		var val = args[0],
  			cfg = this.cfg,
  			Locale = this.Locale;
  
  		switch (type) {
  			case DEF_CFG.LOCALE_MONTHS.key:
  				switch (val) {
  					case Calendar.SHORT:
  						Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_SHORT.key).concat();
  						break;
  					case Calendar.LONG:
  						Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_LONG.key).concat();
  						break;
  				}
  				break;
  			case DEF_CFG.LOCALE_WEEKDAYS.key:
  				switch (val) {
  					case Calendar.ONE_CHAR:
  						Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_1CHAR.key).concat();
  						break;
  					case Calendar.SHORT:
  						Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_SHORT.key).concat();
  						break;
  					case Calendar.MEDIUM:
  						Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_MEDIUM.key).concat();
  						break;
  					case Calendar.LONG:
  						Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_LONG.key).concat();
  						break;
  				}
  				
  				var START_WEEKDAY = cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
  	
  				if (START_WEEKDAY > 0) {
  					for (var w=0; w < START_WEEKDAY; ++w) {
  						Locale.LOCALE_WEEKDAYS.push(Locale.LOCALE_WEEKDAYS.shift());
  					}
  				}
  				break;
  		}
  	},
  
  	/**
  	 * The default handler for the "navigator" property
  	 * @method configNavigator
  	 */
  	configNavigator : function(type, args, obj) {
  		var val = args[0];
  		if (YAHOO.widget.CalendarNavigator && (val === true || Lang.isObject(val))) {
  			if (!this.oNavigator) {
  				this.oNavigator = new YAHOO.widget.CalendarNavigator(this);
  				// Cleanup DOM Refs/Events before innerHTML is removed.
  				this.beforeRenderEvent.subscribe(function () {
  					if (!this.pages) {
  						this.oNavigator.erase();
  					}
  				}, this, true);
  			}
  		} else {
  			if (this.oNavigator) {
  				this.oNavigator.destroy();
  				this.oNavigator = null;
  			}
  		}
  	},
  
  	/**
  	* Defines the style constants for the Calendar
  	* @method initStyles
  	*/
  	initStyles : function() {
  
  		var defStyle = Calendar._STYLES;
  
  		this.Style = {
  			/**
  			* @property Style.CSS_ROW_HEADER
  			*/
  			CSS_ROW_HEADER: defStyle.CSS_ROW_HEADER,
  			/**
  			* @property Style.CSS_ROW_FOOTER
  			*/
  			CSS_ROW_FOOTER: defStyle.CSS_ROW_FOOTER,
  			/**
  			* @property Style.CSS_CELL
  			*/
  			CSS_CELL : defStyle.CSS_CELL,
  			/**
  			* @property Style.CSS_CELL_SELECTOR
  			*/
  			CSS_CELL_SELECTOR : defStyle.CSS_CELL_SELECTOR,
  			/**
  			* @property Style.CSS_CELL_SELECTED
  			*/
  			CSS_CELL_SELECTED : defStyle.CSS_CELL_SELECTED,
  			/**
  			* @property Style.CSS_CELL_SELECTABLE
  			*/
  			CSS_CELL_SELECTABLE : defStyle.CSS_CELL_SELECTABLE,
  			/**
  			* @property Style.CSS_CELL_RESTRICTED
  			*/
  			CSS_CELL_RESTRICTED : defStyle.CSS_CELL_RESTRICTED,
  			/**
  			* @property Style.CSS_CELL_TODAY
  			*/
  			CSS_CELL_TODAY : defStyle.CSS_CELL_TODAY,
  			/**
  			* @property Style.CSS_CELL_OOM
  			*/
  			CSS_CELL_OOM : defStyle.CSS_CELL_OOM,
  			/**
  			* @property Style.CSS_CELL_OOB
  			*/
  			CSS_CELL_OOB : defStyle.CSS_CELL_OOB,
  			/**
  			* @property Style.CSS_HEADER
  			*/
  			CSS_HEADER : defStyle.CSS_HEADER,
  			/**
  			* @property Style.CSS_HEADER_TEXT
  			*/
  			CSS_HEADER_TEXT : defStyle.CSS_HEADER_TEXT,
  			/**
  			* @property Style.CSS_BODY
  			*/
  			CSS_BODY : defStyle.CSS_BODY,
  			/**
  			* @property Style.CSS_WEEKDAY_CELL
  			*/
  			CSS_WEEKDAY_CELL : defStyle.CSS_WEEKDAY_CELL,
  			/**
  			* @property Style.CSS_WEEKDAY_ROW
  			*/
  			CSS_WEEKDAY_ROW : defStyle.CSS_WEEKDAY_ROW,
  			/**
  			* @property Style.CSS_FOOTER
  			*/
  			CSS_FOOTER : defStyle.CSS_FOOTER,
  			/**
  			* @property Style.CSS_CALENDAR
  			*/
  			CSS_CALENDAR : defStyle.CSS_CALENDAR,
  			/**
  			* @property Style.CSS_SINGLE
  			*/
  			CSS_SINGLE : defStyle.CSS_SINGLE,
  			/**
  			* @property Style.CSS_CONTAINER
  			*/
  			CSS_CONTAINER : defStyle.CSS_CONTAINER,
  			/**
  			* @property Style.CSS_NAV_LEFT
  			*/
  			CSS_NAV_LEFT : defStyle.CSS_NAV_LEFT,
  			/**
  			* @property Style.CSS_NAV_RIGHT
  			*/
  			CSS_NAV_RIGHT : defStyle.CSS_NAV_RIGHT,
  			/**
  			* @property Style.CSS_NAV
  			*/
  			CSS_NAV : defStyle.CSS_NAV,
  			/**
  			* @property Style.CSS_CLOSE
  			*/
  			CSS_CLOSE : defStyle.CSS_CLOSE,
  			/**
  			* @property Style.CSS_CELL_TOP
  			*/
  			CSS_CELL_TOP : defStyle.CSS_CELL_TOP,
  			/**
  			* @property Style.CSS_CELL_LEFT
  			*/
  			CSS_CELL_LEFT : defStyle.CSS_CELL_LEFT,
  			/**
  			* @property Style.CSS_CELL_RIGHT
  			*/
  			CSS_CELL_RIGHT : defStyle.CSS_CELL_RIGHT,
  			/**
  			* @property Style.CSS_CELL_BOTTOM
  			*/
  			CSS_CELL_BOTTOM : defStyle.CSS_CELL_BOTTOM,
  			/**
  			* @property Style.CSS_CELL_HOVER
  			*/
  			CSS_CELL_HOVER : defStyle.CSS_CELL_HOVER,
  			/**
  			* @property Style.CSS_CELL_HIGHLIGHT1
  			*/
  			CSS_CELL_HIGHLIGHT1 : defStyle.CSS_CELL_HIGHLIGHT1,
  			/**
  			* @property Style.CSS_CELL_HIGHLIGHT2
  			*/
  			CSS_CELL_HIGHLIGHT2 : defStyle.CSS_CELL_HIGHLIGHT2,
  			/**
  			* @property Style.CSS_CELL_HIGHLIGHT3
  			*/
  			CSS_CELL_HIGHLIGHT3 : defStyle.CSS_CELL_HIGHLIGHT3,
  			/**
  			* @property Style.CSS_CELL_HIGHLIGHT4
  			*/
  			CSS_CELL_HIGHLIGHT4 : defStyle.CSS_CELL_HIGHLIGHT4
  		};
  	},
  
  	/**
  	* Builds the date label that will be displayed in the calendar header or
  	* footer, depending on configuration.
  	* @method buildMonthLabel
  	* @return	{String}	The formatted calendar month label
  	*/
  	buildMonthLabel : function() {
  		return this._buildMonthLabel(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
  	},
  
      /**
       * Helper method, to format a Month Year string, given a JavaScript Date, based on the 
       * Calendar localization settings
       * 
       * @method _buildMonthLabel
       * @private
       * @param {Date} date
       * @return {String} Formated month, year string
       */
  	_buildMonthLabel : function(date) {
  		var	monthLabel  = this.Locale.LOCALE_MONTHS[date.getMonth()] + this.Locale.MY_LABEL_MONTH_SUFFIX,
  			yearLabel = date.getFullYear() + this.Locale.MY_LABEL_YEAR_SUFFIX;
  
  		if (this.Locale.MY_LABEL_MONTH_POSITION == 2 || this.Locale.MY_LABEL_YEAR_POSITION == 1) {
  			return yearLabel + monthLabel;
  		} else {
  			return monthLabel + yearLabel;
  		}
  	},
  	
  	/**
  	* Builds the date digit that will be displayed in calendar cells
  	* @method buildDayLabel
  	* @param {Date}	workingDate	The current working date
  	* @return	{String}	The formatted day label
  	*/
  	buildDayLabel : function(workingDate) {
  		return workingDate.getDate();
  	},
  	
  	/**
  	 * Creates the title bar element and adds it to Calendar container DIV
  	 * 
  	 * @method createTitleBar
  	 * @param {String} strTitle The title to display in the title bar
  	 * @return The title bar element
  	 */
  	createTitleBar : function(strTitle) {
  		var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || document.createElement("div");
  		tDiv.className = YAHOO.widget.CalendarGroup.CSS_2UPTITLE;
  		tDiv.innerHTML = strTitle;
  		this.oDomContainer.insertBefore(tDiv, this.oDomContainer.firstChild);
  	
  		Dom.addClass(this.oDomContainer, "withtitle");
  	
  		return tDiv;
  	},
  	
  	/**
  	 * Removes the title bar element from the DOM
  	 * 
  	 * @method removeTitleBar
  	 */
  	removeTitleBar : function() {
  		var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || null;
  		if (tDiv) {
  			Event.purgeElement(tDiv);
  			this.oDomContainer.removeChild(tDiv);
  		}
  		Dom.removeClass(this.oDomContainer, "withtitle");
  	},
  	
  	/**
  	 * Creates the close button HTML element and adds it to Calendar container DIV
  	 * 
  	 * @method createCloseButton
  	 * @return The close HTML element created
  	 */
  	createCloseButton : function() {
  		var cssClose = YAHOO.widget.CalendarGroup.CSS_2UPCLOSE,
  			DEPR_CLOSE_PATH = "us/my/bn/x_d.gif",
  			lnk = Dom.getElementsByClassName("link-close", "a", this.oDomContainer)[0],
  			strings = this.cfg.getProperty(DEF_CFG.STRINGS.key),
  			closeStr = (strings && strings.close) ? strings.close : "";
  
  		if (!lnk) {
  			lnk = document.createElement("a");
  			Event.addListener(lnk, "click", function(e, cal) {
  				cal.hide(); 
  				Event.preventDefault(e);
  			}, this);
  		}
  
  		lnk.href = "#";
  		lnk.className = "link-close";
  
  		if (Calendar.IMG_ROOT !== null) {
  			var img = Dom.getElementsByClassName(cssClose, "img", lnk)[0] || document.createElement("img");
  			img.src = Calendar.IMG_ROOT + DEPR_CLOSE_PATH;
  			img.className = cssClose;
  			lnk.appendChild(img);
  		} else {
  			lnk.innerHTML = '<span class="' + cssClose + ' ' + this.Style.CSS_CLOSE + '">' + closeStr + '</span>';
  		}
  		this.oDomContainer.appendChild(lnk);
  
  		return lnk;
  	},
  	
  	/**
  	 * Removes the close button HTML element from the DOM
  	 * 
  	 * @method removeCloseButton
  	 */
  	removeCloseButton : function() {
  		var btn = Dom.getElementsByClassName("link-close", "a", this.oDomContainer)[0] || null;
  		if (btn) {
  			Event.purgeElement(btn);
  			this.oDomContainer.removeChild(btn);
  		}
  	},
  
  	/**
  	* Renders the calendar header.
  	* @method renderHeader
  	* @param {Array}	html	The current working HTML array
  	* @return {Array} The current working HTML array
  	*/
  	renderHeader : function(html) {
  
  
  		var colSpan = 7,
  			DEPR_NAV_LEFT = "us/tr/callt.gif",
  			DEPR_NAV_RIGHT = "us/tr/calrt.gif",
  			cfg = this.cfg,
  			pageDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
  			strings= cfg.getProperty(DEF_CFG.STRINGS.key),
  			prevStr = (strings && strings.previousMonth) ?  strings.previousMonth : "",
  			nextStr = (strings && strings.nextMonth) ? strings.nextMonth : "",
              monthLabel;
  
  		if (cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
  			colSpan += 1;
  		}
  	
  		if (cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
  			colSpan += 1;
  		}
  
  		html[html.length] = "<thead>";
  		html[html.length] =		"<tr>";
  		html[html.length] =			'<th colspan="' + colSpan + '" class="' + this.Style.CSS_HEADER_TEXT + '">';
  		html[html.length] =				'<div class="' + this.Style.CSS_HEADER + '">';
  
  		var renderLeft, renderRight = false;
  
  		if (this.parent) {
  			if (this.index === 0) {
  				renderLeft = true;
  			}
  			if (this.index == (this.parent.cfg.getProperty("pages") -1)) {
  				renderRight = true;
  			}
  		} else {
  			renderLeft = true;
  			renderRight = true;
  		}
  
  		if (renderLeft) {
  			monthLabel  = this._buildMonthLabel(DateMath.subtract(pageDate, DateMath.MONTH, 1));
  
  			var leftArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_LEFT.key);
  			// Check for deprecated customization - If someone set IMG_ROOT, but didn't set NAV_ARROW_LEFT, then set NAV_ARROW_LEFT to the old deprecated value
  			if (leftArrow === null && Calendar.IMG_ROOT !== null) {
  				leftArrow = Calendar.IMG_ROOT + DEPR_NAV_LEFT;
  			}
  			var leftStyle = (leftArrow === null) ? "" : ' style="background-image:url(' + leftArrow + ')"';
  			html[html.length] = '<a class="' + this.Style.CSS_NAV_LEFT + '"' + leftStyle + ' href="#">' + prevStr + ' (' + monthLabel + ')' + '</a>';
  		}
  
  		var lbl = this.buildMonthLabel();
  		var cal = this.parent || this;
  		if (cal.cfg.getProperty("navigator")) {
  			lbl = "<a class=\"" + this.Style.CSS_NAV + "\" href=\"#\">" + lbl + "</a>";
  		}
  		html[html.length] = lbl;
  
  		if (renderRight) {
  			monthLabel  = this._buildMonthLabel(DateMath.add(pageDate, DateMath.MONTH, 1));
  
  			var rightArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_RIGHT.key);
  			if (rightArrow === null && Calendar.IMG_ROOT !== null) {
  				rightArrow = Calendar.IMG_ROOT + DEPR_NAV_RIGHT;
  			}
  			var rightStyle = (rightArrow === null) ? "" : ' style="background-image:url(' + rightArrow + ')"';
  			html[html.length] = '<a class="' + this.Style.CSS_NAV_RIGHT + '"' + rightStyle + ' href="#">' + nextStr + ' (' + monthLabel + ')' + '</a>';
  		}
  
  		html[html.length] =	'</div>\n</th>\n</tr>';
  
  		if (cfg.getProperty(DEF_CFG.SHOW_WEEKDAYS.key)) {
  			html = this.buildWeekdays(html);
  		}
  		
  		html[html.length] = '</thead>';
  	
  		return html;
  	},
  	
  	/**
  	* Renders the Calendar's weekday headers.
  	* @method buildWeekdays
  	* @param {Array}	html	The current working HTML array
  	* @return {Array} The current working HTML array
  	*/
  	buildWeekdays : function(html) {
  
  		html[html.length] = '<tr class="' + this.Style.CSS_WEEKDAY_ROW + '">';
  
  		if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
  			html[html.length] = '<th>&#160;</th>';
  		}
  
  		for(var i=0;i < this.Locale.LOCALE_WEEKDAYS.length; ++i) {
  			html[html.length] = '<th class="calweekdaycell">' + this.Locale.LOCALE_WEEKDAYS[i] + '</th>';
  		}
  
  		if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
  			html[html.length] = '<th>&#160;</th>';
  		}
  
  		html[html.length] = '</tr>';
  
  		return html;
  	},
  	
  	/**
  	* Renders the calendar body.
  	* @method renderBody
  	* @param {Date}	workingDate	The current working Date being used for the render process
  	* @param {Array}	html	The current working HTML array
  	* @return {Array} The current working HTML array
  	*/
  	renderBody : function(workingDate, html) {
  
  		var startDay = this.cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
  
  		this.preMonthDays = workingDate.getDay();
  		if (startDay > 0) {
  			this.preMonthDays -= startDay;
  		}
  		if (this.preMonthDays < 0) {
  			this.preMonthDays += 7;
  		}
  
  		this.monthDays = DateMath.findMonthEnd(workingDate).getDate();
  		this.postMonthDays = Calendar.DISPLAY_DAYS-this.preMonthDays-this.monthDays;
  
  
  		workingDate = DateMath.subtract(workingDate, DateMath.DAY, this.preMonthDays);
  	
  		var weekNum,
  			weekClass,
  			weekPrefix = "w",
  			cellPrefix = "_cell",
  			workingDayPrefix = "wd",
  			dayPrefix = "d",
  			cellRenderers,
  			renderer,
  			t = this.today,
  			cfg = this.cfg,
  			todayYear = t.getFullYear(),
  			todayMonth = t.getMonth(),
  			todayDate = t.getDate(),
  			useDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
  			hideBlankWeeks = cfg.getProperty(DEF_CFG.HIDE_BLANK_WEEKS.key),
  			showWeekFooter = cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key),
  			showWeekHeader = cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key),
  			mindate = cfg.getProperty(DEF_CFG.MINDATE.key),
  			maxdate = cfg.getProperty(DEF_CFG.MAXDATE.key);
  
  		if (mindate) {
  			mindate = DateMath.clearTime(mindate);
  		}
  		if (maxdate) {
  			maxdate = DateMath.clearTime(maxdate);
  		}
  
  		html[html.length] = '<tbody class="m' + (useDate.getMonth()+1) + ' ' + this.Style.CSS_BODY + '">';
  
  		var i = 0,
  			tempDiv = document.createElement("div"),
  			cell = document.createElement("td");
  
  		tempDiv.appendChild(cell);
  
  		var cal = this.parent || this;
  
  		for (var r=0;r<6;r++) {
  			weekNum = DateMath.getWeekNumber(workingDate, startDay);
  			weekClass = weekPrefix + weekNum;
  
  			// Local OOM check for performance, since we already have pagedate
  			if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth()) {
  				break;
  			} else {
  				html[html.length] = '<tr class="' + weekClass + '">';
  
  				if (showWeekHeader) { html = this.renderRowHeader(weekNum, html); }
  
  				for (var d=0; d < 7; d++){ // Render actual days
  
  					cellRenderers = [];
  
  					this.clearElement(cell);
  					cell.className = this.Style.CSS_CELL;
  					cell.id = this.id + cellPrefix + i;
  
  					if (workingDate.getDate()		== todayDate && 
  						workingDate.getMonth()		== todayMonth &&
  						workingDate.getFullYear()	== todayYear) {
  						cellRenderers[cellRenderers.length]=cal.renderCellStyleToday;
  					}
  
  					var workingArray = [workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()];
  					this.cellDates[this.cellDates.length] = workingArray; // Add this date to cellDates
  
  					// Local OOM check for performance, since we already have pagedate
  					if (workingDate.getMonth() != useDate.getMonth()) {
  						cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth;
  					} else {
  						Dom.addClass(cell, workingDayPrefix + workingDate.getDay());
  						Dom.addClass(cell, dayPrefix + workingDate.getDate());
  
  						for (var s=0;s<this.renderStack.length;++s) {
  
  							renderer = null;
  
  							var rArray = this.renderStack[s],
  								type = rArray[0],
  								month,
  								day,
  								year;
  
  							switch (type) {
  								case Calendar.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(s,1);
  									}
  									break;
  								case Calendar.MONTH_DAY:
  									month = rArray[1][0];
  									day = rArray[1][1];
  
  									if (workingDate.getMonth()+1 == month && workingDate.getDate() == day) {
  										renderer = rArray[2];
  										this.renderStack.splice(s,1);
  									}
  									break;
  								case Calendar.RANGE:
  									var date1 = rArray[1][0],
  										date2 = rArray[1][1],
  										d1month = date1[1],
  										d1day = date1[2],
  										d1year = date1[0],
  										d1 = DateMath.getDate(d1year, d1month-1, d1day),
  										d2month = date2[1],
  										d2day = date2[2],
  										d2year = date2[0],
  										d2 = DateMath.getDate(d2year, d2month-1, d2day);
  
  									if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime()) {
  										renderer = rArray[2];
  
  										if (workingDate.getTime()==d2.getTime()) { 
  											this.renderStack.splice(s,1);
  										}
  									}
  									break;
  								case Calendar.WEEKDAY:
  									var weekday = rArray[1][0];
  									if (workingDate.getDay()+1 == weekday) {
  										renderer = rArray[2];
  									}
  									break;
  								case Calendar.MONTH:
  									month = rArray[1][0];
  									if (workingDate.getMonth()+1 == month) {
  										renderer = rArray[2];
  									}
  									break;
  							}
  
  							if (renderer) {
  								cellRenderers[cellRenderers.length]=renderer;
  							}
  						}
  
  					}
  
  					if (this._indexOfSelectedFieldArray(workingArray) > -1) {
  						cellRenderers[cellRenderers.length]=cal.renderCellStyleSelected; 
  					}
  
  					if ((mindate && (workingDate.getTime() < mindate.getTime())) ||
  						(maxdate && (workingDate.getTime() > maxdate.getTime()))
  					) {
  						cellRenderers[cellRenderers.length]=cal.renderOutOfBoundsDate;
  					} else {
  						cellRenderers[cellRenderers.length]=cal.styleCellDefault;
  						cellRenderers[cellRenderers.length]=cal.renderCellDefault;	
  					}
  
  					for (var x=0; x < cellRenderers.length; ++x) {
  						if (cellRenderers[x].call(cal, workingDate, cell) == Calendar.STOP_RENDER) {
  							break;
  						}
  					}
  
  					workingDate.setTime(workingDate.getTime() + DateMath.ONE_DAY_MS);
  					// Just in case we crossed DST/Summertime boundaries
  					workingDate = DateMath.clearTime(workingDate);
  
  					if (i >= 0 && i <= 6) {
  						Dom.addClass(cell, this.Style.CSS_CELL_TOP);
  					}
  					if ((i % 7) === 0) {
  						Dom.addClass(cell, this.Style.CSS_CELL_LEFT);
  					}
  					if (((i+1) % 7) === 0) {
  						Dom.addClass(cell, this.Style.CSS_CELL_RIGHT);
  					}
  
  					var postDays = this.postMonthDays; 
  					if (hideBlankWeeks && postDays >= 7) {
  						var blankWeeks = Math.floor(postDays/7);
  						for (var p=0;p<blankWeeks;++p) {
  							postDays -= 7;
  						}
  					}
  					
  					if (i >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
  						Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM);
  					}
  	
  					html[html.length] = tempDiv.innerHTML;
  					i++;
  				}
  	
  				if (showWeekFooter) { html = this.renderRowFooter(weekNum, html); }
  	
  				html[html.length] = '</tr>';
  			}
  		}
  	
  		html[html.length] = '</tbody>';
  	
  		return html;
  	},
  	
  	/**
  	* Renders the calendar footer. In the default implementation, there is
  	* no footer.
  	* @method renderFooter
  	* @param {Array}	html	The current working HTML array
  	* @return {Array} The current working HTML array
  	*/
  	renderFooter : function(html) { return html; },
  	
  	/**
  	* 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.
  	* @method render
  	*/
  	render : function() {
  		this.beforeRenderEvent.fire();
  
  		// Find starting day of the current month
  		var workingDate = DateMath.findMonthStart(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
  
  		this.resetRenderers();
  		this.cellDates.length = 0;
  
  		Event.purgeElement(this.oDomContainer, true);
  
  		var html = [];
  
  		html[html.length] = '<table cellSpacing="0" class="' + this.Style.CSS_CALENDAR + ' y' + workingDate.getFullYear() + '" id="' + this.id + '">';
  		html = this.renderHeader(html);
  		html = this.renderBody(workingDate, html);
  		html = this.renderFooter(html);
  		html[html.length] = '</table>';
  
  		this.oDomContainer.innerHTML = html.join("\n");
  
  		this.applyListeners();
  		this.cells = this.oDomContainer.getElementsByTagName("td");
  	
  		this.cfg.refireEvent(DEF_CFG.TITLE.key);
  		this.cfg.refireEvent(DEF_CFG.CLOSE.key);
  		this.cfg.refireEvent(DEF_CFG.IFRAME.key);
  
  		this.renderEvent.fire();
  	},
  
  	/**
  	* Applies the Calendar's DOM listeners to applicable elements.
  	* @method applyListeners
  	*/
  	applyListeners : function() {
  		var root = this.oDomContainer,
  			cal = this.parent || this,
  			anchor = "a",
  			click = "click";
  
  		var linkLeft = Dom.getElementsByClassName(this.Style.CSS_NAV_LEFT, anchor, root),
  			linkRight = Dom.getElementsByClassName(this.Style.CSS_NAV_RIGHT, anchor, root);
  
  		if (linkLeft && linkLeft.length > 0) {
  			this.linkLeft = linkLeft[0];
  			Event.addListener(this.linkLeft, click, this.doPreviousMonthNav, cal, true);
  		}
  
  		if (linkRight && linkRight.length > 0) {
  			this.linkRight = linkRight[0];
  			Event.addListener(this.linkRight, click, this.doNextMonthNav, cal, true);
  		}
  
  		if (cal.cfg.getProperty("navigator") !== null) {
  			this.applyNavListeners();
  		}
  
  		if (this.domEventMap) {
  			var el,elements;
  			for (var cls in this.domEventMap) {	
  				if (Lang.hasOwnProperty(this.domEventMap, cls)) {
  					var items = this.domEventMap[cls];
  	
  					if (! (items instanceof Array)) {
  						items = [items];
  					}
  	
  					for (var i=0;i<items.length;i++)	{
  						var item = items[i];
  						elements = Dom.getElementsByClassName(cls, item.tag, this.oDomContainer);
  	
  						for (var c=0;c<elements.length;c++) {
  							el = elements[c];
  							 Event.addListener(el, item.event, item.handler, item.scope, item.correct );
  						}
  					}
  				}
  			}
  		}
  
  		Event.addListener(this.oDomContainer, "click", this.doSelectCell, this);
  		Event.addListener(this.oDomContainer, "mouseover", this.doCellMouseOver, this);
  		Event.addListener(this.oDomContainer, "mouseout", this.doCellMouseOut, this);
  	},
  
  	applyNavListeners : function() {
  		var calParent = this.parent || this,
  			cal = this,
  			navBtns = Dom.getElementsByClassName(this.Style.CSS_NAV, "a", this.oDomContainer);
  
  		if (navBtns.length > 0) {
  
  			Event.addListener(navBtns, "click", function (e, obj) {
  				var target = Event.getTarget(e);
  				// this == navBtn
  				if (this === target || Dom.isAncestor(this, target)) {
  					Event.preventDefault(e);
  				}
  				var navigator = calParent.oNavigator;
  				if (navigator) {
  					var pgdate = cal.cfg.getProperty("pagedate");
  					navigator.setYear(pgdate.getFullYear());
  					navigator.setMonth(pgdate.getMonth());
  					navigator.show();
  				}
  			});
  		}
  	},
  
  	/**
  	* Retrieves the Date object for the specified Calendar cell
  	* @method getDateByCellId
  	* @param {String}	id	The id of the cell
  	* @return {Date} The Date object for the specified Calendar cell
  	*/
  	getDateByCellId : function(id) {
  		var date = this.getDateFieldsByCellId(id);
  		return (date) ? DateMath.getDate(date[0],date[1]-1,date[2]) : null;
  	},
  	
  	/**
  	* Retrieves the Date object for the specified Calendar cell
  	* @method getDateFieldsByCellId
  	* @param {String}	id	The id of the cell
  	* @return {Array}	The array of Date fields for the specified Calendar cell
  	*/
  	getDateFieldsByCellId : function(id) {
  		id = this.getIndexFromId(id);
  		return (id > -1) ? this.cellDates[id] : null;
  	},
  
  	/**
  	 * Find the Calendar's cell index for a given date.
  	 * If the date is not found, the method returns -1.
  	 * <p>
  	 * The returned index can be used to lookup the cell HTMLElement  
  	 * using the Calendar's cells array or passed to selectCell to select 
  	 * cells by index. 
  	 * </p>
  	 *
  	 * See <a href="#cells">cells</a>, <a href="#selectCell">selectCell</a>.
  	 *
  	 * @method getCellIndex
  	 * @param {Date} date JavaScript Date object, for which to find a cell index.
  	 * @return {Number} The index of the date in Calendars cellDates/cells arrays, or -1 if the date 
  	 * is not on the curently rendered Calendar page.
  	 */
  	getCellIndex : function(date) {
  		var idx = -1;
  		if (date) {
  			var m = date.getMonth(),
  				y = date.getFullYear(),
  				d = date.getDate(),
  				dates = this.cellDates;
  
  			for (var i = 0; i < dates.length; ++i) {
  				var cellDate = dates[i];
  				if (cellDate[0] === y && cellDate[1] === m+1 && cellDate[2] === d) {
  					idx = i;
  					break;
  				}
  			}
  		}
  		return idx;
  	},
  
  	/**
  	 * Given the id used to mark each Calendar cell, this method
  	 * extracts the index number from the id.
  	 * 
  	 * @param {String} strId The cell id
  	 * @return {Number} The index of the cell, or -1 if id does not contain an index number
  	 */
  	getIndexFromId : function(strId) {
  		var idx = -1,
  			li = strId.lastIndexOf("_cell");
  
  		if (li > -1) {
  			idx = parseInt(strId.substring(li + 5), 10);
  		}
  
  		return idx;
  	},
  	
  	// BEGIN BUILT-IN TABLE CELL RENDERERS
  	
  	/**
  	* Renders a cell that falls before the minimum date or after the maximum date.
  	* widget class.
  	* @method renderOutOfBoundsDate
  	* @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 {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
  	*			should not be terminated
  	*/
  	renderOutOfBoundsDate : function(workingDate, cell) {
  		Dom.addClass(cell, this.Style.CSS_CELL_OOB);
  		cell.innerHTML = workingDate.getDate();
  		return Calendar.STOP_RENDER;
  	},
  	
  	/**
  	* Renders the row header for a week.
  	* @method renderRowHeader
  	* @param {Number}	weekNum	The week number of the current row
  	* @param {Array}	cell	The current working HTML array
  	*/
  	renderRowHeader : function(weekNum, html) {
  		html[html.length] = '<th class="calrowhead">' + weekNum + '</th>';
  		return html;
  	},
  	
  	/**
  	* Renders the row footer for a week.
  	* @method renderRowFooter
  	* @param {Number}	weekNum	The week number of the current row
  	* @param {Array}	cell	The current working HTML array
  	*/
  	renderRowFooter : function(weekNum, html) {
  		html[html.length] = '<th class="calrowfoot">' + weekNum + '</th>';
  		return html;
  	},
  	
  	/**
  	* 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.
  	* @method renderCellDefault
  	* @param {Date}					workingDate		The current working Date object being used to generate the calendar
  	* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
  	*/
  	renderCellDefault : function(workingDate, cell) {
  		cell.innerHTML = '<a href="#" class="' + this.Style.CSS_CELL_SELECTOR + '">' + this.buildDayLabel(workingDate) + "</a>";
  	},
  	
  	/**
  	* Styles a selectable cell.
  	* @method styleCellDefault
  	* @param {Date}					workingDate		The current working Date object being used to generate the calendar
  	* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
  	*/
  	styleCellDefault : function(workingDate, cell) {
  		Dom.addClass(cell, this.Style.CSS_CELL_SELECTABLE);
  	},
  	
  	
  	/**
  	* Renders a single standard calendar cell using the CSS hightlight1 style
  	* @method renderCellStyleHighlight1
  	* @param {Date}					workingDate		The current working Date object being used to generate the calendar
  	* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
  	*/
  	renderCellStyleHighlight1 : function(workingDate, cell) {
  		Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
  	},
  	
  	/**
  	* Renders a single standard calendar cell using the CSS hightlight2 style
  	* @method renderCellStyleHighlight2
  	* @param {Date}					workingDate		The current working Date object being used to generate the calendar
  	* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
  	*/
  	renderCellStyleHighlight2 : function(workingDate, cell) {
  		Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
  	},
  	
  	/**
  	* Renders a single standard calendar cell using the CSS hightlight3 style
  	* @method renderCellStyleHighlight3
  	* @param {Date}					workingDate		The current working Date object being used to generate the calendar
  	* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
  	*/
  	renderCellStyleHighlight3 : function(workingDate, cell) {
  		Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
  	},
  	
  	/**
  	* Renders a single standard calendar cell using the CSS hightlight4 style
  	* @method renderCellStyleHighlight4
  	* @param {Date}					workingDate		The current working Date object being used to generate the calendar
  	* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
  	*/
  	renderCellStyleHighlight4 : function(workingDate, cell) {
  		Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
  	},
  	
  	/**
  	* Applies the default style used for rendering today's date to the current calendar cell
  	* @method renderCellStyleToday
  	* @param {Date}					workingDate		The current working Date object being used to generate the calendar
  	* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
  	*/
  	renderCellStyleToday : function(workingDate, cell) {
  		Dom.addClass(cell, this.Style.CSS_CELL_TODAY);
  	},
  	
  	/**
  	* Applies the default style used for rendering selected dates to the current calendar cell
  	* @method renderCellStyleSelected
  	* @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 {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
  	*			should not be terminated
  	*/
  	renderCellStyleSelected : function(workingDate, cell) {
  		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)
  	* @method renderCellNotThisMonth
  	* @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 {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
  	*			should not be terminated
  	*/
  	renderCellNotThisMonth : function(workingDate, cell) {
  		Dom.addClass(cell, this.Style.CSS_CELL_OOM);
  		cell.innerHTML=workingDate.getDate();
  		return Calendar.STOP_RENDER;
  	},
  	
  	/**
  	* Renders the current calendar cell as a non-selectable "black-out" date using the default
  	* restricted style.
  	* @method renderBodyCellRestricted
  	* @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 {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
  	*			should not be terminated
  	*/
  	renderBodyCellRestricted : function(workingDate, cell) {
  		Dom.addClass(cell, this.Style.CSS_CELL);
  		Dom.addClass(cell, this.Style.CSS_CELL_RESTRICTED);
  		cell.innerHTML=workingDate.getDate();
  		return Calendar.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.
  	* @method addMonths
  	* @param {Number}	count	The number of months to add to the current calendar
  	*/
  	addMonths : function(count) {
  		var cfgPageDate = DEF_CFG.PAGEDATE.key;
  		this.cfg.setProperty(cfgPageDate, DateMath.add(this.cfg.getProperty(cfgPageDate), DateMath.MONTH, count));
  		this.resetRenderers();
  		this.changePageEvent.fire();
  	},
  	
  	/**
  	* Subtracts the designated number of months from the current calendar month, and sets the current
  	* calendar page date to the new month.
  	* @method subtractMonths
  	* @param {Number}	count	The number of months to subtract from the current calendar
  	*/
  	subtractMonths : function(count) {
  		var cfgPageDate = DEF_CFG.PAGEDATE.key;
  		this.cfg.setProperty(cfgPageDate, DateMath.subtract(this.cfg.getProperty(cfgPageDate), DateMath.MONTH, count));
  		this.resetRenderers();
  		this.changePageEvent.fire();
  	},
  
  	/**
  	* Adds the designated number of years to the current calendar, and sets the current
  	* calendar page date to the new month.
  	* @method addYears
  	* @param {Number}	count	The number of years to add to the current calendar
  	*/
  	addYears : function(count) {
  		var cfgPageDate = DEF_CFG.PAGEDATE.key;
  		this.cfg.setProperty(cfgPageDate, DateMath.add(this.cfg.getProperty(cfgPageDate), DateMath.YEAR, count));
  		this.resetRenderers();
  		this.changePageEvent.fire();
  	},
  	
  	/**
  	* Subtcats the designated number of years from the current calendar, and sets the current
  	* calendar page date to the new month.
  	* @method subtractYears
  	* @param {Number}	count	The number of years to subtract from the current calendar
  	*/
  	subtractYears : function(count) {
  		var cfgPageDate = DEF_CFG.PAGEDATE.key;
  		this.cfg.setProperty(cfgPageDate, DateMath.subtract(this.cfg.getProperty(cfgPageDate), DateMath.YEAR, count));
  		this.resetRenderers();
  		this.changePageEvent.fire();
  	},
  	
  	/**
  	* Navigates to the next month page in the calendar widget.
  	* @method nextMonth
  	*/
  	nextMonth : function() {
  		this.addMonths(1);
  	},
  	
  	/**
  	* Navigates to the previous month page in the calendar widget.
  	* @method previousMonth
  	*/
  	previousMonth : function() {
  		this.subtractMonths(1);
  	},
  	
  	/**
  	* Navigates to the next year in the currently selected month in the calendar widget.
  	* @method nextYear
  	*/
  	nextYear : function() {
  		this.addYears(1);
  	},
  	
  	/**
  	* Navigates to the previous year in the currently selected month in the calendar widget.
  	* @method previousYear
  	*/
  	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).
  	* @method reset
  	*/
  	reset : function() {
  		this.cfg.resetProperty(DEF_CFG.SELECTED.key);
  		this.cfg.resetProperty(DEF_CFG.PAGEDATE.key);
  		this.resetEvent.fire();
  	},
  	
  	/**
  	* Clears the selected dates in the current calendar widget and sets the calendar
  	* to the current month and year.
  	* @method clear
  	*/
  	clear : function() {
  		this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
  		this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.today.getTime()));
  		this.clearEvent.fire();
  	},
  	
  	/**
  	* 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.
  	*
  	* Any dates which are OOB (out of bounds, not selectable) will not be selected and the array of 
  	* selected dates passed to the selectEvent will not contain OOB dates.
  	* 
  	* If all dates are OOB, the no state change will occur; beforeSelect and select events will not be fired.
  	*
  	* @method select
  	* @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	{Date[]}			Array of JavaScript Date objects representing all individual dates that are currently selected.
  	*/
  	select : function(date) {
  
  		var aToBeSelected = this._toFieldArray(date),
  			validDates = [],
  			selected = [],
  			cfgSelected = DEF_CFG.SELECTED.key;
  
  		
  		for (var a=0; a < aToBeSelected.length; ++a) {
  			var toSelect = aToBeSelected[a];
  
  			if (!this.isDateOOB(this._toDate(toSelect))) {
  
  				if (validDates.length === 0) {
  					this.beforeSelectEvent.fire();
  					selected = this.cfg.getProperty(cfgSelected);
  				}
  				validDates.push(toSelect);
  
  				if (this._indexOfSelectedFieldArray(toSelect) == -1) { 
  					selected[selected.length] = toSelect;
  				}
  			}
  		}
  
  
  		if (validDates.length > 0) {
  			if (this.parent) {
  				this.parent.cfg.setProperty(cfgSelected, selected);
  			} else {
  				this.cfg.setProperty(cfgSelected, selected);
  			}
  			this.selectEvent.fire(validDates);
  		}
  
  		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.
  	*
  	* If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month 
  	* or out of bounds cells), it will not be selected and in such a case beforeSelect and select events will not be fired.
  	* 
  	* @method selectCell
  	* @param	{Number}	cellIndex	The index of the cell to select in the current calendar. 
  	* @return	{Date[]}	Array of JavaScript Date objects representing all individual dates that are currently selected.
  	*/
  	selectCell : function(cellIndex) {
  
  		var cell = this.cells[cellIndex],
  			cellDate = this.cellDates[cellIndex],
  			dCellDate = this._toDate(cellDate),
  			selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
  
  
  		if (selectable) {
  	
  			this.beforeSelectEvent.fire();
  	
  			var cfgSelected = DEF_CFG.SELECTED.key;
  			var selected = this.cfg.getProperty(cfgSelected);
  	
  			var selectDate = cellDate.concat();
  	
  			if (this._indexOfSelectedFieldArray(selectDate) == -1) {
  				selected[selected.length] = selectDate;
  			}
  			if (this.parent) {
  				this.parent.cfg.setProperty(cfgSelected, selected);
  			} else {
  				this.cfg.setProperty(cfgSelected, selected);
  			}
  			this.renderCellStyleSelected(dCellDate,cell);
  			this.selectEvent.fire([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.
  	* 
  	* The method will not attempt to deselect any dates which are OOB (out of bounds, and hence not selectable) 
  	* and the array of deselected dates passed to the deselectEvent will not contain any OOB dates.
  	* 
  	* If all dates are OOB, beforeDeselect and deselect events will not be fired.
  	* 
  	* @method deselect
  	* @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	{Date[]}			Array of JavaScript Date objects representing all individual dates that are currently selected.
  	*/
  	deselect : function(date) {
  
  		var aToBeDeselected = this._toFieldArray(date),
  			validDates = [],
  			selected = [],
  			cfgSelected = DEF_CFG.SELECTED.key;
  
  
  		for (var a=0; a < aToBeDeselected.length; ++a) {
  			var toDeselect = aToBeDeselected[a];
  	
  			if (!this.isDateOOB(this._toDate(toDeselect))) {
  	
  				if (validDates.length === 0) {
  					this.beforeDeselectEvent.fire();
  					selected = this.cfg.getProperty(cfgSelected);
  				}
  	
  				validDates.push(toDeselect);
  	
  				var index = this._indexOfSelectedFieldArray(toDeselect);
  				if (index != -1) {	
  					selected.splice(index,1);
  				}
  			}
  		}
  	
  	
  		if (validDates.length > 0) {
  			if (this.parent) {
  				this.parent.cfg.setProperty(cfgSelected, selected);
  			} else {
  				this.cfg.setProperty(cfgSelected, selected);
  			}
  			this.deselectEvent.fire(validDates);
  		}
  	
  		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.
  	* 
  	* If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month 
  	* or out of bounds cells), the method will not attempt to deselect it and in such a case, beforeDeselect and 
  	* deselect events will not be fired.
  	* 
  	* @method deselectCell
  	* @param	{Number}	cellIndex	The index of the cell to deselect in the current calendar. 
  	* @return	{Date[]}	Array of JavaScript Date objects representing all individual dates that are currently selected.
  	*/
  	deselectCell : function(cellIndex) {
  		var cell = this.cells[cellIndex],
  			cellDate = this.cellDates[cellIndex],
  			cellDateIndex = this._indexOfSelectedFieldArray(cellDate);
  
  		var selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
  
  		if (selectable) {
  
  			this.beforeDeselectEvent.fire();
  
  			var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key),
  				dCellDate = this._toDate(cellDate),
  				selectDate = cellDate.concat();
  
  			if (cellDateIndex > -1) {
  				if (this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth() == dCellDate.getMonth() &&
  					this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getFullYear() == dCellDate.getFullYear()) {
  					Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
  				}
  				selected.splice(cellDateIndex, 1);
  			}
  	
  			if (this.parent) {
  				this.parent.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
  			} else {
  				this.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
  			}
  	
  			this.deselectEvent.fire(selectDate);
  		}
  	
  		return this.getSelectedDates();
  	},
  	
  	/**
  	* Deselects all dates on the current calendar.
  	* @method deselectAll
  	* @return {Date[]}		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.
  	*/
  	deselectAll : function() {
  		this.beforeDeselectEvent.fire();
  		
  		var cfgSelected = DEF_CFG.SELECTED.key,
  			selected = this.cfg.getProperty(cfgSelected),
  			count = selected.length,
  			sel = selected.concat();
  
  		if (this.parent) {
  			this.parent.cfg.setProperty(cfgSelected, []);
  		} else {
  			this.cfg.setProperty(cfgSelected, []);
  		}
  		
  		if (count > 0) {
  			this.deselectEvent.fire(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]].
  	* @method _toFieldArray
  	* @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[](Number[])}	Array of date field arrays
  	*/
  	_toFieldArray : function(date) {
  		var returnDate = [];
  	
  		if (date instanceof Date) {
  			returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]];
  		} else if (Lang.isString(date)) {
  			returnDate = this._parseDates(date);
  		} else if (Lang.isArray(date)) {
  			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. The date field array
  	* is the format in which dates are as provided as arguments to selectEvent and deselectEvent listeners.
  	* 
  	* @method toDate
  	* @param	{Number[]}	dateFieldArray	The date field array to convert to a JavaScript Date.
  	* @return	{Date}	JavaScript Date object representing the date field array.
  	*/
  	toDate : function(dateFieldArray) {
  		return this._toDate(dateFieldArray);
  	},
  	
  	/**
  	* Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
  	* @method _toDate
  	* @private
  	* @deprecated Made public, toDate 
  	* @param	{Number[]}		dateFieldArray	The date field array to convert to a JavaScript Date.
  	* @return	{Date}	JavaScript Date object representing the date field array
  	*/
  	_toDate : function(dateFieldArray) {
  		if (dateFieldArray instanceof Date) {
  			return dateFieldArray;
  		} else {
  			return DateMath.getDate(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.
  	* @method _fieldArraysAreEqual
  	* @private
  	* @param	{Number[]}	array1	The first date field array to compare
  	* @param	{Number[]}	array2	The first date field array to compare
  	* @return	{Boolean}	The boolean that represents the equality of the two arrays
  	*/
  	_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.
  	* @method	_indexOfSelectedFieldArray
  	* @private
  	* @param	{Number[]}		find	The date field array to search for
  	* @return	{Number}			The index of the date field array within the collection of selected dates.
  	*								-1 will be returned if the date is not found.
  	*/
  	_indexOfSelectedFieldArray : function(find) {
  		var selected = -1,
  			seldates = this.cfg.getProperty(DEF_CFG.SELECTED.key);
  	
  		for (var s=0;s<seldates.length;++s) {
  			var sArray = seldates[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).
  	* @method	isDateOOM
  	* @param	{Date}	date	The JavaScript Date object for which to check the OOM status
  	* @return	{Boolean}	true if the date is OOM
  	*/
  	isDateOOM : function(date) {
  		return (date.getMonth() != this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth());
  	},
  	
  	/**
  	* Determines whether a given date is OOB (out of bounds - less than the mindate or more than the maxdate).
  	*
  	* @method	isDateOOB
  	* @param	{Date}	date	The JavaScript Date object for which to check the OOB status
  	* @return	{Boolean}	true if the date is OOB
  	*/
  	isDateOOB : function(date) {
  		var minDate = this.cfg.getProperty(DEF_CFG.MINDATE.key),
  			maxDate = this.cfg.getProperty(DEF_CFG.MAXDATE.key),
  			dm = DateMath;
  		
  		if (minDate) {
  			minDate = dm.clearTime(minDate);
  		} 
  		if (maxDate) {
  			maxDate = dm.clearTime(maxDate);
  		}
  	
  		var clearedDate = new Date(date.getTime());
  		clearedDate = dm.clearTime(clearedDate);
  	
  		return ((minDate && clearedDate.getTime() < minDate.getTime()) || (maxDate && clearedDate.getTime() > maxDate.getTime()));
  	},
  	
  	/**
  	 * Parses a pagedate configuration property value. The value can either be specified as a string of form "mm/yyyy" or a Date object 
  	 * and is parsed into a Date object normalized to the first day of the month. If no value is passed in, the month and year from today's date are used to create the Date object 
  	 * @method	_parsePageDate
  	 * @private
  	 * @param {Date|String}	date	Pagedate value which needs to be parsed
  	 * @return {Date}	The Date object representing the pagedate
  	 */
  	_parsePageDate : function(date) {
  		var parsedDate;
  
  		if (date) {
  			if (date instanceof Date) {
  				parsedDate = DateMath.findMonthStart(date);
  			} else {
  				var month, year, aMonthYear;
  				aMonthYear = date.split(this.cfg.getProperty(DEF_CFG.DATE_FIELD_DELIMITER.key));
  				month = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_MONTH_POSITION.key)-1], 10)-1;
  				year = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_YEAR_POSITION.key)-1], 10);
  
  				parsedDate = DateMath.getDate(year, month, 1);
  			}
  		} else {
  			parsedDate = DateMath.getDate(this.today.getFullYear(), this.today.getMonth(), 1);
  		}
  		return parsedDate;
  	},
  	
  	// END UTILITY METHODS
  	
  	// BEGIN EVENT HANDLERS
  	
  	/**
  	* Event executed before a date is selected in the calendar widget.
  	* @deprecated Event handlers for this event should be susbcribed to beforeSelectEvent.
  	*/
  	onBeforeSelect : function() {
  		if (this.cfg.getProperty(DEF_CFG.MULTI_SELECT.key) === false) {
  			if (this.parent) {
  				this.parent.callChildFunction("clearAllBodyCellStyles", this.Style.CSS_CELL_SELECTED);
  				this.parent.deselectAll();
  			} else {
  				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] ]
  	* @deprecated Event handlers for this event should be susbcribed to selectEvent.
  	*/
  	onSelect : function(selected) { },
  	
  	/**
  	* Event executed before a date is deselected in the calendar widget.
  	* @deprecated Event handlers for this event should be susbcribed to beforeDeselectEvent.
  	*/
  	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] ]
  	* @deprecated Event handlers for this event should be susbcribed to deselectEvent.
  	*/
  	onDeselect : function(deselected) { },
  	
  	/**
  	* Event executed when the user navigates to a different calendar page.
  	* @deprecated Event handlers for this event should be susbcribed to changePageEvent.
  	*/
  	onChangePage : function() {
  		this.render();
  	},
  
  	/**
  	* Event executed when the calendar widget is rendered.
  	* @deprecated Event handlers for this event should be susbcribed to renderEvent.
  	*/
  	onRender : function() { },
  
  	/**
  	* Event executed when the calendar widget is reset to its original state.
  	* @deprecated Event handlers for this event should be susbcribed to resetEvemt.
  	*/
  	onReset : function() { this.render(); },
  
  	/**
  	* Event executed when the calendar widget is completely cleared to the current month with no selections.
  	* @deprecated Event handlers for this event should be susbcribed to clearEvent.
  	*/
  	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
  	*/
  	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[](Number[])
  	*/
  	_parseDate : function(sDate) {
  		var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER),
  			rArray;
  	
  		if (aDate.length == 2) {
  			rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
  			rArray.type = Calendar.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 = Calendar.DATE;
  		}
  	
  		for (var i=0;i<rArray.length;i++) {
  			rArray[i] = parseInt(rArray[i], 10);
  		}
  	
  		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[](Number[])
  	*/
  	_parseDates : function(sDates) {
  		var aReturn = [],
  			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),
  					dateStart = this._parseDate(aRange[0]),
  					dateEnd = this._parseDate(aRange[1]),
  					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	{Number[]}	startDate	Date field array representing the first date in the range
  	* @param	{Number[]}	endDate		Date field array representing the last date in the range
  	* @return							An array of date field arrays
  	* @type Array[](Number[])
  	*/
  	_parseRange : function(startDate, endDate) {
  		var dCurrent = DateMath.add(DateMath.getDate(startDate[0],startDate[1]-1,startDate[2]),DateMath.DAY,1),
  			dEnd     = DateMath.getDate(endDate[0],  endDate[1]-1,  endDate[2]),
  			results = [];
  
  		results.push(startDate);
  		while (dCurrent.getTime() <= dEnd.getTime()) {
  			results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
  			dCurrent = DateMath.add(dCurrent,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.
  	*/
  	resetRenderers : function() {
  		this.renderStack = this._renderStack.concat();
  	},
  	
  	/**
  	 * Removes all custom renderers added to the Calendar through the addRenderer, addMonthRenderer and 
  	 * addWeekdayRenderer methods. Calendar's render method needs to be called after removing renderers 
  	 * to re-render the Calendar without custom renderers applied.
  	 */
  	removeRenderers : function() {
  		this._renderStack = [];
  		this.renderStack = [];
  	},
  
  	/**
  	* Clears the inner HTML, CSS class and style information from the specified cell.
  	* @method clearElement
  	* @param	{HTMLTableCellElement} cell The cell to clear
  	*/ 
  	clearElement : function(cell) {
  		cell.innerHTML = "&#160;";
  		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.
  	* @method addRenderer
  	* @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.
  	*/
  	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(Calendar.RANGE,aDate,fnRender);
  				} else { // this is a month/day combo
  					this._addRenderer(Calendar.MONTH_DAY,aDate,fnRender);
  				}
  			} else if (aDate.length == 3) {
  				this._addRenderer(Calendar.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.
  	* @method _addRenderer
  	* @private
  	* @param	{String}	type		The type string that indicates the type of date renderer being added.
  	*									Values are YAHOO.widget.Calendar.DATE, YAHOO.widget.Calendar.MONTH_DAY, YAHOO.widget.Calendar.WEEKDAY,
  	*									YAHOO.widget.Calendar.RANGE, YAHOO.widget.Calendar.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.
  	*/
  	_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.
  	* @method addMonthRenderer
  	* @param	{Number}	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.
  	*/
  	addMonthRenderer : function(month, fnRender) {
  		this._addRenderer(Calendar.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.
  	* @method addWeekdayRenderer
  	* @param	{Number}	weekday		The weekday (Sunday = 1, Monday = 2 ... Saturday = 7) to associate with this renderer
  	* @param	{Function}	fnRender	The function executed to render cells that match the render rules for this renderer.
  	*/
  	addWeekdayRenderer : function(weekday, fnRender) {
  		this._addRenderer(Calendar.WEEKDAY,[weekday],fnRender);
  	},
  
  	// END RENDERER METHODS
  	
  	// BEGIN CSS METHODS
  	
  	/**
  	* Removes all styles from all body cells in the current calendar table.
  	* @method clearAllBodyCellStyles
  	* @param	{style}	style The CSS class name to remove from all calendar body cells
  	*/
  	clearAllBodyCellStyles : function(style) {
  		for (var c=0;c<this.cells.length;++c) {
  			Dom.removeClass(this.cells[c],style);
  		}
  	},
  	
  	// END CSS METHODS
  	
  	// BEGIN GETTER/SETTER METHODS
  	/**
  	* Sets the calendar's month explicitly
  	* @method setMonth
  	* @param {Number}	month		The numeric month, from 0 (January) to 11 (December)
  	*/
  	setMonth : function(month) {
  		var cfgPageDate = DEF_CFG.PAGEDATE.key,
  			current = this.cfg.getProperty(cfgPageDate);
  		current.setMonth(parseInt(month, 10));
  		this.cfg.setProperty(cfgPageDate, current);
  	},
  
  	/**
  	* Sets the calendar's year explicitly.
  	* @method setYear
  	* @param {Number}	year		The numeric 4-digit year
  	*/
  	setYear : function(year) {
  		var cfgPageDate = DEF_CFG.PAGEDATE.key,
  			current = this.cfg.getProperty(cfgPageDate);
  
  		current.setFullYear(parseInt(year, 10));
  		this.cfg.setProperty(cfgPageDate, current);
  	},
  
  	/**
  	* Gets the list of currently selected dates from the calendar.
  	* @method getSelectedDates
  	* @return {Date[]} An array of currently selected JavaScript Date objects.
  	*/
  	getSelectedDates : function() {
  		var returnDates = [],
  			selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
  
  		for (var d=0;d<selected.length;++d) {
  			var dateArray = selected[d];
  
  			var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
  			returnDates.push(date);
  		}
  
  		returnDates.sort( function(a,b) { return a-b; } );
  		return returnDates;
  	},
  
  	/// END GETTER/SETTER METHODS ///
  	
  	/**
  	* Hides the Calendar's outer container from view.
  	* @method hide
  	*/
  	hide : function() {
  		if (this.beforeHideEvent.fire()) {
  			this.oDomContainer.style.display = "none";
  			this.hideEvent.fire();
  		}
  	},
  
  	/**
  	* Shows the Calendar's outer container.
  	* @method show
  	*/
  	show : function() {
  		if (this.beforeShowEvent.fire()) {
  			this.oDomContainer.style.display = "block";
  			this.showEvent.fire();
  		}
  	},
  
  	/**
  	* Returns a string representing the current browser.
  	* @deprecated As of 2.3.0, environment information is available in YAHOO.env.ua
  	* @see YAHOO.env.ua
  	* @property browser
  	* @type String
  	*/
  	browser : (function() {
  				var ua = navigator.userAgent.toLowerCase();
  					  if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof)
  						 return 'opera';
  					  } else if (ua.indexOf('msie 7')!=-1) { // IE7
  						 return 'ie7';
  					  } 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.
  	* @method toString
  	* @return {String}	A string representation of the Calendar object.
  	*/
  	toString : function() {
  		return "Calendar " + this.id;
  	},
  
  	/**
  	 * Destroys the Calendar instance. The method will remove references
  	 * to HTML elements, remove any event listeners added by the Calendar,
  	 * and destroy the Config and CalendarNavigator instances it has created.
  	 *
  	 * @method destroy
  	 */
  	destroy : function() {
  
  		if (this.beforeDestroyEvent.fire()) {
  			var cal = this;
  
  			// Child objects
  			if (cal.navigator) {
  				cal.navigator.destroy();
  			}
  
  			if (cal.cfg) {
  				cal.cfg.destroy();
  			}
  
  			// DOM event listeners
  			Event.purgeElement(cal.oDomContainer, true);
  
  			// Generated markup/DOM - Not removing the container DIV since we didn't create it.
  			Dom.removeClass(cal.oDomContainer, "withtitle");
  			Dom.removeClass(cal.oDomContainer, cal.Style.CSS_CONTAINER);
  			Dom.removeClass(cal.oDomContainer, cal.Style.CSS_SINGLE);
  			cal.oDomContainer.innerHTML = "";
  
  			// JS-to-DOM references
  			cal.oDomContainer = null;
  			cal.cells = null;
  
  			this.destroyEvent.fire();
  		}
  	}
  };
  
  YAHOO.widget.Calendar = Calendar;
  
  /**
  * @namespace YAHOO.widget
  * @class Calendar_Core
  * @extends YAHOO.widget.Calendar
  * @deprecated The old Calendar_Core class is no longer necessary.
  */
  YAHOO.widget.Calendar_Core = YAHOO.widget.Calendar;
  
  YAHOO.widget.Cal_Core = YAHOO.widget.Calendar;
  
  })();
  
  (function() {
  
  	var Dom = YAHOO.util.Dom,
  		DateMath = YAHOO.widget.DateMath,
  		Event = YAHOO.util.Event,
  		Lang = YAHOO.lang,
  		Calendar = YAHOO.widget.Calendar;
  
  /**
  * YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar. This class facilitates
  * the ability to have multi-page calendar views that share a single dataset and are
  * dependent on each other.
  *
  * 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>
  * <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
  * The CalendarGroup can be constructed by simply providing a container ID string, 
  * or a reference to a container DIV HTMLElement (the element needs to exist 
  * in the document).
  * 
  * E.g.:
  *	<xmp>
  *		var c = new YAHOO.widget.CalendarGroup("calContainer", configOptions);
  *	</xmp>
  * or:
  *   <xmp>
  *       var containerDiv = YAHOO.util.Dom.get("calContainer");
  *		var c = new YAHOO.widget.CalendarGroup(containerDiv, configOptions);
  *	</xmp>
  * </p>
  * <p>
  * If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
  * For example if an ID is not provided, and the container's ID is "calContainer", the CalendarGroup's ID will be set to "calContainer_t".
  * </p>
  * 
  * @namespace YAHOO.widget
  * @class CalendarGroup
  * @constructor
  * @param {String} id optional The id of the table element that will represent the CalendarGroup widget. As of 2.4.0, this argument is optional.
  * @param {String | HTMLElement} container The id of the container div element that will wrap the CalendarGroup table, or a reference to a DIV element which exists in the document.
  * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
  */
  function CalendarGroup(id, containerId, config) {
  	if (arguments.length > 0) {
  		this.init.apply(this, arguments);
  	}
  }
  
  /**
  * The set of default Config property keys and values for the CalendarGroup
  * @property YAHOO.widget.CalendarGroup._DEFAULT_CONFIG
  * @final
  * @static
  * @private
  * @type Object
  */
  CalendarGroup._DEFAULT_CONFIG = Calendar._DEFAULT_CONFIG;
  CalendarGroup._DEFAULT_CONFIG.PAGES = {key:"pages", value:2};
  
  var DEF_CFG = CalendarGroup._DEFAULT_CONFIG;
  
  CalendarGroup.prototype = {
  
  	/**
  	* Initializes the calendar group. All subclasses must call this method in order for the
  	* group to be initialized properly.
  	* @method init
  	* @param {String} id optional The id of the table element that will represent the CalendarGroup widget. As of 2.4.0, this argument is optional.
  	* @param {String | HTMLElement} container The id of the container div element that will wrap the CalendarGroup table, or a reference to a DIV element which exists in the document.
  	* @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
  	*/
  	init : function(id, container, config) {
  
  		// Normalize 2.4.0, pre 2.4.0 args
  		var nArgs = this._parseArgs(arguments);
  
  		id = nArgs.id;
  		container = nArgs.container;
  		config = nArgs.config;
  
  		this.oDomContainer = Dom.get(container);
  
  		if (!this.oDomContainer.id) {
  			this.oDomContainer.id = Dom.generateId();
  		}
  		if (!id) {
  			id = this.oDomContainer.id + "_t";
  		}
  
  		/**
  		* The unique id associated with the CalendarGroup
  		* @property id
  		* @type String
  		*/
  		this.id = id;
  
  		/**
  		* The unique id associated with the CalendarGroup container
  		* @property containerId
  		* @type String
  		*/
  		this.containerId = this.oDomContainer.id;
  
  		this.initEvents();
  		this.initStyles();
  
  		/**
  		* The collection of Calendar pages contained within the CalendarGroup
  		* @property pages
  		* @type YAHOO.widget.Calendar[]
  		*/
  		this.pages = [];
  
  		Dom.addClass(this.oDomContainer, CalendarGroup.CSS_CONTAINER);
  		Dom.addClass(this.oDomContainer, CalendarGroup.CSS_MULTI_UP);
  
  		/**
  		* The Config object used to hold the configuration variables for the CalendarGroup
  		* @property cfg
  		* @type YAHOO.util.Config
  		*/
  		this.cfg = new YAHOO.util.Config(this);
  
  		/**
  		* The local object which contains the CalendarGroup's options
  		* @property Options
  		* @type Object
  		*/
  		this.Options = {};
  
  		/**
  		* The local object which contains the CalendarGroup's locale settings
  		* @property Locale
  		* @type Object
  		*/
  		this.Locale = {};
  
  		this.setupConfig();
  
  		if (config) {
  			this.cfg.applyConfig(config, true);
  		}
  
  		this.cfg.fireQueue();
  
  		// OPERA HACK FOR MISWRAPPED FLOATS
  		if (YAHOO.env.ua.opera){
  			this.renderEvent.subscribe(this._fixWidth, this, true);
  			this.showEvent.subscribe(this._fixWidth, this, true);
  		}
  
  	},
  
  	setupConfig : function() {
  
  		var cfg = this.cfg;
  
  		/**
  		* The number of pages to include in the CalendarGroup. This value can only be set once, in the CalendarGroup's constructor arguments.
  		* @config pages
  		* @type Number
  		* @default 2
  		*/
  		cfg.addProperty(DEF_CFG.PAGES.key, { value:DEF_CFG.PAGES.value, validator:cfg.checkNumber, handler:this.configPages } );
  
  		/**
  		* The month/year representing the current visible Calendar date (mm/yyyy)
  		* @config pagedate
  		* @type String | Date
  		* @default today's date
  		*/
  		cfg.addProperty(DEF_CFG.PAGEDATE.key, { value:new Date(), handler:this.configPageDate } );
  
  		/**
  		* The date or range of dates representing the current Calendar selection
  		*
  		* @config selected
  		* @type String
  		* @default []
  		*/
  		cfg.addProperty(DEF_CFG.SELECTED.key, { value:[], handler:this.configSelected } );
  
  		/**
  		* The title to display above the CalendarGroup's month header
  		* @config title
  		* @type String
  		* @default ""
  		*/
  		cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
  
  		/**
  		* Whether or not a close button should be displayed for this CalendarGroup
  		* @config close
  		* @type Boolean
  		* @default false
  		*/
  		cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
  
  		/**
  		* Whether or not an iframe shim should be placed under the Calendar to prevent select boxes from bleeding through in Internet Explorer 6 and below.
  		* This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be 
  		* enabled if required.
  		* 
  		* @config iframe
  		* @type Boolean
  		* @default true for IE6 and below, false for all other browsers
  		*/
  		cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
  	
  		/**
  		* The minimum selectable date in the current Calendar (mm/dd/yyyy)
  		* @config mindate
  		* @type String | Date
  		* @default null
  		*/
  		cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.delegateConfig } );
  	
  		/**
  		* The maximum selectable date in the current Calendar (mm/dd/yyyy)
  		* @config maxdate
  		* @type String | Date
  		* @default null
  		*/
  		cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.delegateConfig  } );
  	
  		// Options properties
  
  		/**
  		* True if the Calendar should allow multiple selections. False by default.
  		* @config MULTI_SELECT
  		* @type Boolean
  		* @default false
  		*/
  		cfg.addProperty(DEF_CFG.MULTI_SELECT.key,	{ value:DEF_CFG.MULTI_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
  
  		/**
  		* The weekday the week begins on. Default is 0 (Sunday).
  		* @config START_WEEKDAY
  		* @type number
  		* @default 0
  		*/	
  		cfg.addProperty(DEF_CFG.START_WEEKDAY.key,	{ value:DEF_CFG.START_WEEKDAY.value, handler:this.delegateConfig, validator:cfg.checkNumber  } );
  		
  		/**
  		* True if the Calendar should show weekday labels. True by default.
  		* @config SHOW_WEEKDAYS
  		* @type Boolean
  		* @default true
  		*/	
  		cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key,	{ value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
  		
  		/**
  		* True if the Calendar should show week row headers. False by default.
  		* @config SHOW_WEEK_HEADER
  		* @type Boolean
  		* @default false
  		*/	
  		cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key,{ value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
  		
  		/**
  		* True if the Calendar should show week row footers. False by default.
  		* @config SHOW_WEEK_FOOTER
  		* @type Boolean
  		* @default false
  		*/
  		cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
  		
  		/**
  		* True if the Calendar should suppress weeks that are not a part of the current month. False by default.
  		* @config HIDE_BLANK_WEEKS
  		* @type Boolean
  		* @default false
  		*/		
  		cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key,{ value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
  		
  		/**
  		* The image that should be used for the left navigation arrow.
  		* @config NAV_ARROW_LEFT
  		* @type String
  		* @deprecated	You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
  		* @default null
  		*/		
  		cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key,	{ value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.delegateConfig } );
  		
  		/**
  		* The image that should be used for the right navigation arrow.
  		* @config NAV_ARROW_RIGHT
  		* @type String
  		* @deprecated	You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
  		* @default null
  		*/		
  		cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key,	{ value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.delegateConfig } );
  	
  		// Locale properties
  		
  		/**
  		* The short month labels for the current locale.
  		* @config MONTHS_SHORT
  		* @type String[]
  		* @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  		*/
  		cfg.addProperty(DEF_CFG.MONTHS_SHORT.key,	{ value:DEF_CFG.MONTHS_SHORT.value, handler:this.delegateConfig } );
  		
  		/**
  		* The long month labels for the current locale.
  		* @config MONTHS_LONG
  		* @type String[]
  		* @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
  		*/		
  		cfg.addProperty(DEF_CFG.MONTHS_LONG.key,		{ value:DEF_CFG.MONTHS_LONG.value, handler:this.delegateConfig } );
  		
  		/**
  		* The 1-character weekday labels for the current locale.
  		* @config WEEKDAYS_1CHAR
  		* @type String[]
  		* @default ["S", "M", "T", "W", "T", "F", "S"]
  		*/		
  		cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key,	{ value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.delegateConfig } );
  		
  		/**
  		* The short weekday labels for the current locale.
  		* @config WEEKDAYS_SHORT
  		* @type String[]
  		* @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
  		*/		
  		cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key,	{ value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.delegateConfig } );
  		
  		/**
  		* The medium weekday labels for the current locale.
  		* @config WEEKDAYS_MEDIUM
  		* @type String[]
  		* @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
  		*/		
  		cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key,	{ value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.delegateConfig } );
  		
  		/**
  		* The long weekday labels for the current locale.
  		* @config WEEKDAYS_LONG
  		* @type String[]
  		* @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
  		*/		
  		cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key,	{ value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.delegateConfig } );
  	
  		/**
  		* The setting that determines which length of month labels should be used. Possible values are "short" and "long".
  		* @config LOCALE_MONTHS
  		* @type String
  		* @default "long"
  		*/
  		cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key,	{ value:DEF_CFG.LOCALE_MONTHS.value, handler:this.delegateConfig } );
  	
  		/**
  		* The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
  		* @config LOCALE_WEEKDAYS
  		* @type String
  		* @default "short"
  		*/	
  		cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key,	{ value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.delegateConfig } );
  	
  		/**
  		* The value used to delimit individual dates in a date string passed to various Calendar functions.
  		* @config DATE_DELIMITER
  		* @type String
  		* @default ","
  		*/
  		cfg.addProperty(DEF_CFG.DATE_DELIMITER.key,		{ value:DEF_CFG.DATE_DELIMITER.value, handler:this.delegateConfig } );
  	
  		/**
  		* The value used to delimit date fields in a date string passed to various Calendar functions.
  		* @config DATE_FIELD_DELIMITER
  		* @type String
  		* @default "/"
  		*/	
  		cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key,{ value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.delegateConfig } );
  	
  		/**
  		* The value used to delimit date ranges in a date string passed to various Calendar functions.
  		* @config DATE_RANGE_DELIMITER
  		* @type String
  		* @default "-"
  		*/
  		cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key,{ value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.delegateConfig } );
  	
  		/**
  		* The position of the month in a month/year date string
  		* @config MY_MONTH_POSITION
  		* @type Number
  		* @default 1
  		*/
  		cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key,	{ value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
  		
  		/**
  		* The position of the year in a month/year date string
  		* @config MY_YEAR_POSITION
  		* @type Number
  		* @default 2
  		*/	
  		cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key,	{ value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
  		
  		/**
  		* The position of the month in a month/day date string
  		* @config MD_MONTH_POSITION
  		* @type Number
  		* @default 1
  		*/	
  		cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key,	{ value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
  		
  		/**
  		* The position of the day in a month/year date string
  		* @config MD_DAY_POSITION
  		* @type Number
  		* @default 2
  		*/	
  		cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key,		{ value:DEF_CFG.MD_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
  		
  		/**
  		* The position of the month in a month/day/year date string
  		* @config MDY_MONTH_POSITION
  		* @type Number
  		* @default 1
  		*/	
  		cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key,	{ value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
  		
  		/**
  		* The position of the day in a month/day/year date string
  		* @config MDY_DAY_POSITION
  		* @type Number
  		* @default 2
  		*/	
  		cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key,	{ value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
  		
  		/**
  		* The position of the year in a month/day/year date string
  		* @config MDY_YEAR_POSITION
  		* @type Number
  		* @default 3
  		*/	
  		cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key,	{ value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
  	
  		/**
  		* The position of the month in the month year label string used as the Calendar header
  		* @config MY_LABEL_MONTH_POSITION
  		* @type Number
  		* @default 1
  		*/
  		cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key,	{ value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
  	
  		/**
  		* The position of the year in the month year label string used as the Calendar header
  		* @config MY_LABEL_YEAR_POSITION
  		* @type Number
  		* @default 2
  		*/
  		cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key,	{ value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
  
  		/**
  		* The suffix used after the month when rendering the Calendar header
  		* @config MY_LABEL_MONTH_SUFFIX
  		* @type String
  		* @default " "
  		*/
  		cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key,	{ value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.delegateConfig } );
  		
  		/**
  		* The suffix used after the year when rendering the Calendar header
  		* @config MY_LABEL_YEAR_SUFFIX
  		* @type String
  		* @default ""
  		*/
  		cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.delegateConfig } );
  
  		/**
  		* Configuration for the Month Year Navigation UI. By default it is disabled
  		* @config NAV
  		* @type Object
  		* @default null
  		*/
  		cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
  
  		/**
  		 * The map of UI strings which the CalendarGroup UI uses.
  		 *
  		 * @config strings
  		 * @type {Object}
  		 * @default An object with the properties shown below:
  		 *     <dl>
  		 *         <dt>previousMonth</dt><dd><em>String</em> : The string to use for the "Previous Month" navigation UI. Defaults to "Previous Month".</dd>
  		 *         <dt>nextMonth</dt><dd><em>String</em> : The string to use for the "Next Month" navigation UI. Defaults to "Next Month".</dd>
  		 *         <dt>close</dt><dd><em>String</em> : The string to use for the close button label. Defaults to "Close".</dd>
  		 *     </dl>
  		 */
  		cfg.addProperty(DEF_CFG.STRINGS.key, { 
  			value:DEF_CFG.STRINGS.value, 
  			handler:this.configStrings, 
  			validator: function(val) {
  				return Lang.isObject(val);
  			},
  			supercedes: DEF_CFG.STRINGS.supercedes
  		});
  	},
  
  	/**
  	* Initializes CalendarGroup's built-in CustomEvents
  	* @method initEvents
  	*/
  	initEvents : function() {
  
  		var me = this,
  			strEvent = "Event",
  			CE = YAHOO.util.CustomEvent;
  
  		/**
  		* Proxy subscriber to subscribe to the CalendarGroup's child Calendars' CustomEvents
  		* @method sub
  		* @private
  		* @param {Function} fn	The function to subscribe to this CustomEvent
  		* @param {Object}	obj	The CustomEvent's scope object
  		* @param {Boolean}	bOverride	Whether or not to apply scope correction
  		*/
  		var sub = function(fn, obj, bOverride) {
  			for (var p=0;p<me.pages.length;++p) {
  				var cal = me.pages[p];
  				cal[this.type + strEvent].subscribe(fn, obj, bOverride);
  			}
  		};
  
  		/**
  		* Proxy unsubscriber to unsubscribe from the CalendarGroup's child Calendars' CustomEvents
  		* @method unsub
  		* @private
  		* @param {Function} fn	The function to subscribe to this CustomEvent
  		* @param {Object}	obj	The CustomEvent's scope object
  		*/
  		var unsub = function(fn, obj) {
  			for (var p=0;p<me.pages.length;++p) {
  				var cal = me.pages[p];
  				cal[this.type + strEvent].unsubscribe(fn, obj);
  			}
  		};
  		
  		var defEvents = Calendar._EVENT_TYPES;
  	
  		/**
  		* Fired before a selection is made
  		* @event beforeSelectEvent
  		*/
  		me.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
  		me.beforeSelectEvent.subscribe = sub; me.beforeSelectEvent.unsubscribe = unsub;
  	
  		/**
  		* Fired when a selection is made
  		* @event selectEvent
  		* @param {Array}	Array of Date field arrays in the format [YYYY, MM, DD].
  		*/
  		me.selectEvent = new CE(defEvents.SELECT); 
  		me.selectEvent.subscribe = sub; me.selectEvent.unsubscribe = unsub;
  	
  		/**
  		* Fired before a selection is made
  		* @event beforeDeselectEvent
  		*/
  		me.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT); 
  		me.beforeDeselectEvent.subscribe = sub; me.beforeDeselectEvent.unsubscribe = unsub;
  	
  		/**
  		* Fired when a selection is made
  		* @event deselectEvent
  		* @param {Array}	Array of Date field arrays in the format [YYYY, MM, DD].
  		*/
  		me.deselectEvent = new CE(defEvents.DESELECT); 
  		me.deselectEvent.subscribe = sub; me.deselectEvent.unsubscribe = unsub;
  		
  		/**
  		* Fired when the Calendar page is changed
  		* @event changePageEvent
  		*/
  		me.changePageEvent = new CE(defEvents.CHANGE_PAGE); 
  		me.changePageEvent.subscribe = sub; me.changePageEvent.unsubscribe = unsub;
  
  		/**
  		* Fired before the Calendar is rendered
  		* @event beforeRenderEvent
  		*/
  		me.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
  		me.beforeRenderEvent.subscribe = sub; me.beforeRenderEvent.unsubscribe = unsub;
  	
  		/**
  		* Fired when the Calendar is rendered
  		* @event renderEvent
  		*/
  		me.renderEvent = new CE(defEvents.RENDER);
  		me.renderEvent.subscribe = sub; me.renderEvent.unsubscribe = unsub;
  	
  		/**
  		* Fired when the Calendar is reset
  		* @event resetEvent
  		*/
  		me.resetEvent = new CE(defEvents.RESET); 
  		me.resetEvent.subscribe = sub; me.resetEvent.unsubscribe = unsub;
  	
  		/**
  		* Fired when the Calendar is cleared
  		* @event clearEvent
  		*/
  		me.clearEvent = new CE(defEvents.CLEAR);
  		me.clearEvent.subscribe = sub; me.clearEvent.unsubscribe = unsub;
  
  		/**
  		* Fired just before the CalendarGroup is to be shown
  		* @event beforeShowEvent
  		*/
  		me.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
  	
  		/**
  		* Fired after the CalendarGroup is shown
  		* @event showEvent
  		*/
  		me.showEvent = new CE(defEvents.SHOW);
  	
  		/**
  		* Fired just before the CalendarGroup is to be hidden
  		* @event beforeHideEvent
  		*/
  		me.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
  	
  		/**
  		* Fired after the CalendarGroup is hidden
  		* @event hideEvent
  		*/
  		me.hideEvent = new CE(defEvents.HIDE);
  
  		/**
  		* Fired just before the CalendarNavigator is to be shown
  		* @event beforeShowNavEvent
  		*/
  		me.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
  	
  		/**
  		* Fired after the CalendarNavigator is shown
  		* @event showNavEvent
  		*/
  		me.showNavEvent = new CE(defEvents.SHOW_NAV);
  	
  		/**
  		* Fired just before the CalendarNavigator is to be hidden
  		* @event beforeHideNavEvent
  		*/
  		me.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
  
  		/**
  		* Fired after the CalendarNavigator is hidden
  		* @event hideNavEvent
  		*/
  		me.hideNavEvent = new CE(defEvents.HIDE_NAV);
  
  		/**
  		* Fired just before the CalendarNavigator is to be rendered
  		* @event beforeRenderNavEvent
  		*/
  		me.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
  
  		/**
  		* Fired after the CalendarNavigator is rendered
  		* @event renderNavEvent
  		*/
  		me.renderNavEvent = new CE(defEvents.RENDER_NAV);
  
  		/**
  		* Fired just before the CalendarGroup is to be destroyed
  		* @event beforeDestroyEvent
  		*/
  		me.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
  
  		/**
  		* Fired after the CalendarGroup is destroyed. This event should be used
  		* for notification only. When this event is fired, important CalendarGroup instance
  		* properties, dom references and event listeners have already been 
  		* removed/dereferenced, and hence the CalendarGroup instance is not in a usable 
  		* state.
  		*
  		* @event destroyEvent
  		*/
  		me.destroyEvent = new CE(defEvents.DESTROY);
  	},
  	
  	/**
  	* The default Config handler for the "pages" property
  	* @method configPages
  	* @param {String} type	The CustomEvent type (usually the property name)
  	* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
  	* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
  	*/
  	configPages : function(type, args, obj) {
  		var pageCount = args[0],
  			cfgPageDate = DEF_CFG.PAGEDATE.key,
  			sep = "_",
  			groupCalClass = "groupcal",
  			firstClass = "first-of-type",
  			lastClass = "last-of-type";
  
  		for (var p=0;p<pageCount;++p) {
  			var calId = this.id + sep + p,
  				calContainerId = this.containerId + sep + p,
  				childConfig = this.cfg.getConfig();
  
  			childConfig.close = false;
  			childConfig.title = false;
  			childConfig.navigator = null;
  
  			var cal = this.constructChild(calId, calContainerId, childConfig);
  			var caldate = cal.cfg.getProperty(cfgPageDate);
  			this._setMonthOnDate(caldate, caldate.getMonth() + p);
  			cal.cfg.setProperty(cfgPageDate, caldate);
  
  			Dom.removeClass(cal.oDomContainer, this.Style.CSS_SINGLE);
  			Dom.addClass(cal.oDomContainer, groupCalClass);
  
  			if (p===0) {
  				Dom.addClass(cal.oDomContainer, firstClass);
  			}
  	
  			if (p==(pageCount-1)) {
  				Dom.addClass(cal.oDomContainer, lastClass);
  			}
  	
  			cal.parent = this;
  			cal.index = p; 
  	
  			this.pages[this.pages.length] = cal;
  		}
  	},
  	
  	/**
  	* The default Config handler for the "pagedate" property
  	* @method configPageDate
  	* @param {String} type	The CustomEvent type (usually the property name)
  	* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
  	* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
  	*/
  	configPageDate : function(type, args, obj) {
  		var val = args[0],
  			firstPageDate;
  
  		var cfgPageDate = DEF_CFG.PAGEDATE.key;
  		
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			if (p === 0) {
  				firstPageDate = cal._parsePageDate(val);
  				cal.cfg.setProperty(cfgPageDate, firstPageDate);
  			} else {
  				var pageDate = new Date(firstPageDate);
  				this._setMonthOnDate(pageDate, pageDate.getMonth() + p);
  				cal.cfg.setProperty(cfgPageDate, pageDate);
  			}
  		}
  	},
  	
  	/**
  	* The default Config handler for the CalendarGroup "selected" property
  	* @method configSelected
  	* @param {String} type	The CustomEvent type (usually the property name)
  	* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
  	* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
  	*/
  	configSelected : function(type, args, obj) {
  		var cfgSelected = DEF_CFG.SELECTED.key;
  		this.delegateConfig(type, args, obj);
  		var selected = (this.pages.length > 0) ? this.pages[0].cfg.getProperty(cfgSelected) : []; 
  		this.cfg.setProperty(cfgSelected, selected, true);
  	},
  
  	
  	/**
  	* Delegates a configuration property to the CustomEvents associated with the CalendarGroup's children
  	* @method delegateConfig
  	* @param {String} type	The CustomEvent type (usually the property name)
  	* @param {Object[]}	args	The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
  	* @param {Object} obj	The scope object. For configuration handlers, this will usually equal the owner.
  	*/
  	delegateConfig : function(type, args, obj) {
  		var val = args[0];
  		var cal;
  	
  		for (var p=0;p<this.pages.length;p++) {
  			cal = this.pages[p];
  			cal.cfg.setProperty(type, val);
  		}
  	},
  
  	/**
  	* Adds a function to all child Calendars within this CalendarGroup.
  	* @method setChildFunction
  	* @param {String}		fnName		The name of the function
  	* @param {Function}		fn			The function to apply to each Calendar page object
  	*/
  	setChildFunction : function(fnName, fn) {
  		var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
  	
  		for (var p=0;p<pageCount;++p) {
  			this.pages[p][fnName] = fn;
  		}
  	},
  
  	/**
  	* Calls a function within all child Calendars within this CalendarGroup.
  	* @method callChildFunction
  	* @param {String}		fnName		The name of the function
  	* @param {Array}		args		The arguments to pass to the function
  	*/
  	callChildFunction : function(fnName, args) {
  		var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
  	
  		for (var p=0;p<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.
  	* @method constructChild
  	* @param {String}	id			The id of the table element that will represent the calendar widget
  	* @param {String}	containerId	The id of the container div element that will wrap the calendar table
  	* @param {Object}	config		The configuration object containing the Calendar's arguments
  	* @return {YAHOO.widget.Calendar}	The YAHOO.widget.Calendar instance that is constructed
  	*/
  	constructChild : function(id,containerId,config) {
  		var container = document.getElementById(containerId);
  		if (! container) {
  			container = document.createElement("div");
  			container.id = containerId;
  			this.oDomContainer.appendChild(container);
  		}
  		return new Calendar(id,containerId,config);
  	},
  	
  	/**
  	* 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.
  	* @method setMonth
  	* @param {Number}	month		The numeric month, from 0 (January) to 11 (December)
  	*/
  	setMonth : function(month) {
  		month = parseInt(month, 10);
  		var currYear;
  
  		var cfgPageDate = DEF_CFG.PAGEDATE.key;
  
  		for (var p=0; p<this.pages.length; ++p) {
  			var cal = this.pages[p];
  			var pageDate = cal.cfg.getProperty(cfgPageDate);
  			if (p === 0) {
  				currYear = pageDate.getFullYear();
  			} else {
  				pageDate.setFullYear(currYear);
  			}
  			this._setMonthOnDate(pageDate, month+p); 
  			cal.cfg.setProperty(cfgPageDate, pageDate);
  		}
  	},
  
  	/**
  	* 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.
  	* @method setYear
  	* @param {Number}	year		The numeric 4-digit year
  	*/
  	setYear : function(year) {
  	
  		var cfgPageDate = DEF_CFG.PAGEDATE.key;
  	
  		year = parseInt(year, 10);
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			var pageDate = cal.cfg.getProperty(cfgPageDate);
  	
  			if ((pageDate.getMonth()+1) == 1 && p>0) {
  				year+=1;
  			}
  			cal.setYear(year);
  		}
  	},
  
  	/**
  	* Calls the render function of all child calendars within the group.
  	* @method render
  	*/
  	render : function() {
  		this.renderHeader();
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.render();
  		}
  		this.renderFooter();
  	},
  
  	/**
  	* 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.
  	* @method select
  	* @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	{Date[]}			Array of JavaScript Date objects representing all individual dates that are currently selected.
  	*/
  	select : function(date) {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.select(date);
  		}
  		return this.getSelectedDates();
  	},
  
  	/**
  	* Selects dates in the CalendarGroup based on the cell index provided. This method is used to select cells without having to do a full render. The selected style is applied to the cells directly.
  	* The value of the MULTI_SELECT Configuration attribute will determine the set of dates which get selected. 
  	* <ul>
  	*    <li>If MULTI_SELECT is false, selectCell will select the cell at the specified index for only the last displayed Calendar page.</li>
  	*    <li>If MULTI_SELECT is true, selectCell will select the cell at the specified index, on each displayed Calendar page.</li>
  	* </ul>
  	* @method selectCell
  	* @param	{Number}	cellIndex	The index of the cell to be selected. 
  	* @return	{Date[]}	Array of JavaScript Date objects representing all individual dates that are currently selected.
  	*/
  	selectCell : function(cellIndex) {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.selectCell(cellIndex);
  		}
  		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.
  	* @method deselect
  	* @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	{Date[]}			Array of JavaScript Date objects representing all individual dates that are currently selected.
  	*/
  	deselect : function(date) {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.deselect(date);
  		}
  		return this.getSelectedDates();
  	},
  	
  	/**
  	* Deselects all dates on the current calendar.
  	* @method deselectAll
  	* @return {Date[]}		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.
  	*/
  	deselectAll : function() {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.deselectAll();
  		}
  		return this.getSelectedDates();
  	},
  	
  	/**
  	* Deselects dates in the CalendarGroup based on the cell index provided. This method is used to select cells without having to do a full render. The selected style is applied to the cells directly.
  	* deselectCell will deselect the cell at the specified index on each displayed Calendar page.
  	*
  	* @method deselectCell
  	* @param	{Number}	cellIndex	The index of the cell to deselect. 
  	* @return	{Date[]}	Array of JavaScript Date objects representing all individual dates that are currently selected.
  	*/
  	deselectCell : function(cellIndex) {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.deselectCell(cellIndex);
  		}
  		return this.getSelectedDates();
  	},
  	
  	/**
  	* Resets the calendar widget to the originally selected month and year, and 
  	* sets the calendar to the initial selection(s).
  	* @method reset
  	*/
  	reset : function() {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.reset();
  		}
  	},
  
  	/**
  	* Clears the selected dates in the current calendar widget and sets the calendar
  	* to the current month and year.
  	* @method clear
  	*/
  	clear : function() {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.clear();
  		}
  
  		this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
  		this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.pages[0].today.getTime()));
  		this.render();
  	},
  
  	/**
  	* Navigates to the next month page in the calendar widget.
  	* @method nextMonth
  	*/
  	nextMonth : function() {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.nextMonth();
  		}
  	},
  	
  	/**
  	* Navigates to the previous month page in the calendar widget.
  	* @method previousMonth
  	*/
  	previousMonth : function() {
  		for (var p=this.pages.length-1;p>=0;--p) {
  			var cal = this.pages[p];
  			cal.previousMonth();
  		}
  	},
  	
  	/**
  	* Navigates to the next year in the currently selected month in the calendar widget.
  	* @method nextYear
  	*/
  	nextYear : function() {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.nextYear();
  		}
  	},
  
  	/**
  	* Navigates to the previous year in the currently selected month in the calendar widget.
  	* @method previousYear
  	*/
  	previousYear : function() {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.previousYear();
  		}
  	},
  
  	/**
  	* Gets the list of currently selected dates from the calendar.
  	* @return			An array of currently selected JavaScript Date objects.
  	* @type Date[]
  	*/
  	getSelectedDates : function() { 
  		var returnDates = [];
  		var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
  		for (var d=0;d<selected.length;++d) {
  			var dateArray = selected[d];
  
  			var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
  			returnDates.push(date);
  		}
  
  		returnDates.sort( function(a,b) { return a-b; } );
  		return returnDates;
  	},
  
  	/**
  	* 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.
  	* @method addRenderer
  	* @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.
  	*/
  	addRenderer : function(sDates, fnRender) {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.addRenderer(sDates, fnRender);
  		}
  	},
  
  	/**
  	* 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.
  	* @method addMonthRenderer
  	* @param	{Number}	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.
  	*/
  	addMonthRenderer : function(month, fnRender) {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.addMonthRenderer(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.
  	* @method addWeekdayRenderer
  	* @param	{Number}	weekday		The weekday (1-7) to associate with this renderer. 1=Sunday, 2=Monday etc.
  	* @param	{Function}	fnRender	The function executed to render cells that match the render rules for this renderer.
  	*/
  	addWeekdayRenderer : function(weekday, fnRender) {
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			cal.addWeekdayRenderer(weekday, fnRender);
  		}
  	},
  
  	/**
  	 * Removes all custom renderers added to the CalendarGroup through the addRenderer, addMonthRenderer and 
  	 * addWeekRenderer methods. CalendarGroup's render method needs to be called to after removing renderers 
  	 * to see the changes applied.
  	 * 
  	 * @method removeRenderers
  	 */
  	removeRenderers : function() {
  		this.callChildFunction("removeRenderers");
  	},
  
  	/**
  	* Renders the header for the CalendarGroup.
  	* @method renderHeader
  	*/
  	renderHeader : function() {
  		// EMPTY DEFAULT IMPL
  	},
  
  	/**
  	* Renders a footer for the 2-up calendar container. By default, this method is
  	* unimplemented.
  	* @method renderFooter
  	*/
  	renderFooter : function() {
  		// EMPTY DEFAULT IMPL
  	},
  
  	/**
  	* Adds the designated number of months to the current calendar month, and sets the current
  	* calendar page date to the new month.
  	* @method addMonths
  	* @param {Number}	count	The number of months to add to the current calendar
  	*/
  	addMonths : function(count) {
  		this.callChildFunction("addMonths", count);
  	},
  	
  	/**
  	* Subtracts the designated number of months from the current calendar month, and sets the current
  	* calendar page date to the new month.
  	* @method subtractMonths
  	* @param {Number}	count	The number of months to subtract from the current calendar
  	*/
  	subtractMonths : function(count) {
  		this.callChildFunction("subtractMonths", count);
  	},
  
  	/**
  	* Adds the designated number of years to the current calendar, and sets the current
  	* calendar page date to the new month.
  	* @method addYears
  	* @param {Number}	count	The number of years to add to the current calendar
  	*/
  	addYears : function(count) {
  		this.callChildFunction("addYears", count);
  	},
  
  	/**
  	* Subtcats the designated number of years from the current calendar, and sets the current
  	* calendar page date to the new month.
  	* @method subtractYears
  	* @param {Number}	count	The number of years to subtract from the current calendar
  	*/
  	subtractYears : function(count) {
  		this.callChildFunction("subtractYears", count);
  	},
  
  	/**
  	 * Returns the Calendar page instance which has a pagedate (month/year) matching the given date. 
  	 * Returns null if no match is found.
  	 * 
  	 * @method getCalendarPage
  	 * @param {Date} date The JavaScript Date object for which a Calendar page is to be found.
  	 * @return {Calendar} The Calendar page instance representing the month to which the date 
  	 * belongs.
  	 */
  	getCalendarPage : function(date) {
  		var cal = null;
  		if (date) {
  			var y = date.getFullYear(),
  				m = date.getMonth();
  
  			var pages = this.pages;
  			for (var i = 0; i < pages.length; ++i) {
  				var pageDate = pages[i].cfg.getProperty("pagedate");
  				if (pageDate.getFullYear() === y && pageDate.getMonth() === m) {
  					cal = pages[i];
  					break;
  				}
  			}
  		}
  		return cal;
  	},
  
  	/**
  	* Sets the month on a Date object, taking into account year rollover if the month is less than 0 or greater than 11.
  	* The Date object passed in is modified. It should be cloned before passing it into this method if the original value needs to be maintained
  	* @method	_setMonthOnDate
  	* @private
  	* @param	{Date}	date	The Date object on which to set the month index
  	* @param	{Number}	iMonth	The month index to set
  	*/
  	_setMonthOnDate : function(date, iMonth) {
  		// Bug in Safari 1.3, 2.0 (WebKit build < 420), Date.setMonth does not work consistently if iMonth is not 0-11
  		if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420 && (iMonth < 0 || iMonth > 11)) {
  			var newDate = DateMath.add(date, DateMath.MONTH, iMonth-date.getMonth());
  			date.setTime(newDate.getTime());
  		} else {
  			date.setMonth(iMonth);
  		}
  	},
  	
  	/**
  	 * Fixes the width of the CalendarGroup container element, to account for miswrapped floats
  	 * @method _fixWidth
  	 * @private
  	 */
  	_fixWidth : function() {
  		var w = 0;
  		for (var p=0;p<this.pages.length;++p) {
  			var cal = this.pages[p];
  			w += cal.oDomContainer.offsetWidth;
  		}
  		if (w > 0) {
  			this.oDomContainer.style.width = w + "px";
  		}
  	},
  	
  	/**
  	* Returns a string representation of the object.
  	* @method toString
  	* @return {String}	A string representation of the CalendarGroup object.
  	*/
  	toString : function() {
  		return "CalendarGroup " + this.id;
  	},
  
  	/**
  	 * Destroys the CalendarGroup instance. The method will remove references
  	 * to HTML elements, remove any event listeners added by the CalendarGroup.
  	 * 
  	 * It will also destroy the Config and CalendarNavigator instances created by the 
  	 * CalendarGroup and the individual Calendar instances created for each page.
  	 *
  	 * @method destroy
  	 */
  	destroy : function() {
  
  		if (this.beforeDestroyEvent.fire()) {
  
  			var cal = this;
  	
  			// Child objects
  			if (cal.navigator) {
  				cal.navigator.destroy();
  			}
  	
  			if (cal.cfg) {
  				cal.cfg.destroy();
  			}
  	
  			// DOM event listeners
  			Event.purgeElement(cal.oDomContainer, true);
  	
  			// Generated markup/DOM - Not removing the container DIV since we didn't create it.
  			Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_CONTAINER);
  			Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_MULTI_UP);
  			
  			for (var i = 0, l = cal.pages.length; i < l; i++) {
  				cal.pages[i].destroy();
  				cal.pages[i] = null;
  			}
  	
  			cal.oDomContainer.innerHTML = "";
  	
  			// JS-to-DOM references
  			cal.oDomContainer = null;
  	
  			this.destroyEvent.fire();
  		}
  	}
  };
  
  /**
  * CSS class representing the container for the calendar
  * @property YAHOO.widget.CalendarGroup.CSS_CONTAINER
  * @static
  * @final
  * @type String
  */
  CalendarGroup.CSS_CONTAINER = "yui-calcontainer";
  
  /**
  * CSS class representing the container for the calendar
  * @property YAHOO.widget.CalendarGroup.CSS_MULTI_UP
  * @static
  * @final
  * @type String
  */
  CalendarGroup.CSS_MULTI_UP = "multi";
  
  /**
  * CSS class representing the title for the 2-up calendar
  * @property YAHOO.widget.CalendarGroup.CSS_2UPTITLE
  * @static
  * @final
  * @type String
  */
  CalendarGroup.CSS_2UPTITLE = "title";
  
  /**
  * CSS class representing the close icon for the 2-up calendar
  * @property YAHOO.widget.CalendarGroup.CSS_2UPCLOSE
  * @static
  * @final
  * @deprecated	Along with Calendar.IMG_ROOT and NAV_ARROW_LEFT, NAV_ARROW_RIGHT configuration properties.
  *					Calendar's <a href="YAHOO.widget.Calendar.html#Style.CSS_CLOSE">Style.CSS_CLOSE</a> property now represents the CSS class used to render the close icon
  * @type String
  */
  CalendarGroup.CSS_2UPCLOSE = "close-icon";
  
  YAHOO.lang.augmentProto(CalendarGroup, Calendar, "buildDayLabel",
  												 "buildMonthLabel",
  												 "renderOutOfBoundsDate",
  												 "renderRowHeader",
  												 "renderRowFooter",
  												 "renderCellDefault",
  												 "styleCellDefault",
  												 "renderCellStyleHighlight1",
  												 "renderCellStyleHighlight2",
  												 "renderCellStyleHighlight3",
  												 "renderCellStyleHighlight4",
  												 "renderCellStyleToday",
  												 "renderCellStyleSelected",
  												 "renderCellNotThisMonth",
  												 "renderBodyCellRestricted",
  												 "initStyles",
  												 "configTitle",
  												 "configClose",
  												 "configIframe",
  												 "configStrings",
  												 "configNavigator",
  												 "createTitleBar",
  												 "createCloseButton",
  												 "removeTitleBar",
  												 "removeCloseButton",
  												 "hide",
  												 "show",
  												 "toDate",
  												 "_toDate",
  												 "_parseArgs",
  												 "browser");
  
  YAHOO.widget.CalGrp = CalendarGroup;
  YAHOO.widget.CalendarGroup = CalendarGroup;
  
  /**
  * @class YAHOO.widget.Calendar2up
  * @extends YAHOO.widget.CalendarGroup
  * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
  */
  YAHOO.widget.Calendar2up = function(id, containerId, config) {
  	this.init(id, containerId, config);
  };
  
  YAHOO.extend(YAHOO.widget.Calendar2up, CalendarGroup);
  
  /**
  * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
  */
  YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up;
  
  })();
  
  /**
   * The CalendarNavigator is used along with a Calendar/CalendarGroup to 
   * provide a Month/Year popup navigation control, allowing the user to navigate 
   * to a specific month/year in the Calendar/CalendarGroup without having to 
   * scroll through months sequentially
   *
   * @namespace YAHOO.widget
   * @class CalendarNavigator
   * @constructor
   * @param {Calendar|CalendarGroup} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached.
   */
  YAHOO.widget.CalendarNavigator = function(cal) {
  	this.init(cal);
  };
  
  (function() {
  	// Setup static properties (inside anon fn, so that we can use shortcuts)
  	var CN = YAHOO.widget.CalendarNavigator;
  
  	/**
  	 * YAHOO.widget.CalendarNavigator.CLASSES contains constants
  	 * for the class values applied to the CalendarNaviatgator's 
  	 * DOM elements
  	 * @property YAHOO.widget.CalendarNavigator.CLASSES
  	 * @type Object
  	 * @static
  	 */
  	CN.CLASSES = {
  		/**
  		 * Class applied to the Calendar Navigator's bounding box
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV
  		 * @type String
  		 * @static
  		 */
  		NAV :"yui-cal-nav",
  		/**
  		 * Class applied to the Calendar/CalendarGroup's bounding box to indicate
  		 * the Navigator is currently visible
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV_VISIBLE
  		 * @type String
  		 * @static
  		 */
  		NAV_VISIBLE: "yui-cal-nav-visible",
  		/**
  		 * Class applied to the Navigator mask's bounding box
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.MASK
  		 * @type String
  		 * @static
  		 */
  		MASK : "yui-cal-nav-mask",
  		/**
  		 * Class applied to the year label/control bounding box
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR
  		 * @type String
  		 * @static
  		 */
  		YEAR : "yui-cal-nav-y",
  		/**
  		 * Class applied to the month label/control bounding box
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH
  		 * @type String
  		 * @static
  		 */
  		MONTH : "yui-cal-nav-m",
  		/**
  		 * Class applied to the submit/cancel button's bounding box
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTONS
  		 * @type String
  		 * @static
  		 */
  		BUTTONS : "yui-cal-nav-b",
  		/**
  		 * Class applied to buttons wrapping element
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTON
  		 * @type String
  		 * @static
  		 */
  		BUTTON : "yui-cal-nav-btn",
  		/**
  		 * Class applied to the validation error area's bounding box
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.ERROR
  		 * @type String
  		 * @static
  		 */
  		ERROR : "yui-cal-nav-e",
  		/**
  		 * Class applied to the year input control
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR_CTRL
  		 * @type String
  		 * @static
  		 */
  		YEAR_CTRL : "yui-cal-nav-yc",
  		/**
  		 * Class applied to the month input control
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH_CTRL
  		 * @type String
  		 * @static
  		 */
  		MONTH_CTRL : "yui-cal-nav-mc",
  		/**
  		 * Class applied to controls with invalid data (e.g. a year input field with invalid an year)
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.INVALID
  		 * @type String
  		 * @static
  		 */
  		INVALID : "yui-invalid",
  		/**
  		 * Class applied to default controls
  		 * @property YAHOO.widget.CalendarNavigator.CLASSES.DEFAULT
  		 * @type String
  		 * @static
  		 */
  		DEFAULT : "yui-default"
  	};
  
  	/**
  	 * Object literal containing the default configuration values for the CalendarNavigator
  	 * The configuration object is expected to follow the format below, with the properties being
  	 * case sensitive.
  	 * <dl>
  	 * <dt>strings</dt>
  	 * <dd><em>Object</em> :  An object with the properties shown below, defining the string labels to use in the Navigator's UI
  	 *     <dl>
  	 *         <dt>month</dt><dd><em>String</em> : The string to use for the month label. Defaults to "Month".</dd>
  	 *         <dt>year</dt><dd><em>String</em> : The string to use for the year label. Defaults to "Year".</dd>
  	 *         <dt>submit</dt><dd><em>String</em> : The string to use for the submit button label. Defaults to "Okay".</dd>
  	 *         <dt>cancel</dt><dd><em>String</em> : The string to use for the cancel button label. Defaults to "Cancel".</dd>
  	 *         <dt>invalidYear</dt><dd><em>String</em> : The string to use for invalid year values. Defaults to "Year needs to be a number".</dd>
  	 *     </dl>
  	 * </dd>
  	 * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd>
  	 * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
  	 * </dl>
  	 * @property _DEFAULT_CFG
  	 * @protected
  	 * @type Object
  	 * @static
  	 */
  	CN._DEFAULT_CFG = {
  		strings : {
  			month: "Month",
  			year: "Year",
  			submit: "Okay",
  			cancel: "Cancel",
  			invalidYear : "Year needs to be a number"
  		},
  		monthFormat: YAHOO.widget.Calendar.LONG,
  		initialFocus: "year"
  	};
  
  	/**
  	 * The suffix added to the Calendar/CalendarGroup's ID, to generate
  	 * a unique ID for the Navigator and it's bounding box.
  	 * @property YAHOO.widget.CalendarNavigator.ID_SUFFIX
  	 * @static
  	 * @type String
  	 * @final
  	 */
  	CN.ID_SUFFIX = "_nav";
  	/**
  	 * The suffix added to the Navigator's ID, to generate
  	 * a unique ID for the month control.
  	 * @property YAHOO.widget.CalendarNavigator.MONTH_SUFFIX
  	 * @static
  	 * @type String 
  	 * @final
  	 */
  	CN.MONTH_SUFFIX = "_month";
  	/**
  	 * The suffix added to the Navigator's ID, to generate
  	 * a unique ID for the year control.
  	 * @property YAHOO.widget.CalendarNavigator.YEAR_SUFFIX
  	 * @static
  	 * @type String
  	 * @final
  	 */
  	CN.YEAR_SUFFIX = "_year";
  	/**
  	 * The suffix added to the Navigator's ID, to generate
  	 * a unique ID for the error bounding box.
  	 * @property YAHOO.widget.CalendarNavigator.ERROR_SUFFIX
  	 * @static
  	 * @type String
  	 * @final
  	 */
  	CN.ERROR_SUFFIX = "_error";
  	/**
  	 * The suffix added to the Navigator's ID, to generate
  	 * a unique ID for the "Cancel" button.
  	 * @property YAHOO.widget.CalendarNavigator.CANCEL_SUFFIX
  	 * @static
  	 * @type String
  	 * @final
  	 */
  	CN.CANCEL_SUFFIX = "_cancel";
  	/**
  	 * The suffix added to the Navigator's ID, to generate
  	 * a unique ID for the "Submit" button.
  	 * @property YAHOO.widget.CalendarNavigator.SUBMIT_SUFFIX
  	 * @static
  	 * @type String
  	 * @final
  	 */
  	CN.SUBMIT_SUFFIX = "_submit";
  
  	/**
  	 * The number of digits to which the year input control is to be limited.
  	 * @property YAHOO.widget.CalendarNavigator.YR_MAX_DIGITS
  	 * @static
  	 * @type Number
  	 */
  	CN.YR_MAX_DIGITS = 4;
  
  	/**
  	 * The amount by which to increment the current year value,
  	 * when the arrow up/down key is pressed on the year control
  	 * @property YAHOO.widget.CalendarNavigator.YR_MINOR_INC
  	 * @static
  	 * @type Number
  	 */
  	CN.YR_MINOR_INC = 1;
  
  	/**
  	 * The amount by which to increment the current year value,
  	 * when the page up/down key is pressed on the year control
  	 * @property YAHOO.widget.CalendarNavigator.YR_MAJOR_INC
  	 * @static
  	 * @type Number
  	 */
  	CN.YR_MAJOR_INC = 10;
  
  	/**
  	 * Artificial delay (in ms) between the time the Navigator is hidden
  	 * and the Calendar/CalendarGroup state is updated. Allows the user
  	 * the see the Calendar/CalendarGroup page changing. If set to 0
  	 * the Calendar/CalendarGroup page will be updated instantly
  	 * @property YAHOO.widget.CalendarNavigator.UPDATE_DELAY
  	 * @static
  	 * @type Number
  	 */
  	CN.UPDATE_DELAY = 50;
  
  	/**
  	 * Regular expression used to validate the year input
  	 * @property YAHOO.widget.CalendarNavigator.YR_PATTERN
  	 * @static
  	 * @type RegExp
  	 */
  	CN.YR_PATTERN = /^\d+$/;
  	/**
  	 * Regular expression used to trim strings
  	 * @property YAHOO.widget.CalendarNavigator.TRIM
  	 * @static
  	 * @type RegExp
  	 */
  	CN.TRIM = /^\s*(.*?)\s*$/;
  })();
  
  YAHOO.widget.CalendarNavigator.prototype = {
  
  	/**
  	 * The unique ID for this CalendarNavigator instance
  	 * @property id
  	 * @type String
  	 */
  	id : null,
  
  	/**
  	 * The Calendar/CalendarGroup instance to which the navigator belongs
  	 * @property cal
  	 * @type {Calendar|CalendarGroup}
  	 */
  	cal : null,
  
  	/**
  	 * Reference to the HTMLElement used to render the navigator's bounding box
  	 * @property navEl
  	 * @type HTMLElement
  	 */
  	navEl : null,
  
  	/**
  	 * Reference to the HTMLElement used to render the navigator's mask
  	 * @property maskEl
  	 * @type HTMLElement
  	 */
  	maskEl : null,
  
  	/**
  	 * Reference to the HTMLElement used to input the year
  	 * @property yearEl
  	 * @type HTMLElement
  	 */
  	yearEl : null,
  
  	/**
  	 * Reference to the HTMLElement used to input the month
  	 * @property monthEl
  	 * @type HTMLElement
  	 */
  	monthEl : null,
  
  	/**
  	 * Reference to the HTMLElement used to display validation errors
  	 * @property errorEl
  	 * @type HTMLElement
  	 */
  	errorEl : null,
  
  	/**
  	 * Reference to the HTMLElement used to update the Calendar/Calendar group
  	 * with the month/year values
  	 * @property submitEl
  	 * @type HTMLElement
  	 */
  	submitEl : null,
  	
  	/**
  	 * Reference to the HTMLElement used to hide the navigator without updating the 
  	 * Calendar/Calendar group
  	 * @property cancelEl
  	 * @type HTMLElement
  	 */
  	cancelEl : null,
  
  	/** 
  	 * Reference to the first focusable control in the navigator (by default monthEl)
  	 * @property firstCtrl
  	 * @type HTMLElement
  	 */
  	firstCtrl : null,
  	
  	/** 
  	 * Reference to the last focusable control in the navigator (by default cancelEl)
  	 * @property lastCtrl
  	 * @type HTMLElement
  	 */
  	lastCtrl : null,
  
  	/**
  	 * The document containing the Calendar/Calendar group instance
  	 * @protected
  	 * @property _doc
  	 * @type HTMLDocument
  	 */
  	_doc : null,
  
  	/**
  	 * Internal state property for the current year displayed in the navigator
  	 * @protected
  	 * @property _year
  	 * @type Number
  	 */
  	_year: null,
  	
  	/**
  	 * Internal state property for the current month index displayed in the navigator
  	 * @protected
  	 * @property _month
  	 * @type Number
  	 */
  	_month: 0,
  
  	/**
  	 * Private internal state property which indicates whether or not the 
  	 * Navigator has been rendered.
  	 * @private
  	 * @property __rendered
  	 * @type Boolean
  	 */
  	__rendered: false,
  
  	/**
  	 * Init lifecycle method called as part of construction
  	 * 
  	 * @method init
  	 * @param {Calendar} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached
  	 */
  	init : function(cal) {
  		var calBox = cal.oDomContainer;
  
  		this.cal = cal;
  		this.id = calBox.id + YAHOO.widget.CalendarNavigator.ID_SUFFIX;
  		this._doc = calBox.ownerDocument;
  
  		/**
  		 * Private flag, to identify IE6/IE7 Quirks
  		 * @private
  		 * @property __isIEQuirks
  		 */
  		var ie = YAHOO.env.ua.ie;
  		this.__isIEQuirks = (ie && ((ie <= 6) || (ie === 7 && this._doc.compatMode == "BackCompat")));
  	},
  
  	/**
  	 * Displays the navigator and mask, updating the input controls to reflect the 
  	 * currently set month and year. The show method will invoke the render method
  	 * if the navigator has not been renderered already, allowing for lazy rendering
  	 * of the control.
  	 * 
  	 * The show method will fire the Calendar/CalendarGroup's beforeShowNav and showNav events
  	 * 
  	 * @method show
  	 */
  	show : function() {
  		var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
  
  		if (this.cal.beforeShowNavEvent.fire()) {
  			if (!this.__rendered) {
  				this.render();
  			}
  			this.clearErrors();
  
  			this._updateMonthUI();
  			this._updateYearUI();
  			this._show(this.navEl, true);
  
  			this.setInitialFocus();
  			this.showMask();
  
  			YAHOO.util.Dom.addClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
  			this.cal.showNavEvent.fire();
  		}
  	},
  
  	/**
  	 * Hides the navigator and mask
  	 * 
  	 * The show method will fire the Calendar/CalendarGroup's beforeHideNav event and hideNav events
  	 * @method hide
  	 */
  	hide : function() {
  		var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
  
  		if (this.cal.beforeHideNavEvent.fire()) {
  			this._show(this.navEl, false);
  			this.hideMask();
  			YAHOO.util.Dom.removeClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
  			this.cal.hideNavEvent.fire();
  		}
  	},
  	
  
  	/**
  	 * Displays the navigator's mask element
  	 * 
  	 * @method showMask
  	 */
  	showMask : function() {
  		this._show(this.maskEl, true);
  		if (this.__isIEQuirks) {
  			this._syncMask();
  		}
  	},
  
  	/**
  	 * Hides the navigator's mask element
  	 * 
  	 * @method hideMask
  	 */
  	hideMask : function() {
  		this._show(this.maskEl, false);
  	},
  
  	/**
  	 * Returns the current month set on the navigator
  	 * 
  	 * Note: This may not be the month set in the UI, if 
  	 * the UI contains an invalid value.
  	 * 
  	 * @method getMonth
  	 * @return {Number} The Navigator's current month index
  	 */
  	getMonth: function() {
  		return this._month;
  	},
  
  	/**
  	 * Returns the current year set on the navigator
  	 * 
  	 * Note: This may not be the year set in the UI, if 
  	 * the UI contains an invalid value.
  	 * 
  	 * @method getYear
  	 * @return {Number} The Navigator's current year value
  	 */
  	getYear: function() {
  		return this._year;
  	},
  
  	/**
  	 * Sets the current month on the Navigator, and updates the UI
  	 * 
  	 * @method setMonth
  	 * @param {Number} nMonth The month index, from 0 (Jan) through 11 (Dec).
  	 */
  	setMonth : function(nMonth) {
  		if (nMonth >= 0 && nMonth < 12) {
  			this._month = nMonth;
  		}
  		this._updateMonthUI();
  	},
  
  	/**
  	 * Sets the current year on the Navigator, and updates the UI. If the 
  	 * provided year is invalid, it will not be set.
  	 * 
  	 * @method setYear
  	 * @param {Number} nYear The full year value to set the Navigator to.
  	 */
  	setYear : function(nYear) {
  		var yrPattern = YAHOO.widget.CalendarNavigator.YR_PATTERN;
  		if (YAHOO.lang.isNumber(nYear) && yrPattern.test(nYear+"")) {
  			this._year = nYear;
  		}
  		this._updateYearUI();
  	},
  
  	/**
  	 * Renders the HTML for the navigator, adding it to the 
  	 * document and attaches event listeners if it has not 
  	 * already been rendered.
  	 * 
  	 * @method render
  	 */
  	render: function() {
  		this.cal.beforeRenderNavEvent.fire();
  		if (!this.__rendered) {
  			this.createNav();
  			this.createMask();
  			this.applyListeners();
  			this.__rendered = true;
  		}
  		this.cal.renderNavEvent.fire();
  	},
  
  	/**
  	 * Creates the navigator's containing HTMLElement, it's contents, and appends 
  	 * the containg element to the Calendar/CalendarGroup's container.
  	 * 
  	 * @method createNav
  	 */
  	createNav : function() {
  		var NAV = YAHOO.widget.CalendarNavigator;
  		var doc = this._doc;
  
  		var d = doc.createElement("div");
  		d.className = NAV.CLASSES.NAV;
  
  		var htmlBuf = this.renderNavContents([]);
  
  		d.innerHTML = htmlBuf.join('');
  		this.cal.oDomContainer.appendChild(d);
  
  		this.navEl = d;
  
  		this.yearEl = doc.getElementById(this.id + NAV.YEAR_SUFFIX);
  		this.monthEl = doc.getElementById(this.id + NAV.MONTH_SUFFIX);
  		this.errorEl = doc.getElementById(this.id + NAV.ERROR_SUFFIX);
  		this.submitEl = doc.getElementById(this.id + NAV.SUBMIT_SUFFIX);
  		this.cancelEl = doc.getElementById(this.id + NAV.CANCEL_SUFFIX);
  
  		if (YAHOO.env.ua.gecko && this.yearEl && this.yearEl.type == "text") {
  			// Avoid XUL error on focus, select [ https://bugzilla.mozilla.org/show_bug.cgi?id=236791, 
  			// supposedly fixed in 1.8.1, but there are reports of it still being around for methods other than blur ]
  			this.yearEl.setAttribute("autocomplete", "off");
  		}
  
  		this._setFirstLastElements();
  	},
  
  	/**
  	 * Creates the Mask HTMLElement and appends it to the Calendar/CalendarGroups
  	 * container.
  	 * 
  	 * @method createMask
  	 */
  	createMask : function() {
  		var C = YAHOO.widget.CalendarNavigator.CLASSES;
  
  		var d = this._doc.createElement("div");
  		d.className = C.MASK;
  
  		this.cal.oDomContainer.appendChild(d);
  		this.maskEl = d;
  	},
  
  	/**
  	 * Used to set the width/height of the mask in pixels to match the Calendar Container.
  	 * Currently only used for IE6 and IE7 quirks mode. The other A-Grade browser are handled using CSS (width/height 100%).
  	 * <p>
  	 * The method is also registered as an HTMLElement resize listener on the Calendars container element.
  	 * </p>
  	 * @protected
  	 * @method _syncMask
  	 */
  	_syncMask : function() {
  		var c = this.cal.oDomContainer;
  		if (c && this.maskEl) {
  			var r = YAHOO.util.Dom.getRegion(c);
  			YAHOO.util.Dom.setStyle(this.maskEl, "width", r.right - r.left + "px");
  			YAHOO.util.Dom.setStyle(this.maskEl, "height", r.bottom - r.top + "px");
  		}
  	},
  
  	/**
  	 * Renders the contents of the navigator
  	 * 
  	 * @method renderNavContents
  	 * 
  	 * @param {Array} html The HTML buffer to append the HTML to.
  	 * @return {Array} A reference to the buffer passed in.
  	 */
  	renderNavContents : function(html) {
  		var NAV = YAHOO.widget.CalendarNavigator,
  			C = NAV.CLASSES,
  			h = html; // just to use a shorter name
  
  		h[h.length] = '<div class="' + C.MONTH + '">';
  		this.renderMonth(h);
  		h[h.length] = '</div>';
  		h[h.length] = '<div class="' + C.YEAR + '">';
  		this.renderYear(h);
  		h[h.length] = '</div>';
  		h[h.length] = '<div class="' + C.BUTTONS + '">';
  		this.renderButtons(h);
  		h[h.length] = '</div>';
  		h[h.length] = '<div class="' + C.ERROR + '" id="' + this.id + NAV.ERROR_SUFFIX + '"></div>';
  
  		return h;
  	},
  
  	/**
  	 * Renders the month label and control for the navigator
  	 * 
  	 * @method renderNavContents
  	 * @param {Array} html The HTML buffer to append the HTML to.
  	 * @return {Array} A reference to the buffer passed in.
  	 */
  	renderMonth : function(html) {
  		var NAV = YAHOO.widget.CalendarNavigator,
  			C = NAV.CLASSES;
  
  		var id = this.id + NAV.MONTH_SUFFIX,
  			mf = this.__getCfg("monthFormat"),
  			months = this.cal.cfg.getProperty((mf == YAHOO.widget.Calendar.SHORT) ? "MONTHS_SHORT" : "MONTHS_LONG"),
  			h = html;
  
  		if (months && months.length > 0) {
  			h[h.length] = '<label for="' + id + '">';
  			h[h.length] = this.__getCfg("month", true);
  			h[h.length] = '</label>';
  			h[h.length] = '<select name="' + id + '" id="' + id + '" class="' + C.MONTH_CTRL + '">';
  			for (var i = 0; i < months.length; i++) {
  				h[h.length] = '<option value="' + i + '">';
  				h[h.length] = months[i];
  				h[h.length] = '</option>';
  			}
  			h[h.length] = '</select>';
  		}
  		return h;
  	},
  
  	/**
  	 * Renders the year label and control for the navigator
  	 * 
  	 * @method renderYear
  	 * @param {Array} html The HTML buffer to append the HTML to.
  	 * @return {Array} A reference to the buffer passed in.
  	 */
  	renderYear : function(html) {
  		var NAV = YAHOO.widget.CalendarNavigator,
  			C = NAV.CLASSES;
  
  		var id = this.id + NAV.YEAR_SUFFIX,
  			size = NAV.YR_MAX_DIGITS,
  			h = html;
  
  		h[h.length] = '<label for="' + id + '">';
  		h[h.length] = this.__getCfg("year", true);
  		h[h.length] = '</label>';
  		h[h.length] = '<input type="text" name="' + id + '" id="' + id + '" class="' + C.YEAR_CTRL + '" maxlength="' + size + '"/>';
  		return h;
  	},
  
  	/**
  	 * Renders the submit/cancel buttons for the navigator
  	 * 
  	 * @method renderButton
  	 * @return {String} The HTML created for the Button UI
  	 */
  	renderButtons : function(html) {
  		var C = YAHOO.widget.CalendarNavigator.CLASSES;
  		var h = html;
  
  		h[h.length] = '<span class="' + C.BUTTON + ' ' + C.DEFAULT + '">';
  		h[h.length] = '<button type="button" id="' + this.id + '_submit' + '">';
  		h[h.length] = this.__getCfg("submit", true);
  		h[h.length] = '</button>';
  		h[h.length] = '</span>';
  		h[h.length] = '<span class="' + C.BUTTON +'">';
  		h[h.length] = '<button type="button" id="' + this.id + '_cancel' + '">';
  		h[h.length] = this.__getCfg("cancel", true);
  		h[h.length] = '</button>';
  		h[h.length] = '</span>';
  
  		return h;
  	},
  
  	/**
  	 * Attaches DOM event listeners to the rendered elements
  	 * <p>
  	 * The method will call applyKeyListeners, to setup keyboard specific 
  	 * listeners
  	 * </p>
  	 * @method applyListeners
  	 */
  	applyListeners : function() {
  		var E = YAHOO.util.Event;
  
  		function yearUpdateHandler() {
  			if (this.validate()) {
  				this.setYear(this._getYearFromUI());
  			}
  		}
  
  		function monthUpdateHandler() {
  			this.setMonth(this._getMonthFromUI());
  		}
  
  		E.on(this.submitEl, "click", this.submit, this, true);
  		E.on(this.cancelEl, "click", this.cancel, this, true);
  		E.on(this.yearEl, "blur", yearUpdateHandler, this, true);
  		E.on(this.monthEl, "change", monthUpdateHandler, this, true);
  
  		if (this.__isIEQuirks) {
  			YAHOO.util.Event.on(this.cal.oDomContainer, "resize", this._syncMask, this, true);
  		}
  
  		this.applyKeyListeners();
  	},
  
  	/**
  	 * Removes/purges DOM event listeners from the rendered elements
  	 * 
  	 * @method purgeListeners
  	 */
  	purgeListeners : function() {
  		var E = YAHOO.util.Event;
  		E.removeListener(this.submitEl, "click", this.submit);
  		E.removeListener(this.cancelEl, "click", this.cancel);
  		E.removeListener(this.yearEl, "blur");
  		E.removeListener(this.monthEl, "change");
  		if (this.__isIEQuirks) {
  			E.removeListener(this.cal.oDomContainer, "resize", this._syncMask);
  		}
  
  		this.purgeKeyListeners();
  	},
  
  	/**
  	 * Attaches DOM listeners for keyboard support. 
  	 * Tab/Shift-Tab looping, Enter Key Submit on Year element,
  	 * Up/Down/PgUp/PgDown year increment on Year element
  	 * <p>
  	 * NOTE: MacOSX Safari 2.x doesn't let you tab to buttons and 
  	 * MacOSX Gecko does not let you tab to buttons or select controls,
  	 * so for these browsers, Tab/Shift-Tab looping is limited to the 
  	 * elements which can be reached using the tab key.
  	 * </p>
  	 * @method applyKeyListeners
  	 */
  	applyKeyListeners : function() {
  		var E = YAHOO.util.Event,
  			ua = YAHOO.env.ua;
  
  		// IE/Safari 3.1 doesn't fire keypress for arrow/pg keys (non-char keys)
  		var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
  
  		// - IE/Safari 3.1 doesn't fire keypress for non-char keys
  		// - Opera doesn't allow us to cancel keydown or keypress for tab, but 
  		//   changes focus successfully on keydown (keypress is too late to change focus - opera's already moved on).
  		var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
  
  		// Everyone likes keypress for Enter (char keys) - whoo hoo!
  		E.on(this.yearEl, "keypress", this._handleEnterKey, this, true);
  
  		E.on(this.yearEl, arrowEvt, this._handleDirectionKeys, this, true);
  		E.on(this.lastCtrl, tabEvt, this._handleTabKey, this, true);
  		E.on(this.firstCtrl, tabEvt, this._handleShiftTabKey, this, true);
  	},
  
  	/**
  	 * Removes/purges DOM listeners for keyboard support
  	 *
  	 * @method purgeKeyListeners
  	 */
  	purgeKeyListeners : function() {
  		var E = YAHOO.util.Event,
  			ua = YAHOO.env.ua;
  
  		var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
  		var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
  
  		E.removeListener(this.yearEl, "keypress", this._handleEnterKey);
  		E.removeListener(this.yearEl, arrowEvt, this._handleDirectionKeys);
  		E.removeListener(this.lastCtrl, tabEvt, this._handleTabKey);
  		E.removeListener(this.firstCtrl, tabEvt, this._handleShiftTabKey);
  	},
  
  	/**
  	 * Updates the Calendar/CalendarGroup's pagedate with the currently set month and year if valid.
  	 * <p>
  	 * If the currently set month/year is invalid, a validation error will be displayed and the 
  	 * Calendar/CalendarGroup's pagedate will not be updated.
  	 * </p>
  	 * @method submit
  	 */
  	submit : function() {
  		if (this.validate()) {
  			this.hide();
  
  			this.setMonth(this._getMonthFromUI());
  			this.setYear(this._getYearFromUI());
  
  			var cal = this.cal;
  
  			// Artificial delay, just to help the user see something changed
  			var delay = YAHOO.widget.CalendarNavigator.UPDATE_DELAY;
  			if (delay > 0) {
  				var nav = this;
  				window.setTimeout(function(){ nav._update(cal); }, delay);
  			} else {
  				this._update(cal);
  			}
  		}
  	},
  
  	/**
  	 * Updates the Calendar rendered state, based on the state of the CalendarNavigator
  	 * @method _update
  	 * @param cal The Calendar instance to update
  	 * @protected
  	 */
  	_update : function(cal) {
  		cal.setYear(this.getYear());
  		cal.setMonth(this.getMonth());
  		cal.render();
  	},
  
  	/**
  	 * Hides the navigator and mask, without updating the Calendar/CalendarGroup's state
  	 * 
  	 * @method cancel
  	 */
  	cancel : function() {
  		this.hide();
  	},
  
  	/**
  	 * Validates the current state of the UI controls
  	 * 
  	 * @method validate
  	 * @return {Boolean} true, if the current UI state contains valid values, false if not
  	 */
  	validate : function() {
  		if (this._getYearFromUI() !== null) {
  			this.clearErrors();
  			return true;
  		} else {
  			this.setYearError();
  			this.setError(this.__getCfg("invalidYear", true));
  			return false;
  		}
  	},
  
  	/**
  	 * Displays an error message in the Navigator's error panel
  	 * @method setError
  	 * @param {String} msg The error message to display
  	 */
  	setError : function(msg) {
  		if (this.errorEl) {
  			this.errorEl.innerHTML = msg;
  			this._show(this.errorEl, true);
  		}
  	},
  
  	/**
  	 * Clears the navigator's error message and hides the error panel
  	 * @method clearError 
  	 */
  	clearError : function() {
  		if (this.errorEl) {
  			this.errorEl.innerHTML = "";
  			this._show(this.errorEl, false);
  		}
  	},
  
  	/**
  	 * Displays the validation error UI for the year control
  	 * @method setYearError
  	 */
  	setYearError : function() {
  		YAHOO.util.Dom.addClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
  	},
  
  	/**
  	 * Removes the validation error UI for the year control
  	 * @method clearYearError
  	 */
  	clearYearError : function() {
  		YAHOO.util.Dom.removeClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
  	},
  
  	/**
  	 * Clears all validation and error messages in the UI
  	 * @method clearErrors
  	 */
  	clearErrors : function() {
  		this.clearError();
  		this.clearYearError();
  	},
  
  	/**
  	 * Sets the initial focus, based on the configured value
  	 * @method setInitialFocus
  	 */
  	setInitialFocus : function() {
  		var el = this.submitEl,
  			f = this.__getCfg("initialFocus");
  
  		if (f && f.toLowerCase) {
  			f = f.toLowerCase();
  			if (f == "year") {
  				el = this.yearEl;
  				try {
  					this.yearEl.select();
  				} catch (selErr) {
  					// Ignore;
  				}
  			} else if (f == "month") {
  				el = this.monthEl;
  			}
  		}
  
  		if (el && YAHOO.lang.isFunction(el.focus)) {
  			try {
  				el.focus();
  			} catch (focusErr) {
  				// TODO: Fall back if focus fails?
  			}
  		}
  	},
  
  	/**
  	 * Removes all renderered HTML elements for the Navigator from
  	 * the DOM, purges event listeners and clears (nulls) any property
  	 * references to HTML references
  	 * @method erase
  	 */
  	erase : function() {
  		if (this.__rendered) {
  			this.purgeListeners();
  
  			// Clear out innerHTML references
  			this.yearEl = null;
  			this.monthEl = null;
  			this.errorEl = null;
  			this.submitEl = null;
  			this.cancelEl = null;
  			this.firstCtrl = null;
  			this.lastCtrl = null;
  			if (this.navEl) {
  				this.navEl.innerHTML = "";
  			}
  
  			var p = this.navEl.parentNode;
  			if (p) {
  				p.removeChild(this.navEl);
  			}
  			this.navEl = null;
  
  			var pm = this.maskEl.parentNode;
  			if (pm) {
  				pm.removeChild(this.maskEl);
  			}
  			this.maskEl = null;
  			this.__rendered = false;
  		}
  	},
  
  	/**
  	 * Destroys the Navigator object and any HTML references
  	 * @method destroy
  	 */
  	destroy : function() {
  		this.erase();
  		this._doc = null;
  		this.cal = null;
  		this.id = null;
  	},
  
  	/**
  	 * Protected implementation to handle how UI elements are 
  	 * hidden/shown.
  	 *
  	 * @method _show
  	 * @protected
  	 */
  	_show : function(el, bShow) {
  		if (el) {
  			YAHOO.util.Dom.setStyle(el, "display", (bShow) ? "block" : "none");
  		}
  	},
  
  	/**
  	 * Returns the month value (index), from the month UI element
  	 * @protected
  	 * @method _getMonthFromUI
  	 * @return {Number} The month index, or 0 if a UI element for the month
  	 * is not found
  	 */
  	_getMonthFromUI : function() {
  		if (this.monthEl) {
  			return this.monthEl.selectedIndex;
  		} else {
  			return 0; // Default to Jan
  		}
  	},
  
  	/**
  	 * Returns the year value, from the Navitator's year UI element
  	 * @protected
  	 * @method _getYearFromUI
  	 * @return {Number} The year value set in the UI, if valid. null is returned if 
  	 * the UI does not contain a valid year value.
  	 */
  	_getYearFromUI : function() {
  		var NAV = YAHOO.widget.CalendarNavigator;
  
  		var yr = null;
  		if (this.yearEl) {
  			var value = this.yearEl.value;
  			value = value.replace(NAV.TRIM, "$1");
  
  			if (NAV.YR_PATTERN.test(value)) {
  				yr = parseInt(value, 10);
  			}
  		}
  		return yr;
  	},
  
  	/**
  	 * Updates the Navigator's year UI, based on the year value set on the Navigator object
  	 * @protected
  	 * @method _updateYearUI
  	 */
  	_updateYearUI : function() {
  		if (this.yearEl && this._year !== null) {
  			this.yearEl.value = this._year;
  		}
  	},
  
  	/**
  	 * Updates the Navigator's month UI, based on the month value set on the Navigator object
  	 * @protected
  	 * @method _updateMonthUI
  	 */
  	_updateMonthUI : function() {
  		if (this.monthEl) {
  			this.monthEl.selectedIndex = this._month;
  		}
  	},
  
  	/**
  	 * Sets up references to the first and last focusable element in the Navigator's UI
  	 * in terms of tab order (Naviagator's firstEl and lastEl properties). The references
  	 * are used to control modality by looping around from the first to the last control
  	 * and visa versa for tab/shift-tab navigation.
  	 * <p>
  	 * See <a href="#applyKeyListeners">applyKeyListeners</a>
  	 * </p>
  	 * @protected
  	 * @method _setFirstLastElements
  	 */
  	_setFirstLastElements : function() {
  		this.firstCtrl = this.monthEl;
  		this.lastCtrl = this.cancelEl;
  
  		// Special handling for MacOSX.
  		// - Safari 2.x can't focus on buttons
  		// - Gecko can't focus on select boxes or buttons
  		if (this.__isMac) {
  			if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420){
  				this.firstCtrl = this.monthEl;
  				this.lastCtrl = this.yearEl;
  			}
  			if (YAHOO.env.ua.gecko) {
  				this.firstCtrl = this.yearEl;
  				this.lastCtrl = this.yearEl;
  			}
  		}
  	},
  
  	/**
  	 * Default Keyboard event handler to capture Enter 
  	 * on the Navigator's year control (yearEl)
  	 * 
  	 * @method _handleEnterKey
  	 * @protected
  	 * @param {Event} e The DOM event being handled
  	 */
  	_handleEnterKey : function(e) {
  		var KEYS = YAHOO.util.KeyListener.KEY;
  
  		if (YAHOO.util.Event.getCharCode(e) == KEYS.ENTER) {
  			YAHOO.util.Event.preventDefault(e);
  			this.submit();
  		}
  	},
  
  	/**
  	 * Default Keyboard event handler to capture up/down/pgup/pgdown
  	 * on the Navigator's year control (yearEl).
  	 * 
  	 * @method _handleDirectionKeys
  	 * @protected
  	 * @param {Event} e The DOM event being handled
  	 */
  	_handleDirectionKeys : function(e) {
  		var E = YAHOO.util.Event,
  			KEYS = YAHOO.util.KeyListener.KEY,
  			NAV = YAHOO.widget.CalendarNavigator;
  
  		var value = (this.yearEl.value) ? parseInt(this.yearEl.value, 10) : null;
  		if (isFinite(value)) {
  			var dir = false;
  			switch(E.getCharCode(e)) {
  				case KEYS.UP:
  					this.yearEl.value = value + NAV.YR_MINOR_INC;
  					dir = true;
  					break;
  				case KEYS.DOWN:
  					this.yearEl.value = Math.max(value - NAV.YR_MINOR_INC, 0);
  					dir = true;
  					break;
  				case KEYS.PAGE_UP:
  					this.yearEl.value = value + NAV.YR_MAJOR_INC;
  					dir = true;
  					break;
  				case KEYS.PAGE_DOWN:
  					this.yearEl.value = Math.max(value - NAV.YR_MAJOR_INC, 0);
  					dir = true;
  					break;
  				default:
  					break;
  			}
  			if (dir) {
  				E.preventDefault(e);
  				try {
  					this.yearEl.select();
  				} catch(err) {
  					// Ignore
  				}
  			}
  		}
  	},
  
  	/**
  	 * Default Keyboard event handler to capture Tab 
  	 * on the last control (lastCtrl) in the Navigator.
  	 * 
  	 * @method _handleTabKey
  	 * @protected
  	 * @param {Event} e The DOM event being handled
  	 */
  	_handleTabKey : function(e) {
  		var E = YAHOO.util.Event,
  			KEYS = YAHOO.util.KeyListener.KEY;
  
  		if (E.getCharCode(e) == KEYS.TAB && !e.shiftKey) {
  			try {
  				E.preventDefault(e);
  				this.firstCtrl.focus();
  			} catch (err) {
  				// Ignore - mainly for focus edge cases
  			}
  		}
  	},
  
  	/**
  	 * Default Keyboard event handler to capture Shift-Tab 
  	 * on the first control (firstCtrl) in the Navigator.
  	 * 
  	 * @method _handleShiftTabKey
  	 * @protected
  	 * @param {Event} e The DOM event being handled
  	 */
  	_handleShiftTabKey : function(e) {
  		var E = YAHOO.util.Event,
  			KEYS = YAHOO.util.KeyListener.KEY;
  
  		if (e.shiftKey && E.getCharCode(e) == KEYS.TAB) {
  			try {
  				E.preventDefault(e);
  				this.lastCtrl.focus();
  			} catch (err) {
  				// Ignore - mainly for focus edge cases
  			}
  		}
  	},
  
  	/**
  	 * Retrieve Navigator configuration values from 
  	 * the parent Calendar/CalendarGroup's config value.
  	 * <p>
  	 * If it has not been set in the user provided configuration, the method will 
  	 * return the default value of the configuration property, as set in _DEFAULT_CFG
  	 * </p>
  	 * @private
  	 * @method __getCfg
  	 * @param {String} Case sensitive property name.
  	 * @param {Boolean} true, if the property is a string property, false if not.
  	 * @return The value of the configuration property
  	 */
  	__getCfg : function(prop, bIsStr) {
  		var DEF_CFG = YAHOO.widget.CalendarNavigator._DEFAULT_CFG;
  		var cfg = this.cal.cfg.getProperty("navigator");
  
  		if (bIsStr) {
  			return (cfg !== true && cfg.strings && cfg.strings[prop]) ? cfg.strings[prop] : DEF_CFG.strings[prop];
  		} else {
  			return (cfg !== true && cfg[prop]) ? cfg[prop] : DEF_CFG[prop];
  		}
  	},
  
  	/**
  	 * Private flag, to identify MacOS
  	 * @private
  	 * @property __isMac
  	 */
  	__isMac : (navigator.userAgent.toLowerCase().indexOf("macintosh") != -1)
  
  };
  
  YAHOO.register("calendar", YAHOO.widget.Calendar, {version: "2.6.0", build: "1321"});
  