/* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.8.0r4 */ (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); } if (!owner) { YAHOO.log("No owner specified for Config object", "error", "Config"); } }; 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 ) { YAHOO.log("Firing Config event: " + key + "=" + value, "info", "Config"); 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(); YAHOO.log("Added property: " + key, "info", "Config"); 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(); YAHOO.log("setProperty: " + key + "=" + value, "info", "Config"); 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(); YAHOO.log("queueProperty: " + key + "=" + value, "info", "Config"); 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; } } } } } YAHOO.log("Config event queue: " + this.outputEventQueue(), "info", "Config"); 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; // Clear out queue entry, to avoid it being // re-added to the queue by any queueProperty/supercedes // calls which are invoked during fireEvent this.eventQueue[i] = null; 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} overrideContext 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, overrideContext) { var property = this.config[key.toLowerCase()]; if (property && property.event) { if (!Config.alreadySubscribed(property.event, handler, obj)) { property.event.subscribe(handler, obj, overrideContext); } 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); }()); /** * The datemath module provides utility methods for basic JavaScript Date object manipulation and * comparison. * * @module datemath */ /** * 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. *
* 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. *
* @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. ** Fix approach and original findings are available here: * http://brianary.blogspot.com/2006/03/safari-date-bug.html *
* @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(); // 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. ** NOTE: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. *
* @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. *To construct the placeholder for the calendar widget, the code is as * follows: *
* NOTE: As of 2.4.0, the constructor's ID argument is optional. * 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.: *
* 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". *
* * @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. * ** NOTE: This property is made public in order to allow users to change * the default values of configuration properties. Users should not * modify the key string, unless they are overriding the Calendar implementation *
* *
* The property is an object with key/value pairs, the key being the
* uppercase configuration property name and the value being an object
* literal with a key string property, and a value property, specifying the
* default value of the property. To override a default value, you can set
* the value property, for example, YAHOO.widget.Calendar.DEFAULT_CONFIG.MULTI_SELECT.value = true;
*
* @property YAHOO.widget.Calendar.DEFAULT_CONFIG
* @static
* @type Object
*/
Calendar.DEFAULT_CONFIG = {
YEAR_OFFSET : {key:"year_offset", value:0, supercedes:["pagedate", "selected", "mindate","maxdate"]},
TODAY : {key:"today", value:new Date(), supercedes:["pagedate"]},
PAGEDATE : {key:"pagedate", value:null},
SELECTED : {key:"selected", value:[]},
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"]
}
};
/**
* The set of default Config property keys and values for the Calendar
* @property YAHOO.widget.Calendar._DEFAULT_CONFIG
* @deprecated Made public. See the public DEFAULT_CONFIG property for details
* @final
* @static
* @private
* @type Object
*/
Calendar._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG;
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
* @static
* @type Object An object with name/value pairs for the class name identifier/value.
*/
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",
CSS_WITH_TITLE: "withtitle",
CSS_FIXED_SIZE: "fixedsize",
CSS_LINK_CLOSE: "link-close"
};
/**
* The set of default style constants for the Calendar
* @property YAHOO.widget.Calendar._STYLES
* @deprecated Made public. See the public STYLES property for details
* @final
* @static
* @private
* @type Object
*/
Calendar._STYLES = Calendar.STYLES;
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.
* @deprecated Use the "today" configuration property
* @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 {
this.logger.log("Invalid constructor/init arguments", "error");
}
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) { this.logger.log("Container not found in document.", "error"); }
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.logger = new YAHOO.widget.LogWriter("Calendar " + this.id);
this.initEvents();
/**
* 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();
this.today = this.cfg.getProperty("today");
},
/**
* 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, this.Style.CSS_FIXED_SIZE);
}
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(" ");
}
}
},
/**
* 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(" ");
}
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 date selection is made
* @event beforeSelectEvent
*/
cal.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
/**
* Fired when a date 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 date or set of dates is deselected
* @event beforeDeselectEvent
*/
cal.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
/**
* Fired when a date or set of dates is deselected
* @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
* @param {Date} prevDate The date before the page was changed
* @param {Date} newDate The date after the page was changed
*/
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 (ex) {
// 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 (ex) {
// ignore
}
}
}, 0);
},
/**
* The default event handler for date cell selection. Currently attached to
* the Calendar's bounding box, referenced by it's oDomContainer 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;
cal.logger.log("Selecting cell " + index + " via click", "info");
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 date to use to represent "Today".
*
* @config today
* @type Date
* @default The client side date (new Date()) when the Calendar is instantiated.
*/
cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler:this.configToday, suppressEvent:true } );
/**
* 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: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), 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:DEF_CFG.SELECTED.value.concat(), 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 positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to
* be used when displaying and parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by
* methods will always represent the Gregorian year, in order to maintain date/month/week values.
*
* @config YEAR_OFFSET
* @type Number
* @default 0
*/
cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, handler:this.configLocale } );
/**
* 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.
*
* Setting this property to null (default value) or false, will disable the CalendarNavigator UI. *
** Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values. *
** 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). *
*E.g.
** 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" * } ** @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: *
* 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. *
* * See cells, selectCell. * * @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] = '* NOTE: As of 2.4.0, the constructor's ID argument is optional. * 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.: *
* 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". *
* * @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. * ** NOTE: This property is made public in order to allow users to change * the default values of configuration properties. Users should not * modify the key string, unless they are overriding the Calendar implementation *
* * @property YAHOO.widget.CalendarGroup.DEFAULT_CONFIG * @static * @type Object An object with key/value pairs, the key being the * uppercase configuration property name and the value being an objec * literal with a key string property, and a value property, specifying the * default value of the property */ /** * The set of default Config property keys and values for the CalendarGroup * @property YAHOO.widget.CalendarGroup._DEFAULT_CONFIG * @deprecated Made public. See the public DEFAULT_CONFIG property for details * @private * @static * @type Object */ CalendarGroup.DEFAULT_CONFIG = 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) { this.logger.log("Container not found in document.", "error"); } 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.logger = new YAHOO.widget.LogWriter("CalendarGroup " + this.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); } this.logger.log("Initialized " + this.pages.length + "-page CalendarGroup", "info"); }, 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 positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to * be used when displaying or parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by * methods will always represent the Gregorian year, in order to maintain date/month/week values. * * @config year_offset * @type Number * @default 0 */ cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, handler: this.delegateConfig, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, suppressEvent:true } ); /** * The date to use to represent "Today". * * @config today * @type Date * @default Today's date */ cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler: this.configToday, suppressEvent:false } ); /** * 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: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), 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: ** The method is also registered as an HTMLElement resize listener on the Calendars container element. *
* @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] = '* The method will call applyKeyListeners, to setup keyboard specific * listeners *
* @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 ** 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. *
* @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. ** If the currently set month/year is invalid, a validation error will be displayed and the * Calendar/CalendarGroup's pagedate will not be updated. *
* @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) { var date = YAHOO.widget.DateMath.getDate(this.getYear() - cal.cfg.getProperty("YEAR_OFFSET"), this.getMonth(), 1); cal.cfg.setProperty("pagedate", date); 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. ** See applyKeyListeners *
* @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. ** 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_CONFIG *
* @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_CONFIG; 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.8.0r4", build: "2449"});