2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
10 * Config is a utility used within an Object to allow the implementer to
11 * maintain a list of local configuration properties and listen for changes
12 * to those properties dynamically using CustomEvent. The initial values are
13 * also maintained so that the configuration can be reset at any given point
14 * to its initial state.
15 * @namespace YAHOO.util
18 * @param {Object} owner The owner Object to which this Config Object belongs
20 YAHOO.util.Config = function (owner) {
26 if (!owner) { YAHOO.log("No owner specified for Config object", "error", "Config"); }
31 var Lang = YAHOO.lang,
32 CustomEvent = YAHOO.util.CustomEvent,
33 Config = YAHOO.util.Config;
37 * Constant representing the CustomEvent type for the config changed event.
38 * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT
43 Config.CONFIG_CHANGED_EVENT = "configChanged";
46 * Constant representing the boolean type string
47 * @property YAHOO.util.Config.BOOLEAN_TYPE
52 Config.BOOLEAN_TYPE = "boolean";
57 * Object reference to the owner of this Config Object
64 * Boolean flag that specifies whether a queue is currently
66 * @property queueInProgress
69 queueInProgress: false,
72 * Maintains the local collection of configuration property objects and
73 * their specified values
81 * Maintains the local collection of configuration property objects as
82 * they were initially applied.
83 * This object is used when resetting a property.
84 * @property initialConfig
91 * Maintains the local, normalized CustomEvent queue
92 * @property eventQueue
99 * Custom Event, notifying subscribers when Config properties are set
100 * (setProperty is called without the silent flag
101 * @event configChangedEvent
103 configChangedEvent: null,
106 * Initializes the configuration Object and all of its local members.
108 * @param {Object} owner The owner Object to which this Config
111 init: function (owner) {
115 this.configChangedEvent =
116 this.createEvent(Config.CONFIG_CHANGED_EVENT);
118 this.configChangedEvent.signature = CustomEvent.LIST;
119 this.queueInProgress = false;
121 this.initialConfig = {};
122 this.eventQueue = [];
127 * Validates that the value passed in is a Boolean.
128 * @method checkBoolean
129 * @param {Object} val The value to validate
130 * @return {Boolean} true, if the value is valid
132 checkBoolean: function (val) {
133 return (typeof val == Config.BOOLEAN_TYPE);
137 * Validates that the value passed in is a number.
138 * @method checkNumber
139 * @param {Object} val The value to validate
140 * @return {Boolean} true, if the value is valid
142 checkNumber: function (val) {
143 return (!isNaN(val));
147 * Fires a configuration property event using the specified value.
150 * @param {String} key The configuration property's name
151 * @param {value} Object The value of the correct type for the property
153 fireEvent: function ( key, value ) {
154 YAHOO.log("Firing Config event: " + key + "=" + value, "info", "Config");
155 var property = this.config[key];
157 if (property && property.event) {
158 property.event.fire(value);
163 * Adds a property to the Config Object's private config hash.
164 * @method addProperty
165 * @param {String} key The configuration property's name
166 * @param {Object} propertyObject The Object containing all of this
167 * property's arguments
169 addProperty: function ( key, propertyObject ) {
170 key = key.toLowerCase();
171 YAHOO.log("Added property: " + key, "info", "Config");
173 this.config[key] = propertyObject;
175 propertyObject.event = this.createEvent(key, { scope: this.owner });
176 propertyObject.event.signature = CustomEvent.LIST;
179 propertyObject.key = key;
181 if (propertyObject.handler) {
182 propertyObject.event.subscribe(propertyObject.handler,
186 this.setProperty(key, propertyObject.value, true);
188 if (! propertyObject.suppressEvent) {
189 this.queueProperty(key, propertyObject.value);
195 * Returns a key-value configuration map of the values currently set in
198 * @return {Object} The current config, represented in a key-value map
200 getConfig: function () {
203 currCfg = this.config,
207 for (prop in currCfg) {
208 if (Lang.hasOwnProperty(currCfg, prop)) {
209 property = currCfg[prop];
210 if (property && property.event) {
211 cfg[prop] = property.value;
220 * Returns the value of specified property.
221 * @method getProperty
222 * @param {String} key The name of the property
223 * @return {Object} The value of the specified property
225 getProperty: function (key) {
226 var property = this.config[key.toLowerCase()];
227 if (property && property.event) {
228 return property.value;
235 * Resets the specified property's value to its initial value.
236 * @method resetProperty
237 * @param {String} key The name of the property
238 * @return {Boolean} True is the property was reset, false if not
240 resetProperty: function (key) {
242 key = key.toLowerCase();
244 var property = this.config[key];
246 if (property && property.event) {
248 if (this.initialConfig[key] &&
249 !Lang.isUndefined(this.initialConfig[key])) {
251 this.setProperty(key, this.initialConfig[key]);
265 * Sets the value of a property. If the silent property is passed as
266 * true, the property's event will not be fired.
267 * @method setProperty
268 * @param {String} key The name of the property
269 * @param {String} value The value to set the property to
270 * @param {Boolean} silent Whether the value should be set silently,
271 * without firing the property event.
272 * @return {Boolean} True, if the set was successful, false if it failed.
274 setProperty: function (key, value, silent) {
278 key = key.toLowerCase();
279 YAHOO.log("setProperty: " + key + "=" + value, "info", "Config");
281 if (this.queueInProgress && ! silent) {
282 // Currently running through a queue...
283 this.queueProperty(key,value);
287 property = this.config[key];
288 if (property && property.event) {
289 if (property.validator && !property.validator(value)) {
292 property.value = value;
294 this.fireEvent(key, value);
295 this.configChangedEvent.fire([key, value]);
306 * Sets the value of a property and queues its event to execute. If the
307 * event is already scheduled to execute, it is
308 * moved from its current position to the end of the queue.
309 * @method queueProperty
310 * @param {String} key The name of the property
311 * @param {String} value The value to set the property to
312 * @return {Boolean} true, if the set was successful, false if
315 queueProperty: function (key, value) {
317 key = key.toLowerCase();
318 YAHOO.log("queueProperty: " + key + "=" + value, "info", "Config");
320 var property = this.config[key],
321 foundDuplicate = false,
336 if (property && property.event) {
338 if (!Lang.isUndefined(value) && property.validator &&
339 !property.validator(value)) { // validator
343 if (!Lang.isUndefined(value)) {
344 property.value = value;
346 value = property.value;
349 foundDuplicate = false;
350 iLen = this.eventQueue.length;
352 for (i = 0; i < iLen; i++) {
353 queueItem = this.eventQueue[i];
356 queueItemKey = queueItem[0];
357 queueItemValue = queueItem[1];
359 if (queueItemKey == key) {
362 found a dupe... push to end of queue, null
363 current item, and break
366 this.eventQueue[i] = null;
368 this.eventQueue.push(
369 [key, (!Lang.isUndefined(value) ?
370 value : queueItemValue)]);
372 foundDuplicate = true;
378 // this is a refire, or a new property in the queue
380 if (! foundDuplicate && !Lang.isUndefined(value)) {
381 this.eventQueue.push([key, value]);
385 if (property.supercedes) {
387 sLen = property.supercedes.length;
389 for (s = 0; s < sLen; s++) {
391 supercedesCheck = property.supercedes[s];
392 qLen = this.eventQueue.length;
394 for (q = 0; q < qLen; q++) {
395 queueItemCheck = this.eventQueue[q];
397 if (queueItemCheck) {
398 queueItemCheckKey = queueItemCheck[0];
399 queueItemCheckValue = queueItemCheck[1];
401 if (queueItemCheckKey ==
402 supercedesCheck.toLowerCase() ) {
404 this.eventQueue.push([queueItemCheckKey,
405 queueItemCheckValue]);
407 this.eventQueue[q] = null;
416 YAHOO.log("Config event queue: " + this.outputEventQueue(), "info", "Config");
425 * Fires the event for a property using the property's current value.
426 * @method refireEvent
427 * @param {String} key The name of the property
429 refireEvent: function (key) {
431 key = key.toLowerCase();
433 var property = this.config[key];
435 if (property && property.event &&
437 !Lang.isUndefined(property.value)) {
439 if (this.queueInProgress) {
441 this.queueProperty(key);
445 this.fireEvent(key, property.value);
453 * Applies a key-value Object literal to the configuration, replacing
454 * any existing values, and queueing the property events.
455 * Although the values will be set, fireQueue() must be called for their
456 * associated events to execute.
457 * @method applyConfig
458 * @param {Object} userConfig The configuration Object literal
459 * @param {Boolean} init When set to true, the initialConfig will
460 * be set to the userConfig passed in, so that calling a reset will
461 * reset the properties to the passed values.
463 applyConfig: function (userConfig, init) {
470 for (sKey in userConfig) {
471 if (Lang.hasOwnProperty(userConfig, sKey)) {
472 oConfig[sKey.toLowerCase()] = userConfig[sKey];
475 this.initialConfig = oConfig;
478 for (sKey in userConfig) {
479 if (Lang.hasOwnProperty(userConfig, sKey)) {
480 this.queueProperty(sKey, userConfig[sKey]);
486 * Refires the events for all configuration properties using their
490 refresh: function () {
494 for (prop in this.config) {
495 if (Lang.hasOwnProperty(this.config, prop)) {
496 this.refireEvent(prop);
502 * Fires the normalized list of queued property change events
505 fireQueue: function () {
513 this.queueInProgress = true;
514 for (i = 0;i < this.eventQueue.length; i++) {
515 queueItem = this.eventQueue[i];
519 value = queueItem[1];
520 property = this.config[key];
522 property.value = value;
524 // Clear out queue entry, to avoid it being
525 // re-added to the queue by any queueProperty/supercedes
526 // calls which are invoked during fireEvent
527 this.eventQueue[i] = null;
529 this.fireEvent(key,value);
533 this.queueInProgress = false;
534 this.eventQueue = [];
538 * Subscribes an external handler to the change event for any
540 * @method subscribeToConfigEvent
541 * @param {String} key The property name
542 * @param {Function} handler The handler function to use subscribe to
543 * the property's event
544 * @param {Object} obj The Object to use for scoping the event handler
545 * (see CustomEvent documentation)
546 * @param {Boolean} overrideContext Optional. If true, will override
547 * "this" within the handler to map to the scope Object passed into the
549 * @return {Boolean} True, if the subscription was successful,
552 subscribeToConfigEvent: function (key, handler, obj, overrideContext) {
554 var property = this.config[key.toLowerCase()];
556 if (property && property.event) {
557 if (!Config.alreadySubscribed(property.event, handler, obj)) {
558 property.event.subscribe(handler, obj, overrideContext);
568 * Unsubscribes an external handler from the change event for any
570 * @method unsubscribeFromConfigEvent
571 * @param {String} key The property name
572 * @param {Function} handler The handler function to use subscribe to
573 * the property's event
574 * @param {Object} obj The Object to use for scoping the event
575 * handler (see CustomEvent documentation)
576 * @return {Boolean} True, if the unsubscription was successful,
579 unsubscribeFromConfigEvent: function (key, handler, obj) {
580 var property = this.config[key.toLowerCase()];
581 if (property && property.event) {
582 return property.event.unsubscribe(handler, obj);
589 * Returns a string representation of the Config object
591 * @return {String} The Config object in string format.
593 toString: function () {
594 var output = "Config";
596 output += " [" + this.owner.toString() + "]";
602 * Returns a string representation of the Config object's current
604 * @method outputEventQueue
605 * @return {String} The string list of CustomEvents currently queued
608 outputEventQueue: function () {
613 nQueue = this.eventQueue.length;
615 for (q = 0; q < nQueue; q++) {
616 queueItem = this.eventQueue[q];
618 output += queueItem[0] + "=" + queueItem[1] + ", ";
625 * Sets all properties to null, unsubscribes all listeners from each
626 * property's change event and all listeners from the configChangedEvent.
629 destroy: function () {
631 var oConfig = this.config,
636 for (sProperty in oConfig) {
638 if (Lang.hasOwnProperty(oConfig, sProperty)) {
640 oProperty = oConfig[sProperty];
642 oProperty.event.unsubscribeAll();
643 oProperty.event = null;
649 this.configChangedEvent.unsubscribeAll();
651 this.configChangedEvent = null;
654 this.initialConfig = null;
655 this.eventQueue = null;
664 * Checks to determine if a particular function/Object pair are already
665 * subscribed to the specified CustomEvent
666 * @method YAHOO.util.Config.alreadySubscribed
668 * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check
670 * @param {Function} fn The function to look for in the subscribers list
671 * @param {Object} obj The execution scope Object for the subscription
672 * @return {Boolean} true, if the function/Object pair is already subscribed
673 * to the CustomEvent passed in
675 Config.alreadySubscribed = function (evt, fn, obj) {
677 var nSubscribers = evt.subscribers.length,
681 if (nSubscribers > 0) {
682 i = nSubscribers - 1;
684 subsc = evt.subscribers[i];
685 if (subsc && subsc.obj == obj && subsc.fn == fn) {
696 YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider);
700 * The datemath module provides utility methods for basic JavaScript Date object manipulation and
707 * YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility
708 * used for adding, subtracting, and comparing dates.
709 * @namespace YAHOO.widget
712 YAHOO.widget.DateMath = {
714 * Constant field representing Day
723 * Constant field representing Week
732 * Constant field representing Year
741 * Constant field representing Month
750 * Constant field representing one day, in milliseconds
751 * @property ONE_DAY_MS
756 ONE_DAY_MS : 1000*60*60*24,
759 * Constant field representing the date in first week of January
760 * which identifies the first week of the year.
762 * In the U.S, Jan 1st is normally used based on a Sunday start of week.
763 * ISO 8601, used widely throughout Europe, uses Jan 4th, based on a Monday start of week.
765 * @property WEEK_ONE_JAN_DATE
769 WEEK_ONE_JAN_DATE : 1,
772 * Adds the specified amount of time to the this instance.
774 * @param {Date} date The JavaScript Date object to perform addition on
775 * @param {String} field The field constant to be used for performing addition.
776 * @param {Number} amount The number of units (measured in the field constant) to add to the date.
777 * @return {Date} The resulting Date object
779 add : function(date, field, amount) {
780 var d = new Date(date.getTime());
783 var newMonth = date.getMonth() + amount;
787 while (newMonth < 0) {
791 } else if (newMonth > 11) {
792 while (newMonth > 11) {
798 d.setMonth(newMonth);
799 d.setFullYear(date.getFullYear() + years);
802 this._addDays(d, amount);
803 // d.setDate(date.getDate() + amount);
806 d.setFullYear(date.getFullYear() + amount);
809 this._addDays(d, (amount * 7));
810 // d.setDate(date.getDate() + (amount * 7));
817 * Private helper method to account for bug in Safari 2 (webkit < 420)
818 * when Date.setDate(n) is called with n less than -128 or greater than 127.
820 * Fix approach and original findings are available here:
821 * http://brianary.blogspot.com/2006/03/safari-date-bug.html
824 * @param {Date} d JavaScript date object
825 * @param {Number} nDays The number of days to add to the date object (can be negative)
828 _addDays : function(d, nDays) {
829 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420) {
831 // Ensure we don't go below -128 (getDate() is always 1 to 31, so we won't go above 127)
832 for(var min = -128; nDays < min; nDays -= min) {
833 d.setDate(d.getDate() + min);
836 // Ensure we don't go above 96 + 31 = 127
837 for(var max = 96; nDays > max; nDays -= max) {
838 d.setDate(d.getDate() + max);
841 // nDays should be remainder between -128 and 96
843 d.setDate(d.getDate() + nDays);
847 * Subtracts the specified amount of time from the this instance.
849 * @param {Date} date The JavaScript Date object to perform subtraction on
850 * @param {Number} field The this field constant to be used for performing subtraction.
851 * @param {Number} amount The number of units (measured in the field constant) to subtract from the date.
852 * @return {Date} The resulting Date object
854 subtract : function(date, field, amount) {
855 return this.add(date, field, (amount*-1));
859 * Determines whether a given date is before another date on the calendar.
861 * @param {Date} date The Date object to compare with the compare argument
862 * @param {Date} compareTo The Date object to use for the comparison
863 * @return {Boolean} true if the date occurs before the compared date; false if not.
865 before : function(date, compareTo) {
866 var ms = compareTo.getTime();
867 if (date.getTime() < ms) {
875 * Determines whether a given date is after another date on the calendar.
877 * @param {Date} date The Date object to compare with the compare argument
878 * @param {Date} compareTo The Date object to use for the comparison
879 * @return {Boolean} true if the date occurs after the compared date; false if not.
881 after : function(date, compareTo) {
882 var ms = compareTo.getTime();
883 if (date.getTime() > ms) {
891 * Determines whether a given date is between two other dates on the calendar.
893 * @param {Date} date The date to check for
894 * @param {Date} dateBegin The start of the range
895 * @param {Date} dateEnd The end of the range
896 * @return {Boolean} true if the date occurs between the compared dates; false if not.
898 between : function(date, dateBegin, dateEnd) {
899 if (this.after(date, dateBegin) && this.before(date, dateEnd)) {
907 * Retrieves a JavaScript Date object representing January 1 of any given year.
909 * @param {Number} calendarYear The calendar year for which to retrieve January 1
910 * @return {Date} January 1 of the calendar year specified.
912 getJan1 : function(calendarYear) {
913 return this.getDate(calendarYear,0,1);
917 * Calculates the number of days the specified date is from January 1 of the specified calendar year.
918 * Passing January 1 to this function would return an offset value of zero.
919 * @method getDayOffset
920 * @param {Date} date The JavaScript date for which to find the offset
921 * @param {Number} calendarYear The calendar year to use for determining the offset
922 * @return {Number} The number of days since January 1 of the given year
924 getDayOffset : function(date, calendarYear) {
925 var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1.
927 // Find the number of days the passed in date is away from the calendar year start
928 var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS);
933 * Calculates the week number for the given date. Can currently support standard
934 * U.S. week numbers, based on Jan 1st defining the 1st week of the year, and
935 * ISO8601 week numbers, based on Jan 4th defining the 1st week of the year.
937 * @method getWeekNumber
938 * @param {Date} date The JavaScript date for which to find the week number
939 * @param {Number} firstDayOfWeek The index of the first day of the week (0 = Sun, 1 = Mon ... 6 = Sat).
941 * @param {Number} janDate The date in the first week of January which defines week one for the year
942 * Defaults to the value of YAHOO.widget.DateMath.WEEK_ONE_JAN_DATE, which is 1 (Jan 1st).
943 * For the U.S, this is normally Jan 1st. ISO8601 uses Jan 4th to define the first week of the year.
945 * @return {Number} The number of the week containing the given date.
947 getWeekNumber : function(date, firstDayOfWeek, janDate) {
950 firstDayOfWeek = firstDayOfWeek || 0;
951 janDate = janDate || this.WEEK_ONE_JAN_DATE;
953 var targetDate = this.clearTime(date),
957 if (targetDate.getDay() === firstDayOfWeek) {
958 startOfWeek = targetDate;
960 startOfWeek = this.getFirstDayOfWeek(targetDate, firstDayOfWeek);
963 var startYear = startOfWeek.getFullYear();
965 // DST shouldn't be a problem here, math is quicker than setDate();
966 endOfWeek = new Date(startOfWeek.getTime() + 6*this.ONE_DAY_MS);
969 if (startYear !== endOfWeek.getFullYear() && endOfWeek.getDate() >= janDate) {
970 // If years don't match, endOfWeek is in Jan. and if the
971 // week has WEEK_ONE_JAN_DATE in it, it's week one by definition.
974 // Get the 1st day of the 1st week, and
975 // find how many days away we are from it.
976 var weekOne = this.clearTime(this.getDate(startYear, 0, janDate)),
977 weekOneDayOne = this.getFirstDayOfWeek(weekOne, firstDayOfWeek);
979 // Round days to smoothen out 1 hr DST diff
980 var daysDiff = Math.round((targetDate.getTime() - weekOneDayOne.getTime())/this.ONE_DAY_MS);
983 var rem = daysDiff % 7;
984 var weeksDiff = (daysDiff - rem)/7;
985 weekNum = weeksDiff + 1;
991 * Get the first day of the week, for the give date.
992 * @param {Date} dt The date in the week for which the first day is required.
993 * @param {Number} startOfWeek The index for the first day of the week, 0 = Sun, 1 = Mon ... 6 = Sat (defaults to 0)
994 * @return {Date} The first day of the week
996 getFirstDayOfWeek : function (dt, startOfWeek) {
997 startOfWeek = startOfWeek || 0;
998 var dayOfWeekIndex = dt.getDay(),
999 dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7;
1001 return this.subtract(dt, this.DAY, dayOfWeek);
1005 * Determines if a given week overlaps two different years.
1006 * @method isYearOverlapWeek
1007 * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week.
1008 * @return {Boolean} true if the date overlaps two different years.
1010 isYearOverlapWeek : function(weekBeginDate) {
1011 var overlaps = false;
1012 var nextWeek = this.add(weekBeginDate, this.DAY, 6);
1013 if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) {
1020 * Determines if a given week overlaps two different months.
1021 * @method isMonthOverlapWeek
1022 * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week.
1023 * @return {Boolean} true if the date overlaps two different months.
1025 isMonthOverlapWeek : function(weekBeginDate) {
1026 var overlaps = false;
1027 var nextWeek = this.add(weekBeginDate, this.DAY, 6);
1028 if (nextWeek.getMonth() != weekBeginDate.getMonth()) {
1035 * Gets the first day of a month containing a given date.
1036 * @method findMonthStart
1037 * @param {Date} date The JavaScript Date used to calculate the month start
1038 * @return {Date} The JavaScript Date representing the first day of the month
1040 findMonthStart : function(date) {
1041 var start = this.getDate(date.getFullYear(), date.getMonth(), 1);
1046 * Gets the last day of a month containing a given date.
1047 * @method findMonthEnd
1048 * @param {Date} date The JavaScript Date used to calculate the month end
1049 * @return {Date} The JavaScript Date representing the last day of the month
1051 findMonthEnd : function(date) {
1052 var start = this.findMonthStart(date);
1053 var nextMonth = this.add(start, this.MONTH, 1);
1054 var end = this.subtract(nextMonth, this.DAY, 1);
1059 * Clears the time fields from a given date, effectively setting the time to 12 noon.
1061 * @param {Date} date The JavaScript Date for which the time fields will be cleared
1062 * @return {Date} The JavaScript Date cleared of all time fields
1064 clearTime : function(date) {
1065 date.setHours(12,0,0,0);
1070 * Returns a new JavaScript Date object, representing the given year, month and date. Time fields (hr, min, sec, ms) on the new Date object
1071 * 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
1072 * set the year to 19xx if a year (xx) which is less than 100 is provided.
1074 * <em>NOTE:</em>Validation on argument values is not performed. It is the caller's responsibility to ensure
1075 * arguments are valid as per the ECMAScript-262 Date object specification for the new Date(year, month[, date]) constructor.
1078 * @param {Number} y Year.
1079 * @param {Number} m Month index from 0 (Jan) to 11 (Dec).
1080 * @param {Number} d (optional) Date from 1 to 31. If not provided, defaults to 1.
1081 * @return {Date} The JavaScript date object with year, month, date set as provided.
1083 getDate : function(y, m, d) {
1085 if (YAHOO.lang.isUndefined(d)) {
1089 dt = new Date(y, m, d);
1095 dt.setHours(0,0,0,0);
1101 * 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
1102 * multi-month interface. Calendars are generated entirely via script and can be navigated without any page refreshes.
1105 * @namespace YAHOO.widget
1106 * @requires yahoo,dom,event
1110 var Dom = YAHOO.util.Dom,
1111 Event = YAHOO.util.Event,
1113 DateMath = YAHOO.widget.DateMath;
1116 * Calendar is the base class for the Calendar widget. In its most basic
1117 * implementation, it has the ability to render a calendar widget on the page
1118 * that can be manipulated to select a single date, move back and forth between
1120 * <p>To construct the placeholder for the calendar widget, the code is as
1123 * <div id="calContainer"></div>
1127 * <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
1128 * The Calendar can be constructed by simply providing a container ID string,
1129 * or a reference to a container DIV HTMLElement (the element needs to exist
1134 * var c = new YAHOO.widget.Calendar("calContainer", configOptions);
1138 * var containerDiv = YAHOO.util.Dom.get("calContainer");
1139 * var c = new YAHOO.widget.Calendar(containerDiv, configOptions);
1143 * If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
1144 * 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".
1147 * @namespace YAHOO.widget
1150 * @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.
1151 * @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.
1152 * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
1154 function Calendar(id, containerId, config) {
1155 this.init.apply(this, arguments);
1159 * The path to be used for images loaded for the Calendar
1160 * @property YAHOO.widget.Calendar.IMG_ROOT
1162 * @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
1165 Calendar.IMG_ROOT = null;
1168 * Type constant used for renderers to represent an individual date (M/D/Y)
1169 * @property YAHOO.widget.Calendar.DATE
1174 Calendar.DATE = "D";
1177 * Type constant used for renderers to represent an individual date across any year (M/D)
1178 * @property YAHOO.widget.Calendar.MONTH_DAY
1183 Calendar.MONTH_DAY = "MD";
1186 * Type constant used for renderers to represent a weekday
1187 * @property YAHOO.widget.Calendar.WEEKDAY
1192 Calendar.WEEKDAY = "WD";
1195 * Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
1196 * @property YAHOO.widget.Calendar.RANGE
1201 Calendar.RANGE = "R";
1204 * Type constant used for renderers to represent a month across any year
1205 * @property YAHOO.widget.Calendar.MONTH
1210 Calendar.MONTH = "M";
1213 * Constant that represents the total number of date cells that are displayed in a given month
1214 * @property YAHOO.widget.Calendar.DISPLAY_DAYS
1219 Calendar.DISPLAY_DAYS = 42;
1222 * Constant used for halting the execution of the remainder of the render stack
1223 * @property YAHOO.widget.Calendar.STOP_RENDER
1228 Calendar.STOP_RENDER = "S";
1231 * Constant used to represent short date field string formats (e.g. Tu or Feb)
1232 * @property YAHOO.widget.Calendar.SHORT
1237 Calendar.SHORT = "short";
1240 * Constant used to represent long date field string formats (e.g. Monday or February)
1241 * @property YAHOO.widget.Calendar.LONG
1246 Calendar.LONG = "long";
1249 * Constant used to represent medium date field string formats (e.g. Mon)
1250 * @property YAHOO.widget.Calendar.MEDIUM
1255 Calendar.MEDIUM = "medium";
1258 * Constant used to represent single character date field string formats (e.g. M, T, W)
1259 * @property YAHOO.widget.Calendar.ONE_CHAR
1264 Calendar.ONE_CHAR = "1char";
1267 * The set of default Config property keys and values for the Calendar.
1270 * NOTE: This property is made public in order to allow users to change
1271 * the default values of configuration properties. Users should not
1272 * modify the key string, unless they are overriding the Calendar implementation
1276 * The property is an object with key/value pairs, the key being the
1277 * uppercase configuration property name and the value being an object
1278 * literal with a key string property, and a value property, specifying the
1279 * default value of the property. To override a default value, you can set
1280 * the value property, for example, <code>YAHOO.widget.Calendar.DEFAULT_CONFIG.MULTI_SELECT.value = true;</code>
1282 * @property YAHOO.widget.Calendar.DEFAULT_CONFIG
1287 Calendar.DEFAULT_CONFIG = {
1288 YEAR_OFFSET : {key:"year_offset", value:0, supercedes:["pagedate", "selected", "mindate","maxdate"]},
1289 TODAY : {key:"today", value:new Date(), supercedes:["pagedate"]},
1290 PAGEDATE : {key:"pagedate", value:null},
1291 SELECTED : {key:"selected", value:[]},
1292 TITLE : {key:"title", value:""},
1293 CLOSE : {key:"close", value:false},
1294 IFRAME : {key:"iframe", value:(YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) ? true : false},
1295 MINDATE : {key:"mindate", value:null},
1296 MAXDATE : {key:"maxdate", value:null},
1297 MULTI_SELECT : {key:"multi_select", value:false},
1298 START_WEEKDAY : {key:"start_weekday", value:0},
1299 SHOW_WEEKDAYS : {key:"show_weekdays", value:true},
1300 SHOW_WEEK_HEADER : {key:"show_week_header", value:false},
1301 SHOW_WEEK_FOOTER : {key:"show_week_footer", value:false},
1302 HIDE_BLANK_WEEKS : {key:"hide_blank_weeks", value:false},
1303 NAV_ARROW_LEFT: {key:"nav_arrow_left", value:null} ,
1304 NAV_ARROW_RIGHT : {key:"nav_arrow_right", value:null} ,
1305 MONTHS_SHORT : {key:"months_short", value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]},
1306 MONTHS_LONG: {key:"months_long", value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]},
1307 WEEKDAYS_1CHAR: {key:"weekdays_1char", value:["S", "M", "T", "W", "T", "F", "S"]},
1308 WEEKDAYS_SHORT: {key:"weekdays_short", value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]},
1309 WEEKDAYS_MEDIUM: {key:"weekdays_medium", value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]},
1310 WEEKDAYS_LONG: {key:"weekdays_long", value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]},
1311 LOCALE_MONTHS:{key:"locale_months", value:"long"},
1312 LOCALE_WEEKDAYS:{key:"locale_weekdays", value:"short"},
1313 DATE_DELIMITER:{key:"date_delimiter", value:","},
1314 DATE_FIELD_DELIMITER:{key:"date_field_delimiter", value:"/"},
1315 DATE_RANGE_DELIMITER:{key:"date_range_delimiter", value:"-"},
1316 MY_MONTH_POSITION:{key:"my_month_position", value:1},
1317 MY_YEAR_POSITION:{key:"my_year_position", value:2},
1318 MD_MONTH_POSITION:{key:"md_month_position", value:1},
1319 MD_DAY_POSITION:{key:"md_day_position", value:2},
1320 MDY_MONTH_POSITION:{key:"mdy_month_position", value:1},
1321 MDY_DAY_POSITION:{key:"mdy_day_position", value:2},
1322 MDY_YEAR_POSITION:{key:"mdy_year_position", value:3},
1323 MY_LABEL_MONTH_POSITION:{key:"my_label_month_position", value:1},
1324 MY_LABEL_YEAR_POSITION:{key:"my_label_year_position", value:2},
1325 MY_LABEL_MONTH_SUFFIX:{key:"my_label_month_suffix", value:" "},
1326 MY_LABEL_YEAR_SUFFIX:{key:"my_label_year_suffix", value:""},
1327 NAV: {key:"navigator", value: null},
1331 previousMonth : "Previous Month",
1332 nextMonth : "Next Month",
1335 supercedes : ["close", "title"]
1340 * The set of default Config property keys and values for the Calendar
1341 * @property YAHOO.widget.Calendar._DEFAULT_CONFIG
1342 * @deprecated Made public. See the public DEFAULT_CONFIG property for details
1348 Calendar._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG;
1350 var DEF_CFG = Calendar.DEFAULT_CONFIG;
1353 * The set of Custom Event types supported by the Calendar
1354 * @property YAHOO.widget.Calendar._EVENT_TYPES
1360 Calendar._EVENT_TYPES = {
1361 BEFORE_SELECT : "beforeSelect",
1363 BEFORE_DESELECT : "beforeDeselect",
1364 DESELECT : "deselect",
1365 CHANGE_PAGE : "changePage",
1366 BEFORE_RENDER : "beforeRender",
1368 BEFORE_DESTROY : "beforeDestroy",
1369 DESTROY : "destroy",
1372 BEFORE_HIDE : "beforeHide",
1374 BEFORE_SHOW : "beforeShow",
1376 BEFORE_HIDE_NAV : "beforeHideNav",
1377 HIDE_NAV : "hideNav",
1378 BEFORE_SHOW_NAV : "beforeShowNav",
1379 SHOW_NAV : "showNav",
1380 BEFORE_RENDER_NAV : "beforeRenderNav",
1381 RENDER_NAV : "renderNav"
1385 * The set of default style constants for the Calendar
1386 * @property YAHOO.widget.Calendar.STYLES
1388 * @type Object An object with name/value pairs for the class name identifier/value.
1391 CSS_ROW_HEADER: "calrowhead",
1392 CSS_ROW_FOOTER: "calrowfoot",
1393 CSS_CELL : "calcell",
1394 CSS_CELL_SELECTOR : "selector",
1395 CSS_CELL_SELECTED : "selected",
1396 CSS_CELL_SELECTABLE : "selectable",
1397 CSS_CELL_RESTRICTED : "restricted",
1398 CSS_CELL_TODAY : "today",
1399 CSS_CELL_OOM : "oom",
1400 CSS_CELL_OOB : "previous",
1401 CSS_HEADER : "calheader",
1402 CSS_HEADER_TEXT : "calhead",
1403 CSS_BODY : "calbody",
1404 CSS_WEEKDAY_CELL : "calweekdaycell",
1405 CSS_WEEKDAY_ROW : "calweekdayrow",
1406 CSS_FOOTER : "calfoot",
1407 CSS_CALENDAR : "yui-calendar",
1408 CSS_SINGLE : "single",
1409 CSS_CONTAINER : "yui-calcontainer",
1410 CSS_NAV_LEFT : "calnavleft",
1411 CSS_NAV_RIGHT : "calnavright",
1413 CSS_CLOSE : "calclose",
1414 CSS_CELL_TOP : "calcelltop",
1415 CSS_CELL_LEFT : "calcellleft",
1416 CSS_CELL_RIGHT : "calcellright",
1417 CSS_CELL_BOTTOM : "calcellbottom",
1418 CSS_CELL_HOVER : "calcellhover",
1419 CSS_CELL_HIGHLIGHT1 : "highlight1",
1420 CSS_CELL_HIGHLIGHT2 : "highlight2",
1421 CSS_CELL_HIGHLIGHT3 : "highlight3",
1422 CSS_CELL_HIGHLIGHT4 : "highlight4",
1423 CSS_WITH_TITLE: "withtitle",
1424 CSS_FIXED_SIZE: "fixedsize",
1425 CSS_LINK_CLOSE: "link-close"
1429 * The set of default style constants for the Calendar
1430 * @property YAHOO.widget.Calendar._STYLES
1431 * @deprecated Made public. See the public STYLES property for details
1437 Calendar._STYLES = Calendar.STYLES;
1439 Calendar.prototype = {
1442 * The configuration object used to set up the calendars various locale and style options.
1445 * @deprecated Configuration properties should be set by calling Calendar.cfg.setProperty.
1451 * The parent CalendarGroup, only to be set explicitly by the parent group
1453 * @type CalendarGroup
1458 * The index of this item in the parent group
1465 * The collection of calendar table cells
1467 * @type HTMLTableCellElement[]
1472 * 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].
1473 * @property cellDates
1474 * @type Array[](Number[])
1479 * The id that uniquely identifies this Calendar.
1486 * The unique id associated with the Calendar's container
1487 * @property containerId
1493 * 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.
1494 * @property oDomContainer
1497 oDomContainer : null,
1500 * A Date object representing today's date.
1501 * @deprecated Use the "today" configuration property
1508 * The list of render functions, along with required parameters, used to render cells.
1509 * @property renderStack
1515 * A copy of the initial render functions created before rendering.
1516 * @property _renderStack
1520 _renderStack : null,
1523 * A reference to the CalendarNavigator instance created for this Calendar.
1524 * Will be null if the "navigator" configuration property has not been set
1525 * @property oNavigator
1526 * @type CalendarNavigator
1531 * The private list of initially selected dates.
1532 * @property _selectedDates
1536 _selectedDates : null,
1539 * A map of DOM event handlers to attach to cells associated with specific CSS class names
1540 * @property domEventMap
1546 * Protected helper used to parse Calendar constructor/init arguments.
1548 * As of 2.4.0, Calendar supports a simpler constructor
1549 * signature. This method reconciles arguments
1550 * received in the pre 2.4.0 and 2.4.0 formats.
1553 * @method _parseArgs
1554 * @param {Array} Function "arguments" array
1555 * @return {Object} Object with id, container, config properties containing
1556 * the reconciled argument values.
1558 _parseArgs : function(args) {
1560 2.4.0 Constructors signatures
1562 new Calendar(String)
1563 new Calendar(HTMLElement)
1564 new Calendar(String, ConfigObject)
1565 new Calendar(HTMLElement, ConfigObject)
1567 Pre 2.4.0 Constructor signatures
1569 new Calendar(String, String)
1570 new Calendar(String, HTMLElement)
1571 new Calendar(String, String, ConfigObject)
1572 new Calendar(String, HTMLElement, ConfigObject)
1574 var nArgs = {id:null, container:null, config:null};
1576 if (args && args.length && args.length > 0) {
1577 switch (args.length) {
1580 nArgs.container = args[0];
1581 nArgs.config = null;
1584 if (Lang.isObject(args[1]) && !args[1].tagName && !(args[1] instanceof String)) {
1586 nArgs.container = args[0];
1587 nArgs.config = args[1];
1590 nArgs.container = args[1];
1591 nArgs.config = null;
1596 nArgs.container = args[1];
1597 nArgs.config = args[2];
1601 this.logger.log("Invalid constructor/init arguments", "error");
1607 * Initializes the Calendar widget.
1610 * @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.
1611 * @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.
1612 * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
1614 init : function(id, container, config) {
1615 // Normalize 2.4.0, pre 2.4.0 args
1616 var nArgs = this._parseArgs(arguments);
1619 container = nArgs.container;
1620 config = nArgs.config;
1622 this.oDomContainer = Dom.get(container);
1623 if (!this.oDomContainer) { this.logger.log("Container not found in document.", "error"); }
1625 if (!this.oDomContainer.id) {
1626 this.oDomContainer.id = Dom.generateId();
1629 id = this.oDomContainer.id + "_t";
1633 this.containerId = this.oDomContainer.id;
1635 this.logger = new YAHOO.widget.LogWriter("Calendar " + this.id);
1639 * The Config object used to hold the configuration variables for the Calendar
1641 * @type YAHOO.util.Config
1643 this.cfg = new YAHOO.util.Config(this);
1646 * The local object which contains the Calendar's options
1653 * The local object which contains the Calendar's locale settings
1661 Dom.addClass(this.oDomContainer, this.Style.CSS_CONTAINER);
1662 Dom.addClass(this.oDomContainer, this.Style.CSS_SINGLE);
1664 this.cellDates = [];
1666 this.renderStack = [];
1667 this._renderStack = [];
1672 this.cfg.applyConfig(config, true);
1675 this.cfg.fireQueue();
1677 this.today = this.cfg.getProperty("today");
1681 * Default Config listener for the iframe property. If the iframe config property is set to true,
1682 * renders the built-in IFRAME shim if the container is relatively or absolutely positioned.
1684 * @method configIframe
1686 configIframe : function(type, args, obj) {
1687 var useIframe = args[0];
1690 if (Dom.inDocument(this.oDomContainer)) {
1692 var pos = Dom.getStyle(this.oDomContainer, "position");
1694 if (pos == "absolute" || pos == "relative") {
1696 if (!Dom.inDocument(this.iframe)) {
1697 this.iframe = document.createElement("iframe");
1698 this.iframe.src = "javascript:false;";
1700 Dom.setStyle(this.iframe, "opacity", "0");
1702 if (YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) {
1703 Dom.addClass(this.iframe, this.Style.CSS_FIXED_SIZE);
1706 this.oDomContainer.insertBefore(this.iframe, this.oDomContainer.firstChild);
1711 if (this.iframe.parentNode) {
1712 this.iframe.parentNode.removeChild(this.iframe);
1722 * Default handler for the "title" property
1723 * @method configTitle
1725 configTitle : function(type, args, obj) {
1726 var title = args[0];
1728 // "" disables title bar
1730 this.createTitleBar(title);
1732 var close = this.cfg.getProperty(DEF_CFG.CLOSE.key);
1734 this.removeTitleBar();
1736 this.createTitleBar(" ");
1742 * Default handler for the "close" property
1743 * @method configClose
1745 configClose : function(type, args, obj) {
1746 var close = args[0],
1747 title = this.cfg.getProperty(DEF_CFG.TITLE.key);
1751 this.createTitleBar(" ");
1753 this.createCloseButton();
1755 this.removeCloseButton();
1757 this.removeTitleBar();
1763 * Initializes Calendar's built-in CustomEvents
1764 * @method initEvents
1766 initEvents : function() {
1768 var defEvents = Calendar._EVENT_TYPES,
1769 CE = YAHOO.util.CustomEvent,
1770 cal = this; // To help with minification
1773 * Fired before a date selection is made
1774 * @event beforeSelectEvent
1776 cal.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
1779 * Fired when a date selection is made
1780 * @event selectEvent
1781 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
1783 cal.selectEvent = new CE(defEvents.SELECT);
1786 * Fired before a date or set of dates is deselected
1787 * @event beforeDeselectEvent
1789 cal.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
1792 * Fired when a date or set of dates is deselected
1793 * @event deselectEvent
1794 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
1796 cal.deselectEvent = new CE(defEvents.DESELECT);
1799 * Fired when the Calendar page is changed
1800 * @event changePageEvent
1801 * @param {Date} prevDate The date before the page was changed
1802 * @param {Date} newDate The date after the page was changed
1804 cal.changePageEvent = new CE(defEvents.CHANGE_PAGE);
1807 * Fired before the Calendar is rendered
1808 * @event beforeRenderEvent
1810 cal.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
1813 * Fired when the Calendar is rendered
1814 * @event renderEvent
1816 cal.renderEvent = new CE(defEvents.RENDER);
1819 * Fired just before the Calendar is to be destroyed
1820 * @event beforeDestroyEvent
1822 cal.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
1825 * Fired after the Calendar is destroyed. This event should be used
1826 * for notification only. When this event is fired, important Calendar instance
1827 * properties, dom references and event listeners have already been
1828 * removed/dereferenced, and hence the Calendar instance is not in a usable
1831 * @event destroyEvent
1833 cal.destroyEvent = new CE(defEvents.DESTROY);
1836 * Fired when the Calendar is reset
1839 cal.resetEvent = new CE(defEvents.RESET);
1842 * Fired when the Calendar is cleared
1845 cal.clearEvent = new CE(defEvents.CLEAR);
1848 * Fired just before the Calendar is to be shown
1849 * @event beforeShowEvent
1851 cal.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
1854 * Fired after the Calendar is shown
1857 cal.showEvent = new CE(defEvents.SHOW);
1860 * Fired just before the Calendar is to be hidden
1861 * @event beforeHideEvent
1863 cal.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
1866 * Fired after the Calendar is hidden
1869 cal.hideEvent = new CE(defEvents.HIDE);
1872 * Fired just before the CalendarNavigator is to be shown
1873 * @event beforeShowNavEvent
1875 cal.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
1878 * Fired after the CalendarNavigator is shown
1879 * @event showNavEvent
1881 cal.showNavEvent = new CE(defEvents.SHOW_NAV);
1884 * Fired just before the CalendarNavigator is to be hidden
1885 * @event beforeHideNavEvent
1887 cal.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
1890 * Fired after the CalendarNavigator is hidden
1891 * @event hideNavEvent
1893 cal.hideNavEvent = new CE(defEvents.HIDE_NAV);
1896 * Fired just before the CalendarNavigator is to be rendered
1897 * @event beforeRenderNavEvent
1899 cal.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
1902 * Fired after the CalendarNavigator is rendered
1903 * @event renderNavEvent
1905 cal.renderNavEvent = new CE(defEvents.RENDER_NAV);
1907 cal.beforeSelectEvent.subscribe(cal.onBeforeSelect, this, true);
1908 cal.selectEvent.subscribe(cal.onSelect, this, true);
1909 cal.beforeDeselectEvent.subscribe(cal.onBeforeDeselect, this, true);
1910 cal.deselectEvent.subscribe(cal.onDeselect, this, true);
1911 cal.changePageEvent.subscribe(cal.onChangePage, this, true);
1912 cal.renderEvent.subscribe(cal.onRender, this, true);
1913 cal.resetEvent.subscribe(cal.onReset, this, true);
1914 cal.clearEvent.subscribe(cal.onClear, this, true);
1918 * The default event handler for clicks on the "Previous Month" navigation UI
1920 * @method doPreviousMonthNav
1921 * @param {DOMEvent} e The DOM event
1922 * @param {Calendar} cal A reference to the calendar
1924 doPreviousMonthNav : function(e, cal) {
1925 Event.preventDefault(e);
1926 // previousMonth invoked in a timeout, to allow
1927 // event to bubble up, with correct target. Calling
1928 // previousMonth, will call render which will remove
1929 // HTML which generated the event, resulting in an
1930 // invalid event target in certain browsers.
1931 setTimeout(function() {
1932 cal.previousMonth();
1933 var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_LEFT, "a", cal.oDomContainer);
1934 if (navs && navs[0]) {
1945 * The default event handler for clicks on the "Next Month" navigation UI
1947 * @method doNextMonthNav
1948 * @param {DOMEvent} e The DOM event
1949 * @param {Calendar} cal A reference to the calendar
1951 doNextMonthNav : function(e, cal) {
1952 Event.preventDefault(e);
1953 setTimeout(function() {
1955 var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_RIGHT, "a", cal.oDomContainer);
1956 if (navs && navs[0]) {
1967 * The default event handler for date cell selection. Currently attached to
1968 * the Calendar's bounding box, referenced by it's <a href="#property_oDomContainer">oDomContainer</a> property.
1970 * @method doSelectCell
1971 * @param {DOMEvent} e The DOM event
1972 * @param {Calendar} cal A reference to the calendar
1974 doSelectCell : function(e, cal) {
1975 var cell, d, date, index;
1977 var target = Event.getTarget(e),
1978 tagName = target.tagName.toLowerCase(),
1979 defSelector = false;
1981 while (tagName != "td" && !Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1983 if (!defSelector && tagName == "a" && Dom.hasClass(target, cal.Style.CSS_CELL_SELECTOR)) {
1987 target = target.parentNode;
1988 tagName = target.tagName.toLowerCase();
1990 if (target == this.oDomContainer || tagName == "html") {
1996 // Stop link href navigation for default renderer
1997 Event.preventDefault(e);
2002 if (Dom.hasClass(cell, cal.Style.CSS_CELL_SELECTABLE)) {
2003 index = cal.getIndexFromId(cell.id);
2005 d = cal.cellDates[index];
2007 date = DateMath.getDate(d[0],d[1]-1,d[2]);
2011 cal.logger.log("Selecting cell " + index + " via click", "info");
2012 if (cal.Options.MULTI_SELECT) {
2013 link = cell.getElementsByTagName("a")[0];
2018 var cellDate = cal.cellDates[index];
2019 var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
2021 if (cellDateIndex > -1) {
2022 cal.deselectCell(index);
2024 cal.selectCell(index);
2028 link = cell.getElementsByTagName("a")[0];
2032 cal.selectCell(index);
2040 * The event that is executed when the user hovers over a cell
2041 * @method doCellMouseOver
2042 * @param {DOMEvent} e The event
2043 * @param {Calendar} cal A reference to the calendar passed by the Event utility
2045 doCellMouseOver : function(e, cal) {
2048 target = Event.getTarget(e);
2053 while (target.tagName && target.tagName.toLowerCase() != "td") {
2054 target = target.parentNode;
2055 if (!target.tagName || target.tagName.toLowerCase() == "html") {
2060 if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
2061 Dom.addClass(target, cal.Style.CSS_CELL_HOVER);
2066 * The event that is executed when the user moves the mouse out of a cell
2067 * @method doCellMouseOut
2068 * @param {DOMEvent} e The event
2069 * @param {Calendar} cal A reference to the calendar passed by the Event utility
2071 doCellMouseOut : function(e, cal) {
2074 target = Event.getTarget(e);
2079 while (target.tagName && target.tagName.toLowerCase() != "td") {
2080 target = target.parentNode;
2081 if (!target.tagName || target.tagName.toLowerCase() == "html") {
2086 if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
2087 Dom.removeClass(target, cal.Style.CSS_CELL_HOVER);
2091 setupConfig : function() {
2096 * The date to use to represent "Today".
2100 * @default The client side date (new Date()) when the Calendar is instantiated.
2102 cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler:this.configToday, suppressEvent:true } );
2105 * The month/year representing the current visible Calendar date (mm/yyyy)
2107 * @type String | Date
2108 * @default Today's date
2110 cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } );
2113 * The date or range of dates representing the current Calendar selection
2118 cfg.addProperty(DEF_CFG.SELECTED.key, { value:DEF_CFG.SELECTED.value.concat(), handler:this.configSelected } );
2121 * The title to display above the Calendar's month header
2126 cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
2129 * Whether or not a close button should be displayed for this Calendar
2134 cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
2137 * 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.
2138 * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be
2139 * enabled if required.
2143 * @default true for IE6 and below, false for all other browsers
2145 cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
2148 * The minimum selectable date in the current Calendar (mm/dd/yyyy)
2150 * @type String | Date
2153 cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.configMinDate } );
2156 * The maximum selectable date in the current Calendar (mm/dd/yyyy)
2158 * @type String | Date
2161 cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.configMaxDate } );
2163 // Options properties
2166 * True if the Calendar should allow multiple selections. False by default.
2167 * @config MULTI_SELECT
2171 cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2174 * The weekday the week begins on. Default is 0 (Sunday = 0, Monday = 1 ... Saturday = 6).
2175 * @config START_WEEKDAY
2179 cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.configOptions, validator:cfg.checkNumber } );
2182 * True if the Calendar should show weekday labels. True by default.
2183 * @config SHOW_WEEKDAYS
2187 cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2190 * True if the Calendar should show week row headers. False by default.
2191 * @config SHOW_WEEK_HEADER
2195 cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key, { value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2198 * True if the Calendar should show week row footers. False by default.
2199 * @config SHOW_WEEK_FOOTER
2203 cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2206 * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
2207 * @config HIDE_BLANK_WEEKS
2211 cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key, { value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2214 * The image that should be used for the left navigation arrow.
2215 * @config NAV_ARROW_LEFT
2217 * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
2220 cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.configOptions } );
2223 * The image that should be used for the right navigation arrow.
2224 * @config NAV_ARROW_RIGHT
2226 * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
2229 cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.configOptions } );
2231 // Locale properties
2234 * The short month labels for the current locale.
2235 * @config MONTHS_SHORT
2237 * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
2239 cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.configLocale } );
2242 * The long month labels for the current locale.
2243 * @config MONTHS_LONG
2245 * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
2247 cfg.addProperty(DEF_CFG.MONTHS_LONG.key, { value:DEF_CFG.MONTHS_LONG.value, handler:this.configLocale } );
2250 * The 1-character weekday labels for the current locale.
2251 * @config WEEKDAYS_1CHAR
2253 * @default ["S", "M", "T", "W", "T", "F", "S"]
2255 cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.configLocale } );
2258 * The short weekday labels for the current locale.
2259 * @config WEEKDAYS_SHORT
2261 * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
2263 cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.configLocale } );
2266 * The medium weekday labels for the current locale.
2267 * @config WEEKDAYS_MEDIUM
2269 * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
2271 cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.configLocale } );
2274 * The long weekday labels for the current locale.
2275 * @config WEEKDAYS_LONG
2277 * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
2279 cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.configLocale } );
2282 * Refreshes the locale values used to build the Calendar.
2283 * @method refreshLocale
2286 var refreshLocale = function() {
2287 cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
2288 cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
2291 cfg.subscribeToConfigEvent(DEF_CFG.START_WEEKDAY.key, refreshLocale, this, true);
2292 cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_SHORT.key, refreshLocale, this, true);
2293 cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_LONG.key, refreshLocale, this, true);
2294 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_1CHAR.key, refreshLocale, this, true);
2295 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_SHORT.key, refreshLocale, this, true);
2296 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_MEDIUM.key, refreshLocale, this, true);
2297 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_LONG.key, refreshLocale, this, true);
2300 * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
2301 * @config LOCALE_MONTHS
2305 cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.configLocaleValues } );
2308 * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
2309 * @config LOCALE_WEEKDAYS
2313 cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.configLocaleValues } );
2316 * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to
2317 * be used when displaying and parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by
2318 * methods will always represent the Gregorian year, in order to maintain date/month/week values.
2320 * @config YEAR_OFFSET
2324 cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, handler:this.configLocale } );
2327 * The value used to delimit individual dates in a date string passed to various Calendar functions.
2328 * @config DATE_DELIMITER
2332 cfg.addProperty(DEF_CFG.DATE_DELIMITER.key, { value:DEF_CFG.DATE_DELIMITER.value, handler:this.configLocale } );
2335 * The value used to delimit date fields in a date string passed to various Calendar functions.
2336 * @config DATE_FIELD_DELIMITER
2340 cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key, { value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.configLocale } );
2343 * The value used to delimit date ranges in a date string passed to various Calendar functions.
2344 * @config DATE_RANGE_DELIMITER
2348 cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key, { value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.configLocale } );
2351 * The position of the month in a month/year date string
2352 * @config MY_MONTH_POSITION
2356 cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2359 * The position of the year in a month/year date string
2360 * @config MY_YEAR_POSITION
2364 cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2367 * The position of the month in a month/day date string
2368 * @config MD_MONTH_POSITION
2372 cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2375 * The position of the day in a month/year date string
2376 * @config MD_DAY_POSITION
2380 cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key, { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2383 * The position of the month in a month/day/year date string
2384 * @config MDY_MONTH_POSITION
2388 cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2391 * The position of the day in a month/day/year date string
2392 * @config MDY_DAY_POSITION
2396 cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2399 * The position of the year in a month/day/year date string
2400 * @config MDY_YEAR_POSITION
2404 cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2407 * The position of the month in the month year label string used as the Calendar header
2408 * @config MY_LABEL_MONTH_POSITION
2412 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2415 * The position of the year in the month year label string used as the Calendar header
2416 * @config MY_LABEL_YEAR_POSITION
2420 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2423 * The suffix used after the month when rendering the Calendar header
2424 * @config MY_LABEL_MONTH_SUFFIX
2428 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.configLocale } );
2431 * The suffix used after the year when rendering the Calendar header
2432 * @config MY_LABEL_YEAR_SUFFIX
2436 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.configLocale } );
2439 * Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a
2440 * specific Month/Year without having to scroll sequentially through months.
2442 * Setting this property to null (default value) or false, will disable the CalendarNavigator UI.
2445 * Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values.
2448 * This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI.
2449 * The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object.
2450 * Any properties which are not provided will use the default values (defined in the CalendarNavigator class).
2454 * <dd><em>Object</em> : An object with the properties shown below, defining the string labels to use in the Navigator's UI
2456 * <dt>month</dt><dd><em>String</em> : The string to use for the month label. Defaults to "Month".</dd>
2457 * <dt>year</dt><dd><em>String</em> : The string to use for the year label. Defaults to "Year".</dd>
2458 * <dt>submit</dt><dd><em>String</em> : The string to use for the submit button label. Defaults to "Okay".</dd>
2459 * <dt>cancel</dt><dd><em>String</em> : The string to use for the cancel button label. Defaults to "Cancel".</dd>
2460 * <dt>invalidYear</dt><dd><em>String</em> : The string to use for invalid year values. Defaults to "Year needs to be a number".</dd>
2463 * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd>
2464 * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
2470 * month:"Calendar Month",
2471 * year:"Calendar Year",
2474 * invalidYear: "Please enter a valid year"
2476 * monthFormat: YAHOO.widget.Calendar.SHORT,
2477 * initialFocus: "month"
2481 * @type {Object|Boolean}
2484 cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
2487 * The map of UI strings which the Calendar UI uses.
2491 * @default An object with the properties shown below:
2493 * <dt>previousMonth</dt><dd><em>String</em> : The string to use for the "Previous Month" navigation UI. Defaults to "Previous Month".</dd>
2494 * <dt>nextMonth</dt><dd><em>String</em> : The string to use for the "Next Month" navigation UI. Defaults to "Next Month".</dd>
2495 * <dt>close</dt><dd><em>String</em> : The string to use for the close button label. Defaults to "Close".</dd>
2498 cfg.addProperty(DEF_CFG.STRINGS.key, {
2499 value:DEF_CFG.STRINGS.value,
2500 handler:this.configStrings,
2501 validator: function(val) {
2502 return Lang.isObject(val);
2504 supercedes:DEF_CFG.STRINGS.supercedes
2509 * The default handler for the "strings" property
2510 * @method configStrings
2512 configStrings : function(type, args, obj) {
2513 var val = Lang.merge(DEF_CFG.STRINGS.value, args[0]);
2514 this.cfg.setProperty(DEF_CFG.STRINGS.key, val, true);
2518 * The default handler for the "pagedate" property
2519 * @method configPageDate
2521 configPageDate : function(type, args, obj) {
2522 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, this._parsePageDate(args[0]), true);
2526 * The default handler for the "mindate" property
2527 * @method configMinDate
2529 configMinDate : function(type, args, obj) {
2531 if (Lang.isString(val)) {
2532 val = this._parseDate(val);
2533 this.cfg.setProperty(DEF_CFG.MINDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
2538 * The default handler for the "maxdate" property
2539 * @method configMaxDate
2541 configMaxDate : function(type, args, obj) {
2543 if (Lang.isString(val)) {
2544 val = this._parseDate(val);
2545 this.cfg.setProperty(DEF_CFG.MAXDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
2550 * The default handler for the "today" property
2551 * @method configToday
2553 configToday : function(type, args, obj) {
2554 // Only do this for initial set. Changing the today property after the initial
2555 // set, doesn't affect pagedate
2557 if (Lang.isString(val)) {
2558 val = this._parseDate(val);
2560 var today = DateMath.clearTime(val);
2561 if (!this.cfg.initialConfig[DEF_CFG.PAGEDATE.key]) {
2562 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, today);
2565 this.cfg.setProperty(DEF_CFG.TODAY.key, today, true);
2569 * The default handler for the "selected" property
2570 * @method configSelected
2572 configSelected : function(type, args, obj) {
2573 var selected = args[0],
2574 cfgSelected = DEF_CFG.SELECTED.key;
2577 if (Lang.isString(selected)) {
2578 this.cfg.setProperty(cfgSelected, this._parseDates(selected), true);
2581 if (! this._selectedDates) {
2582 this._selectedDates = this.cfg.getProperty(cfgSelected);
2587 * The default handler for all configuration options properties
2588 * @method configOptions
2590 configOptions : function(type, args, obj) {
2591 this.Options[type.toUpperCase()] = args[0];
2595 * The default handler for all configuration locale properties
2596 * @method configLocale
2598 configLocale : function(type, args, obj) {
2599 this.Locale[type.toUpperCase()] = args[0];
2601 this.cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
2602 this.cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
2606 * The default handler for all configuration locale field length properties
2607 * @method configLocaleValues
2609 configLocaleValues : function(type, args, obj) {
2611 type = type.toLowerCase();
2615 Locale = this.Locale;
2618 case DEF_CFG.LOCALE_MONTHS.key:
2620 case Calendar.SHORT:
2621 Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_SHORT.key).concat();
2624 Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_LONG.key).concat();
2628 case DEF_CFG.LOCALE_WEEKDAYS.key:
2630 case Calendar.ONE_CHAR:
2631 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_1CHAR.key).concat();
2633 case Calendar.SHORT:
2634 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_SHORT.key).concat();
2636 case Calendar.MEDIUM:
2637 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_MEDIUM.key).concat();
2640 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_LONG.key).concat();
2644 var START_WEEKDAY = cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
2646 if (START_WEEKDAY > 0) {
2647 for (var w=0; w < START_WEEKDAY; ++w) {
2648 Locale.LOCALE_WEEKDAYS.push(Locale.LOCALE_WEEKDAYS.shift());
2656 * The default handler for the "navigator" property
2657 * @method configNavigator
2659 configNavigator : function(type, args, obj) {
2661 if (YAHOO.widget.CalendarNavigator && (val === true || Lang.isObject(val))) {
2662 if (!this.oNavigator) {
2663 this.oNavigator = new YAHOO.widget.CalendarNavigator(this);
2664 // Cleanup DOM Refs/Events before innerHTML is removed.
2665 this.beforeRenderEvent.subscribe(function () {
2667 this.oNavigator.erase();
2672 if (this.oNavigator) {
2673 this.oNavigator.destroy();
2674 this.oNavigator = null;
2680 * Defines the style constants for the Calendar
2681 * @method initStyles
2683 initStyles : function() {
2685 var defStyle = Calendar.STYLES;
2689 * @property Style.CSS_ROW_HEADER
2691 CSS_ROW_HEADER: defStyle.CSS_ROW_HEADER,
2693 * @property Style.CSS_ROW_FOOTER
2695 CSS_ROW_FOOTER: defStyle.CSS_ROW_FOOTER,
2697 * @property Style.CSS_CELL
2699 CSS_CELL : defStyle.CSS_CELL,
2701 * @property Style.CSS_CELL_SELECTOR
2703 CSS_CELL_SELECTOR : defStyle.CSS_CELL_SELECTOR,
2705 * @property Style.CSS_CELL_SELECTED
2707 CSS_CELL_SELECTED : defStyle.CSS_CELL_SELECTED,
2709 * @property Style.CSS_CELL_SELECTABLE
2711 CSS_CELL_SELECTABLE : defStyle.CSS_CELL_SELECTABLE,
2713 * @property Style.CSS_CELL_RESTRICTED
2715 CSS_CELL_RESTRICTED : defStyle.CSS_CELL_RESTRICTED,
2717 * @property Style.CSS_CELL_TODAY
2719 CSS_CELL_TODAY : defStyle.CSS_CELL_TODAY,
2721 * @property Style.CSS_CELL_OOM
2723 CSS_CELL_OOM : defStyle.CSS_CELL_OOM,
2725 * @property Style.CSS_CELL_OOB
2727 CSS_CELL_OOB : defStyle.CSS_CELL_OOB,
2729 * @property Style.CSS_HEADER
2731 CSS_HEADER : defStyle.CSS_HEADER,
2733 * @property Style.CSS_HEADER_TEXT
2735 CSS_HEADER_TEXT : defStyle.CSS_HEADER_TEXT,
2737 * @property Style.CSS_BODY
2739 CSS_BODY : defStyle.CSS_BODY,
2741 * @property Style.CSS_WEEKDAY_CELL
2743 CSS_WEEKDAY_CELL : defStyle.CSS_WEEKDAY_CELL,
2745 * @property Style.CSS_WEEKDAY_ROW
2747 CSS_WEEKDAY_ROW : defStyle.CSS_WEEKDAY_ROW,
2749 * @property Style.CSS_FOOTER
2751 CSS_FOOTER : defStyle.CSS_FOOTER,
2753 * @property Style.CSS_CALENDAR
2755 CSS_CALENDAR : defStyle.CSS_CALENDAR,
2757 * @property Style.CSS_SINGLE
2759 CSS_SINGLE : defStyle.CSS_SINGLE,
2761 * @property Style.CSS_CONTAINER
2763 CSS_CONTAINER : defStyle.CSS_CONTAINER,
2765 * @property Style.CSS_NAV_LEFT
2767 CSS_NAV_LEFT : defStyle.CSS_NAV_LEFT,
2769 * @property Style.CSS_NAV_RIGHT
2771 CSS_NAV_RIGHT : defStyle.CSS_NAV_RIGHT,
2773 * @property Style.CSS_NAV
2775 CSS_NAV : defStyle.CSS_NAV,
2777 * @property Style.CSS_CLOSE
2779 CSS_CLOSE : defStyle.CSS_CLOSE,
2781 * @property Style.CSS_CELL_TOP
2783 CSS_CELL_TOP : defStyle.CSS_CELL_TOP,
2785 * @property Style.CSS_CELL_LEFT
2787 CSS_CELL_LEFT : defStyle.CSS_CELL_LEFT,
2789 * @property Style.CSS_CELL_RIGHT
2791 CSS_CELL_RIGHT : defStyle.CSS_CELL_RIGHT,
2793 * @property Style.CSS_CELL_BOTTOM
2795 CSS_CELL_BOTTOM : defStyle.CSS_CELL_BOTTOM,
2797 * @property Style.CSS_CELL_HOVER
2799 CSS_CELL_HOVER : defStyle.CSS_CELL_HOVER,
2801 * @property Style.CSS_CELL_HIGHLIGHT1
2803 CSS_CELL_HIGHLIGHT1 : defStyle.CSS_CELL_HIGHLIGHT1,
2805 * @property Style.CSS_CELL_HIGHLIGHT2
2807 CSS_CELL_HIGHLIGHT2 : defStyle.CSS_CELL_HIGHLIGHT2,
2809 * @property Style.CSS_CELL_HIGHLIGHT3
2811 CSS_CELL_HIGHLIGHT3 : defStyle.CSS_CELL_HIGHLIGHT3,
2813 * @property Style.CSS_CELL_HIGHLIGHT4
2815 CSS_CELL_HIGHLIGHT4 : defStyle.CSS_CELL_HIGHLIGHT4,
2817 * @property Style.CSS_WITH_TITLE
2819 CSS_WITH_TITLE : defStyle.CSS_WITH_TITLE,
2821 * @property Style.CSS_FIXED_SIZE
2823 CSS_FIXED_SIZE : defStyle.CSS_FIXED_SIZE,
2825 * @property Style.CSS_LINK_CLOSE
2827 CSS_LINK_CLOSE : defStyle.CSS_LINK_CLOSE
2832 * Builds the date label that will be displayed in the calendar header or
2833 * footer, depending on configuration.
2834 * @method buildMonthLabel
2835 * @return {String} The formatted calendar month label
2837 buildMonthLabel : function() {
2838 return this._buildMonthLabel(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
2842 * Helper method, to format a Month Year string, given a JavaScript Date, based on the
2843 * Calendar localization settings
2845 * @method _buildMonthLabel
2847 * @param {Date} date
2848 * @return {String} Formated month, year string
2850 _buildMonthLabel : function(date) {
2851 var monthLabel = this.Locale.LOCALE_MONTHS[date.getMonth()] + this.Locale.MY_LABEL_MONTH_SUFFIX,
2852 yearLabel = (date.getFullYear() + this.Locale.YEAR_OFFSET) + this.Locale.MY_LABEL_YEAR_SUFFIX;
2854 if (this.Locale.MY_LABEL_MONTH_POSITION == 2 || this.Locale.MY_LABEL_YEAR_POSITION == 1) {
2855 return yearLabel + monthLabel;
2857 return monthLabel + yearLabel;
2862 * Builds the date digit that will be displayed in calendar cells
2863 * @method buildDayLabel
2864 * @param {Date} workingDate The current working date
2865 * @return {String} The formatted day label
2867 buildDayLabel : function(workingDate) {
2868 return workingDate.getDate();
2872 * Creates the title bar element and adds it to Calendar container DIV
2874 * @method createTitleBar
2875 * @param {String} strTitle The title to display in the title bar
2876 * @return The title bar element
2878 createTitleBar : function(strTitle) {
2879 var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || document.createElement("div");
2880 tDiv.className = YAHOO.widget.CalendarGroup.CSS_2UPTITLE;
2881 tDiv.innerHTML = strTitle;
2882 this.oDomContainer.insertBefore(tDiv, this.oDomContainer.firstChild);
2884 Dom.addClass(this.oDomContainer, this.Style.CSS_WITH_TITLE);
2890 * Removes the title bar element from the DOM
2892 * @method removeTitleBar
2894 removeTitleBar : function() {
2895 var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || null;
2897 Event.purgeElement(tDiv);
2898 this.oDomContainer.removeChild(tDiv);
2900 Dom.removeClass(this.oDomContainer, this.Style.CSS_WITH_TITLE);
2904 * Creates the close button HTML element and adds it to Calendar container DIV
2906 * @method createCloseButton
2907 * @return The close HTML element created
2909 createCloseButton : function() {
2910 var cssClose = YAHOO.widget.CalendarGroup.CSS_2UPCLOSE,
2911 cssLinkClose = this.Style.CSS_LINK_CLOSE,
2912 DEPR_CLOSE_PATH = "us/my/bn/x_d.gif",
2914 lnk = Dom.getElementsByClassName(cssLinkClose, "a", this.oDomContainer)[0],
2915 strings = this.cfg.getProperty(DEF_CFG.STRINGS.key),
2916 closeStr = (strings && strings.close) ? strings.close : "";
2919 lnk = document.createElement("a");
2920 Event.addListener(lnk, "click", function(e, cal) {
2922 Event.preventDefault(e);
2927 lnk.className = cssLinkClose;
2929 if (Calendar.IMG_ROOT !== null) {
2930 var img = Dom.getElementsByClassName(cssClose, "img", lnk)[0] || document.createElement("img");
2931 img.src = Calendar.IMG_ROOT + DEPR_CLOSE_PATH;
2932 img.className = cssClose;
2933 lnk.appendChild(img);
2935 lnk.innerHTML = '<span class="' + cssClose + ' ' + this.Style.CSS_CLOSE + '">' + closeStr + '</span>';
2937 this.oDomContainer.appendChild(lnk);
2943 * Removes the close button HTML element from the DOM
2945 * @method removeCloseButton
2947 removeCloseButton : function() {
2948 var btn = Dom.getElementsByClassName(this.Style.CSS_LINK_CLOSE, "a", this.oDomContainer)[0] || null;
2950 Event.purgeElement(btn);
2951 this.oDomContainer.removeChild(btn);
2956 * Renders the calendar header.
2957 * @method renderHeader
2958 * @param {Array} html The current working HTML array
2959 * @return {Array} The current working HTML array
2961 renderHeader : function(html) {
2963 this.logger.log("Rendering header", "render");
2966 DEPR_NAV_LEFT = "us/tr/callt.gif",
2967 DEPR_NAV_RIGHT = "us/tr/calrt.gif",
2969 pageDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
2970 strings= cfg.getProperty(DEF_CFG.STRINGS.key),
2971 prevStr = (strings && strings.previousMonth) ? strings.previousMonth : "",
2972 nextStr = (strings && strings.nextMonth) ? strings.nextMonth : "",
2975 if (cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
2979 if (cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
2983 html[html.length] = "<thead>";
2984 html[html.length] = "<tr>";
2985 html[html.length] = '<th colspan="' + colSpan + '" class="' + this.Style.CSS_HEADER_TEXT + '">';
2986 html[html.length] = '<div class="' + this.Style.CSS_HEADER + '">';
2988 var renderLeft, renderRight = false;
2991 if (this.index === 0) {
2994 if (this.index == (this.parent.cfg.getProperty("pages") -1)) {
3003 monthLabel = this._buildMonthLabel(DateMath.subtract(pageDate, DateMath.MONTH, 1));
3005 var leftArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_LEFT.key);
3006 // Check for deprecated customization - If someone set IMG_ROOT, but didn't set NAV_ARROW_LEFT, then set NAV_ARROW_LEFT to the old deprecated value
3007 if (leftArrow === null && Calendar.IMG_ROOT !== null) {
3008 leftArrow = Calendar.IMG_ROOT + DEPR_NAV_LEFT;
3010 var leftStyle = (leftArrow === null) ? "" : ' style="background-image:url(' + leftArrow + ')"';
3011 html[html.length] = '<a class="' + this.Style.CSS_NAV_LEFT + '"' + leftStyle + ' href="#">' + prevStr + ' (' + monthLabel + ')' + '</a>';
3014 var lbl = this.buildMonthLabel();
3015 var cal = this.parent || this;
3016 if (cal.cfg.getProperty("navigator")) {
3017 lbl = "<a class=\"" + this.Style.CSS_NAV + "\" href=\"#\">" + lbl + "</a>";
3019 html[html.length] = lbl;
3022 monthLabel = this._buildMonthLabel(DateMath.add(pageDate, DateMath.MONTH, 1));
3024 var rightArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_RIGHT.key);
3025 if (rightArrow === null && Calendar.IMG_ROOT !== null) {
3026 rightArrow = Calendar.IMG_ROOT + DEPR_NAV_RIGHT;
3028 var rightStyle = (rightArrow === null) ? "" : ' style="background-image:url(' + rightArrow + ')"';
3029 html[html.length] = '<a class="' + this.Style.CSS_NAV_RIGHT + '"' + rightStyle + ' href="#">' + nextStr + ' (' + monthLabel + ')' + '</a>';
3032 html[html.length] = '</div>\n</th>\n</tr>';
3034 if (cfg.getProperty(DEF_CFG.SHOW_WEEKDAYS.key)) {
3035 html = this.buildWeekdays(html);
3038 html[html.length] = '</thead>';
3044 * Renders the Calendar's weekday headers.
3045 * @method buildWeekdays
3046 * @param {Array} html The current working HTML array
3047 * @return {Array} The current working HTML array
3049 buildWeekdays : function(html) {
3051 html[html.length] = '<tr class="' + this.Style.CSS_WEEKDAY_ROW + '">';
3053 if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
3054 html[html.length] = '<th> </th>';
3057 for(var i=0;i < this.Locale.LOCALE_WEEKDAYS.length; ++i) {
3058 html[html.length] = '<th class="' + this.Style.CSS_WEEKDAY_CELL + '">' + this.Locale.LOCALE_WEEKDAYS[i] + '</th>';
3061 if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
3062 html[html.length] = '<th> </th>';
3065 html[html.length] = '</tr>';
3071 * Renders the calendar body.
3072 * @method renderBody
3073 * @param {Date} workingDate The current working Date being used for the render process
3074 * @param {Array} html The current working HTML array
3075 * @return {Array} The current working HTML array
3077 renderBody : function(workingDate, html) {
3078 this.logger.log("Rendering body", "render");
3080 var startDay = this.cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
3082 this.preMonthDays = workingDate.getDay();
3084 this.preMonthDays -= startDay;
3086 if (this.preMonthDays < 0) {
3087 this.preMonthDays += 7;
3090 this.monthDays = DateMath.findMonthEnd(workingDate).getDate();
3091 this.postMonthDays = Calendar.DISPLAY_DAYS-this.preMonthDays-this.monthDays;
3093 this.logger.log(this.preMonthDays + " preciding out-of-month days", "render");
3094 this.logger.log(this.monthDays + " month days", "render");
3095 this.logger.log(this.postMonthDays + " post-month days", "render");
3097 workingDate = DateMath.subtract(workingDate, DateMath.DAY, this.preMonthDays);
3098 this.logger.log("Calendar page starts on " + workingDate, "render");
3103 cellPrefix = "_cell",
3104 workingDayPrefix = "wd",
3110 todayYear = t.getFullYear(),
3111 todayMonth = t.getMonth(),
3112 todayDate = t.getDate(),
3113 useDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
3114 hideBlankWeeks = cfg.getProperty(DEF_CFG.HIDE_BLANK_WEEKS.key),
3115 showWeekFooter = cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key),
3116 showWeekHeader = cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key),
3117 mindate = cfg.getProperty(DEF_CFG.MINDATE.key),
3118 maxdate = cfg.getProperty(DEF_CFG.MAXDATE.key),
3119 yearOffset = this.Locale.YEAR_OFFSET;
3122 mindate = DateMath.clearTime(mindate);
3125 maxdate = DateMath.clearTime(maxdate);
3128 html[html.length] = '<tbody class="m' + (useDate.getMonth()+1) + ' ' + this.Style.CSS_BODY + '">';
3131 tempDiv = document.createElement("div"),
3132 cell = document.createElement("td");
3134 tempDiv.appendChild(cell);
3136 var cal = this.parent || this;
3138 for (var r=0;r<6;r++) {
3139 weekNum = DateMath.getWeekNumber(workingDate, startDay);
3140 weekClass = weekPrefix + weekNum;
3142 // Local OOM check for performance, since we already have pagedate
3143 if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth()) {
3146 html[html.length] = '<tr class="' + weekClass + '">';
3148 if (showWeekHeader) { html = this.renderRowHeader(weekNum, html); }
3150 for (var d=0; d < 7; d++){ // Render actual days
3154 this.clearElement(cell);
3155 cell.className = this.Style.CSS_CELL;
3156 cell.id = this.id + cellPrefix + i;
3157 this.logger.log("Rendering cell " + cell.id + " (" + workingDate.getFullYear() + yearOffset + "-" + (workingDate.getMonth()+1) + "-" + workingDate.getDate() + ")", "cellrender");
3159 if (workingDate.getDate() == todayDate &&
3160 workingDate.getMonth() == todayMonth &&
3161 workingDate.getFullYear() == todayYear) {
3162 cellRenderers[cellRenderers.length]=cal.renderCellStyleToday;
3165 var workingArray = [workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()];
3166 this.cellDates[this.cellDates.length] = workingArray; // Add this date to cellDates
3168 // Local OOM check for performance, since we already have pagedate
3169 if (workingDate.getMonth() != useDate.getMonth()) {
3170 cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth;
3172 Dom.addClass(cell, workingDayPrefix + workingDate.getDay());
3173 Dom.addClass(cell, dayPrefix + workingDate.getDate());
3175 for (var s=0;s<this.renderStack.length;++s) {
3179 var rArray = this.renderStack[s],
3187 month = rArray[1][1];
3189 year = rArray[1][0];
3191 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == year) {
3192 renderer = rArray[2];
3193 this.renderStack.splice(s,1);
3196 case Calendar.MONTH_DAY:
3197 month = rArray[1][0];
3200 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day) {
3201 renderer = rArray[2];
3202 this.renderStack.splice(s,1);
3205 case Calendar.RANGE:
3206 var date1 = rArray[1][0],
3207 date2 = rArray[1][1],
3211 d1 = DateMath.getDate(d1year, d1month-1, d1day),
3215 d2 = DateMath.getDate(d2year, d2month-1, d2day);
3217 if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime()) {
3218 renderer = rArray[2];
3220 if (workingDate.getTime()==d2.getTime()) {
3221 this.renderStack.splice(s,1);
3225 case Calendar.WEEKDAY:
3226 var weekday = rArray[1][0];
3227 if (workingDate.getDay()+1 == weekday) {
3228 renderer = rArray[2];
3231 case Calendar.MONTH:
3232 month = rArray[1][0];
3233 if (workingDate.getMonth()+1 == month) {
3234 renderer = rArray[2];
3240 cellRenderers[cellRenderers.length]=renderer;
3246 if (this._indexOfSelectedFieldArray(workingArray) > -1) {
3247 cellRenderers[cellRenderers.length]=cal.renderCellStyleSelected;
3250 if ((mindate && (workingDate.getTime() < mindate.getTime())) ||
3251 (maxdate && (workingDate.getTime() > maxdate.getTime()))
3253 cellRenderers[cellRenderers.length]=cal.renderOutOfBoundsDate;
3255 cellRenderers[cellRenderers.length]=cal.styleCellDefault;
3256 cellRenderers[cellRenderers.length]=cal.renderCellDefault;
3259 for (var x=0; x < cellRenderers.length; ++x) {
3260 this.logger.log("renderer[" + x + "] for (" + workingDate.getFullYear() + yearOffset + "-" + (workingDate.getMonth()+1) + "-" + workingDate.getDate() + ")", "cellrender");
3261 if (cellRenderers[x].call(cal, workingDate, cell) == Calendar.STOP_RENDER) {
3266 workingDate.setTime(workingDate.getTime() + DateMath.ONE_DAY_MS);
3267 // Just in case we crossed DST/Summertime boundaries
3268 workingDate = DateMath.clearTime(workingDate);
3270 if (i >= 0 && i <= 6) {
3271 Dom.addClass(cell, this.Style.CSS_CELL_TOP);
3273 if ((i % 7) === 0) {
3274 Dom.addClass(cell, this.Style.CSS_CELL_LEFT);
3276 if (((i+1) % 7) === 0) {
3277 Dom.addClass(cell, this.Style.CSS_CELL_RIGHT);
3280 var postDays = this.postMonthDays;
3281 if (hideBlankWeeks && postDays >= 7) {
3282 var blankWeeks = Math.floor(postDays/7);
3283 for (var p=0;p<blankWeeks;++p) {
3288 if (i >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
3289 Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM);
3292 html[html.length] = tempDiv.innerHTML;
3296 if (showWeekFooter) { html = this.renderRowFooter(weekNum, html); }
3298 html[html.length] = '</tr>';
3302 html[html.length] = '</tbody>';
3308 * Renders the calendar footer. In the default implementation, there is
3310 * @method renderFooter
3311 * @param {Array} html The current working HTML array
3312 * @return {Array} The current working HTML array
3314 renderFooter : function(html) { return html; },
3317 * Renders the calendar after it has been configured. The render() method has a specific call chain that will execute
3318 * when the method is called: renderHeader, renderBody, renderFooter.
3319 * Refer to the documentation for those methods for information on
3320 * individual render tasks.
3323 render : function() {
3324 this.beforeRenderEvent.fire();
3326 // Find starting day of the current month
3327 var workingDate = DateMath.findMonthStart(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
3329 this.resetRenderers();
3330 this.cellDates.length = 0;
3332 Event.purgeElement(this.oDomContainer, true);
3336 html[html.length] = '<table cellSpacing="0" class="' + this.Style.CSS_CALENDAR + ' y' + (workingDate.getFullYear() + this.Locale.YEAR_OFFSET) +'" id="' + this.id + '">';
3337 html = this.renderHeader(html);
3338 html = this.renderBody(workingDate, html);
3339 html = this.renderFooter(html);
3340 html[html.length] = '</table>';
3342 this.oDomContainer.innerHTML = html.join("\n");
3344 this.applyListeners();
3345 this.cells = Dom.getElementsByClassName(this.Style.CSS_CELL, "td", this.id);
3347 this.cfg.refireEvent(DEF_CFG.TITLE.key);
3348 this.cfg.refireEvent(DEF_CFG.CLOSE.key);
3349 this.cfg.refireEvent(DEF_CFG.IFRAME.key);
3351 this.renderEvent.fire();
3355 * Applies the Calendar's DOM listeners to applicable elements.
3356 * @method applyListeners
3358 applyListeners : function() {
3359 var root = this.oDomContainer,
3360 cal = this.parent || this,
3364 var linkLeft = Dom.getElementsByClassName(this.Style.CSS_NAV_LEFT, anchor, root),
3365 linkRight = Dom.getElementsByClassName(this.Style.CSS_NAV_RIGHT, anchor, root);
3367 if (linkLeft && linkLeft.length > 0) {
3368 this.linkLeft = linkLeft[0];
3369 Event.addListener(this.linkLeft, click, this.doPreviousMonthNav, cal, true);
3372 if (linkRight && linkRight.length > 0) {
3373 this.linkRight = linkRight[0];
3374 Event.addListener(this.linkRight, click, this.doNextMonthNav, cal, true);
3377 if (cal.cfg.getProperty("navigator") !== null) {
3378 this.applyNavListeners();
3381 if (this.domEventMap) {
3383 for (var cls in this.domEventMap) {
3384 if (Lang.hasOwnProperty(this.domEventMap, cls)) {
3385 var items = this.domEventMap[cls];
3387 if (! (items instanceof Array)) {
3391 for (var i=0;i<items.length;i++) {
3392 var item = items[i];
3393 elements = Dom.getElementsByClassName(cls, item.tag, this.oDomContainer);
3395 for (var c=0;c<elements.length;c++) {
3397 Event.addListener(el, item.event, item.handler, item.scope, item.correct );
3404 Event.addListener(this.oDomContainer, "click", this.doSelectCell, this);
3405 Event.addListener(this.oDomContainer, "mouseover", this.doCellMouseOver, this);
3406 Event.addListener(this.oDomContainer, "mouseout", this.doCellMouseOut, this);
3409 applyNavListeners : function() {
3410 var calParent = this.parent || this,
3412 navBtns = Dom.getElementsByClassName(this.Style.CSS_NAV, "a", this.oDomContainer);
3414 if (navBtns.length > 0) {
3416 Event.addListener(navBtns, "click", function (e, obj) {
3417 var target = Event.getTarget(e);
3419 if (this === target || Dom.isAncestor(this, target)) {
3420 Event.preventDefault(e);
3422 var navigator = calParent.oNavigator;
3424 var pgdate = cal.cfg.getProperty("pagedate");
3425 navigator.setYear(pgdate.getFullYear() + cal.Locale.YEAR_OFFSET);
3426 navigator.setMonth(pgdate.getMonth());
3434 * Retrieves the Date object for the specified Calendar cell
3435 * @method getDateByCellId
3436 * @param {String} id The id of the cell
3437 * @return {Date} The Date object for the specified Calendar cell
3439 getDateByCellId : function(id) {
3440 var date = this.getDateFieldsByCellId(id);
3441 return (date) ? DateMath.getDate(date[0],date[1]-1,date[2]) : null;
3445 * Retrieves the Date object for the specified Calendar cell
3446 * @method getDateFieldsByCellId
3447 * @param {String} id The id of the cell
3448 * @return {Array} The array of Date fields for the specified Calendar cell
3450 getDateFieldsByCellId : function(id) {
3451 id = this.getIndexFromId(id);
3452 return (id > -1) ? this.cellDates[id] : null;
3456 * Find the Calendar's cell index for a given date.
3457 * If the date is not found, the method returns -1.
3459 * The returned index can be used to lookup the cell HTMLElement
3460 * using the Calendar's cells array or passed to selectCell to select
3464 * See <a href="#cells">cells</a>, <a href="#selectCell">selectCell</a>.
3466 * @method getCellIndex
3467 * @param {Date} date JavaScript Date object, for which to find a cell index.
3468 * @return {Number} The index of the date in Calendars cellDates/cells arrays, or -1 if the date
3469 * is not on the curently rendered Calendar page.
3471 getCellIndex : function(date) {
3474 var m = date.getMonth(),
3475 y = date.getFullYear(),
3477 dates = this.cellDates;
3479 for (var i = 0; i < dates.length; ++i) {
3480 var cellDate = dates[i];
3481 if (cellDate[0] === y && cellDate[1] === m+1 && cellDate[2] === d) {
3491 * Given the id used to mark each Calendar cell, this method
3492 * extracts the index number from the id.
3494 * @param {String} strId The cell id
3495 * @return {Number} The index of the cell, or -1 if id does not contain an index number
3497 getIndexFromId : function(strId) {
3499 li = strId.lastIndexOf("_cell");
3502 idx = parseInt(strId.substring(li + 5), 10);
3508 // BEGIN BUILT-IN TABLE CELL RENDERERS
3511 * Renders a cell that falls before the minimum date or after the maximum date.
3513 * @method renderOutOfBoundsDate
3514 * @param {Date} workingDate The current working Date object being used to generate the calendar
3515 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3516 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3517 * should not be terminated
3519 renderOutOfBoundsDate : function(workingDate, cell) {
3520 Dom.addClass(cell, this.Style.CSS_CELL_OOB);
3521 cell.innerHTML = workingDate.getDate();
3522 return Calendar.STOP_RENDER;
3526 * Renders the row header for a week.
3527 * @method renderRowHeader
3528 * @param {Number} weekNum The week number of the current row
3529 * @param {Array} cell The current working HTML array
3531 renderRowHeader : function(weekNum, html) {
3532 html[html.length] = '<th class="' + this.Style.CSS_ROW_HEADER + '">' + weekNum + '</th>';
3537 * Renders the row footer for a week.
3538 * @method renderRowFooter
3539 * @param {Number} weekNum The week number of the current row
3540 * @param {Array} cell The current working HTML array
3542 renderRowFooter : function(weekNum, html) {
3543 html[html.length] = '<th class="' + this.Style.CSS_ROW_FOOTER + '">' + weekNum + '</th>';
3548 * Renders a single standard calendar cell in the calendar widget table.
3549 * All logic for determining how a standard default cell will be rendered is
3550 * encapsulated in this method, and must be accounted for when extending the
3552 * @method renderCellDefault
3553 * @param {Date} workingDate The current working Date object being used to generate the calendar
3554 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3556 renderCellDefault : function(workingDate, cell) {
3557 cell.innerHTML = '<a href="#" class="' + this.Style.CSS_CELL_SELECTOR + '">' + this.buildDayLabel(workingDate) + "</a>";
3561 * Styles a selectable cell.
3562 * @method styleCellDefault
3563 * @param {Date} workingDate The current working Date object being used to generate the calendar
3564 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3566 styleCellDefault : function(workingDate, cell) {
3567 Dom.addClass(cell, this.Style.CSS_CELL_SELECTABLE);
3572 * Renders a single standard calendar cell using the CSS hightlight1 style
3573 * @method renderCellStyleHighlight1
3574 * @param {Date} workingDate The current working Date object being used to generate the calendar
3575 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3577 renderCellStyleHighlight1 : function(workingDate, cell) {
3578 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
3582 * Renders a single standard calendar cell using the CSS hightlight2 style
3583 * @method renderCellStyleHighlight2
3584 * @param {Date} workingDate The current working Date object being used to generate the calendar
3585 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3587 renderCellStyleHighlight2 : function(workingDate, cell) {
3588 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
3592 * Renders a single standard calendar cell using the CSS hightlight3 style
3593 * @method renderCellStyleHighlight3
3594 * @param {Date} workingDate The current working Date object being used to generate the calendar
3595 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3597 renderCellStyleHighlight3 : function(workingDate, cell) {
3598 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
3602 * Renders a single standard calendar cell using the CSS hightlight4 style
3603 * @method renderCellStyleHighlight4
3604 * @param {Date} workingDate The current working Date object being used to generate the calendar
3605 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3607 renderCellStyleHighlight4 : function(workingDate, cell) {
3608 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
3612 * Applies the default style used for rendering today's date to the current calendar cell
3613 * @method renderCellStyleToday
3614 * @param {Date} workingDate The current working Date object being used to generate the calendar
3615 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3617 renderCellStyleToday : function(workingDate, cell) {
3618 Dom.addClass(cell, this.Style.CSS_CELL_TODAY);
3622 * Applies the default style used for rendering selected dates to the current calendar cell
3623 * @method renderCellStyleSelected
3624 * @param {Date} workingDate The current working Date object being used to generate the calendar
3625 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3626 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3627 * should not be terminated
3629 renderCellStyleSelected : function(workingDate, cell) {
3630 Dom.addClass(cell, this.Style.CSS_CELL_SELECTED);
3634 * Applies the default style used for rendering dates that are not a part of the current
3635 * month (preceding or trailing the cells for the current month)
3636 * @method renderCellNotThisMonth
3637 * @param {Date} workingDate The current working Date object being used to generate the calendar
3638 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3639 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3640 * should not be terminated
3642 renderCellNotThisMonth : function(workingDate, cell) {
3643 Dom.addClass(cell, this.Style.CSS_CELL_OOM);
3644 cell.innerHTML=workingDate.getDate();
3645 return Calendar.STOP_RENDER;
3649 * Renders the current calendar cell as a non-selectable "black-out" date using the default
3651 * @method renderBodyCellRestricted
3652 * @param {Date} workingDate The current working Date object being used to generate the calendar
3653 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3654 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3655 * should not be terminated
3657 renderBodyCellRestricted : function(workingDate, cell) {
3658 Dom.addClass(cell, this.Style.CSS_CELL);
3659 Dom.addClass(cell, this.Style.CSS_CELL_RESTRICTED);
3660 cell.innerHTML=workingDate.getDate();
3661 return Calendar.STOP_RENDER;
3664 // END BUILT-IN TABLE CELL RENDERERS
3666 // BEGIN MONTH NAVIGATION METHODS
3669 * Adds the designated number of months to the current calendar month, and sets the current
3670 * calendar page date to the new month.
3672 * @param {Number} count The number of months to add to the current calendar
3674 addMonths : function(count) {
3675 var cfgPageDate = DEF_CFG.PAGEDATE.key,
3677 prevDate = this.cfg.getProperty(cfgPageDate),
3678 newDate = DateMath.add(prevDate, DateMath.MONTH, count);
3680 this.cfg.setProperty(cfgPageDate, newDate);
3681 this.resetRenderers();
3682 this.changePageEvent.fire(prevDate, newDate);
3686 * Subtracts the designated number of months from the current calendar month, and sets the current
3687 * calendar page date to the new month.
3688 * @method subtractMonths
3689 * @param {Number} count The number of months to subtract from the current calendar
3691 subtractMonths : function(count) {
3692 this.addMonths(-1*count);
3696 * Adds the designated number of years to the current calendar, and sets the current
3697 * calendar page date to the new month.
3699 * @param {Number} count The number of years to add to the current calendar
3701 addYears : function(count) {
3702 var cfgPageDate = DEF_CFG.PAGEDATE.key,
3704 prevDate = this.cfg.getProperty(cfgPageDate),
3705 newDate = DateMath.add(prevDate, DateMath.YEAR, count);
3707 this.cfg.setProperty(cfgPageDate, newDate);
3708 this.resetRenderers();
3709 this.changePageEvent.fire(prevDate, newDate);
3713 * Subtcats the designated number of years from the current calendar, and sets the current
3714 * calendar page date to the new month.
3715 * @method subtractYears
3716 * @param {Number} count The number of years to subtract from the current calendar
3718 subtractYears : function(count) {
3719 this.addYears(-1*count);
3723 * Navigates to the next month page in the calendar widget.
3726 nextMonth : function() {
3731 * Navigates to the previous month page in the calendar widget.
3732 * @method previousMonth
3734 previousMonth : function() {
3739 * Navigates to the next year in the currently selected month in the calendar widget.
3742 nextYear : function() {
3747 * Navigates to the previous year in the currently selected month in the calendar widget.
3748 * @method previousYear
3750 previousYear : function() {
3754 // END MONTH NAVIGATION METHODS
3756 // BEGIN SELECTION METHODS
3759 * Resets the calendar widget to the originally selected month and year, and
3760 * sets the calendar to the initial selection(s).
3763 reset : function() {
3764 this.cfg.resetProperty(DEF_CFG.SELECTED.key);
3765 this.cfg.resetProperty(DEF_CFG.PAGEDATE.key);
3766 this.resetEvent.fire();
3770 * Clears the selected dates in the current calendar widget and sets the calendar
3771 * to the current month and year.
3774 clear : function() {
3775 this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
3776 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.today.getTime()));
3777 this.clearEvent.fire();
3781 * Selects a date or a collection of dates on the current calendar. This method, by default,
3782 * does not call the render method explicitly. Once selection has completed, render must be
3783 * called for the changes to be reflected visually.
3785 * Any dates which are OOB (out of bounds, not selectable) will not be selected and the array of
3786 * selected dates passed to the selectEvent will not contain OOB dates.
3788 * If all dates are OOB, the no state change will occur; beforeSelect and select events will not be fired.
3791 * @param {String/Date/Date[]} date The date string of dates to select in the current calendar. Valid formats are
3792 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3793 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3794 * This method can also take a JavaScript Date object or an array of Date objects.
3795 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3797 select : function(date) {
3798 this.logger.log("Select: " + date, "info");
3800 var aToBeSelected = this._toFieldArray(date),
3803 cfgSelected = DEF_CFG.SELECTED.key;
3805 this.logger.log("Selection field array: " + aToBeSelected, "info");
3807 for (var a=0; a < aToBeSelected.length; ++a) {
3808 var toSelect = aToBeSelected[a];
3810 if (!this.isDateOOB(this._toDate(toSelect))) {
3812 if (validDates.length === 0) {
3813 this.beforeSelectEvent.fire();
3814 selected = this.cfg.getProperty(cfgSelected);
3816 validDates.push(toSelect);
3818 if (this._indexOfSelectedFieldArray(toSelect) == -1) {
3819 selected[selected.length] = toSelect;
3824 if (validDates.length === 0) { this.logger.log("All provided dates were OOB. beforeSelect and select events not fired", "info"); }
3826 if (validDates.length > 0) {
3828 this.parent.cfg.setProperty(cfgSelected, selected);
3830 this.cfg.setProperty(cfgSelected, selected);
3832 this.selectEvent.fire(validDates);
3835 return this.getSelectedDates();
3839 * Selects a date on the current calendar by referencing the index of the cell that should be selected.
3840 * This method is used to easily select a single cell (usually with a mouse click) without having to do
3841 * a full render. The selected style is applied to the cell directly.
3843 * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month
3844 * or out of bounds cells), it will not be selected and in such a case beforeSelect and select events will not be fired.
3846 * @method selectCell
3847 * @param {Number} cellIndex The index of the cell to select in the current calendar.
3848 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3850 selectCell : function(cellIndex) {
3852 var cell = this.cells[cellIndex],
3853 cellDate = this.cellDates[cellIndex],
3854 dCellDate = this._toDate(cellDate),
3855 selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3857 this.logger.log("Select: " + dCellDate, "info");
3858 if (!selectable) {this.logger.log("The cell at cellIndex:" + cellIndex + " is not a selectable cell. beforeSelect, select events not fired", "info"); }
3862 this.beforeSelectEvent.fire();
3864 var cfgSelected = DEF_CFG.SELECTED.key;
3865 var selected = this.cfg.getProperty(cfgSelected);
3867 var selectDate = cellDate.concat();
3869 if (this._indexOfSelectedFieldArray(selectDate) == -1) {
3870 selected[selected.length] = selectDate;
3873 this.parent.cfg.setProperty(cfgSelected, selected);
3875 this.cfg.setProperty(cfgSelected, selected);
3877 this.renderCellStyleSelected(dCellDate,cell);
3878 this.selectEvent.fire([selectDate]);
3880 this.doCellMouseOut.call(cell, null, this);
3883 return this.getSelectedDates();
3887 * Deselects a date or a collection of dates on the current calendar. This method, by default,
3888 * does not call the render method explicitly. Once deselection has completed, render must be
3889 * called for the changes to be reflected visually.
3891 * The method will not attempt to deselect any dates which are OOB (out of bounds, and hence not selectable)
3892 * and the array of deselected dates passed to the deselectEvent will not contain any OOB dates.
3894 * If all dates are OOB, beforeDeselect and deselect events will not be fired.
3897 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
3898 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3899 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3900 * This method can also take a JavaScript Date object or an array of Date objects.
3901 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3903 deselect : function(date) {
3904 this.logger.log("Deselect: " + date, "info");
3906 var aToBeDeselected = this._toFieldArray(date),
3909 cfgSelected = DEF_CFG.SELECTED.key;
3911 this.logger.log("Deselection field array: " + aToBeDeselected, "info");
3913 for (var a=0; a < aToBeDeselected.length; ++a) {
3914 var toDeselect = aToBeDeselected[a];
3916 if (!this.isDateOOB(this._toDate(toDeselect))) {
3918 if (validDates.length === 0) {
3919 this.beforeDeselectEvent.fire();
3920 selected = this.cfg.getProperty(cfgSelected);
3923 validDates.push(toDeselect);
3925 var index = this._indexOfSelectedFieldArray(toDeselect);
3927 selected.splice(index,1);
3932 if (validDates.length === 0) { this.logger.log("All provided dates were OOB. beforeDeselect and deselect events not fired");}
3934 if (validDates.length > 0) {
3936 this.parent.cfg.setProperty(cfgSelected, selected);
3938 this.cfg.setProperty(cfgSelected, selected);
3940 this.deselectEvent.fire(validDates);
3943 return this.getSelectedDates();
3947 * Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
3948 * This method is used to easily deselect a single cell (usually with a mouse click) without having to do
3949 * a full render. The selected style is removed from the cell directly.
3951 * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month
3952 * or out of bounds cells), the method will not attempt to deselect it and in such a case, beforeDeselect and
3953 * deselect events will not be fired.
3955 * @method deselectCell
3956 * @param {Number} cellIndex The index of the cell to deselect in the current calendar.
3957 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3959 deselectCell : function(cellIndex) {
3960 var cell = this.cells[cellIndex],
3961 cellDate = this.cellDates[cellIndex],
3962 cellDateIndex = this._indexOfSelectedFieldArray(cellDate);
3964 var selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3965 if (!selectable) { this.logger.log("The cell at cellIndex:" + cellIndex + " is not a selectable/deselectable cell", "info"); }
3969 this.beforeDeselectEvent.fire();
3971 var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key),
3972 dCellDate = this._toDate(cellDate),
3973 selectDate = cellDate.concat();
3975 if (cellDateIndex > -1) {
3976 if (this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth() == dCellDate.getMonth() &&
3977 this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getFullYear() == dCellDate.getFullYear()) {
3978 Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
3980 selected.splice(cellDateIndex, 1);
3984 this.parent.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
3986 this.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
3989 this.deselectEvent.fire([selectDate]);
3992 return this.getSelectedDates();
3996 * Deselects all dates on the current calendar.
3997 * @method deselectAll
3998 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3999 * Assuming that this function executes properly, the return value should be an empty array.
4000 * However, the empty array is returned for the sake of being able to check the selection status
4003 deselectAll : function() {
4004 this.beforeDeselectEvent.fire();
4006 var cfgSelected = DEF_CFG.SELECTED.key,
4007 selected = this.cfg.getProperty(cfgSelected),
4008 count = selected.length,
4009 sel = selected.concat();
4012 this.parent.cfg.setProperty(cfgSelected, []);
4014 this.cfg.setProperty(cfgSelected, []);
4018 this.deselectEvent.fire(sel);
4021 return this.getSelectedDates();
4024 // END SELECTION METHODS
4026 // BEGIN TYPE CONVERSION METHODS
4029 * Converts a date (either a JavaScript Date object, or a date string) to the internal data structure
4030 * used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]].
4031 * @method _toFieldArray
4033 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
4034 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
4035 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
4036 * This method can also take a JavaScript Date object or an array of Date objects.
4037 * @return {Array[](Number[])} Array of date field arrays
4039 _toFieldArray : function(date) {
4040 var returnDate = [];
4042 if (date instanceof Date) {
4043 returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]];
4044 } else if (Lang.isString(date)) {
4045 returnDate = this._parseDates(date);
4046 } else if (Lang.isArray(date)) {
4047 for (var i=0;i<date.length;++i) {
4049 returnDate[returnDate.length] = [d.getFullYear(),d.getMonth()+1,d.getDate()];
4057 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object. The date field array
4058 * is the format in which dates are as provided as arguments to selectEvent and deselectEvent listeners.
4061 * @param {Number[]} dateFieldArray The date field array to convert to a JavaScript Date.
4062 * @return {Date} JavaScript Date object representing the date field array.
4064 toDate : function(dateFieldArray) {
4065 return this._toDate(dateFieldArray);
4069 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
4072 * @deprecated Made public, toDate
4073 * @param {Number[]} dateFieldArray The date field array to convert to a JavaScript Date.
4074 * @return {Date} JavaScript Date object representing the date field array
4076 _toDate : function(dateFieldArray) {
4077 if (dateFieldArray instanceof Date) {
4078 return dateFieldArray;
4080 return DateMath.getDate(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]);
4084 // END TYPE CONVERSION METHODS
4086 // BEGIN UTILITY METHODS
4089 * Determines if 2 field arrays are equal.
4090 * @method _fieldArraysAreEqual
4092 * @param {Number[]} array1 The first date field array to compare
4093 * @param {Number[]} array2 The first date field array to compare
4094 * @return {Boolean} The boolean that represents the equality of the two arrays
4096 _fieldArraysAreEqual : function(array1, array2) {
4099 if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2]) {
4107 * Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates.
4108 * @method _indexOfSelectedFieldArray
4110 * @param {Number[]} find The date field array to search for
4111 * @return {Number} The index of the date field array within the collection of selected dates.
4112 * -1 will be returned if the date is not found.
4114 _indexOfSelectedFieldArray : function(find) {
4116 seldates = this.cfg.getProperty(DEF_CFG.SELECTED.key);
4118 for (var s=0;s<seldates.length;++s) {
4119 var sArray = seldates[s];
4120 if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2]) {
4130 * Determines whether a given date is OOM (out of month).
4132 * @param {Date} date The JavaScript Date object for which to check the OOM status
4133 * @return {Boolean} true if the date is OOM
4135 isDateOOM : function(date) {
4136 return (date.getMonth() != this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth());
4140 * Determines whether a given date is OOB (out of bounds - less than the mindate or more than the maxdate).
4143 * @param {Date} date The JavaScript Date object for which to check the OOB status
4144 * @return {Boolean} true if the date is OOB
4146 isDateOOB : function(date) {
4147 var minDate = this.cfg.getProperty(DEF_CFG.MINDATE.key),
4148 maxDate = this.cfg.getProperty(DEF_CFG.MAXDATE.key),
4152 minDate = dm.clearTime(minDate);
4155 maxDate = dm.clearTime(maxDate);
4158 var clearedDate = new Date(date.getTime());
4159 clearedDate = dm.clearTime(clearedDate);
4161 return ((minDate && clearedDate.getTime() < minDate.getTime()) || (maxDate && clearedDate.getTime() > maxDate.getTime()));
4165 * Parses a pagedate configuration property value. The value can either be specified as a string of form "mm/yyyy" or a Date object
4166 * and is parsed into a Date object normalized to the first day of the month. If no value is passed in, the month and year from today's date are used to create the Date object
4167 * @method _parsePageDate
4169 * @param {Date|String} date Pagedate value which needs to be parsed
4170 * @return {Date} The Date object representing the pagedate
4172 _parsePageDate : function(date) {
4176 if (date instanceof Date) {
4177 parsedDate = DateMath.findMonthStart(date);
4179 var month, year, aMonthYear;
4180 aMonthYear = date.split(this.cfg.getProperty(DEF_CFG.DATE_FIELD_DELIMITER.key));
4181 month = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_MONTH_POSITION.key)-1], 10)-1;
4182 year = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_YEAR_POSITION.key)-1], 10) - this.Locale.YEAR_OFFSET;
4184 parsedDate = DateMath.getDate(year, month, 1);
4187 parsedDate = DateMath.getDate(this.today.getFullYear(), this.today.getMonth(), 1);
4192 // END UTILITY METHODS
4194 // BEGIN EVENT HANDLERS
4197 * Event executed before a date is selected in the calendar widget.
4198 * @deprecated Event handlers for this event should be susbcribed to beforeSelectEvent.
4200 onBeforeSelect : function() {
4201 if (this.cfg.getProperty(DEF_CFG.MULTI_SELECT.key) === false) {
4203 this.parent.callChildFunction("clearAllBodyCellStyles", this.Style.CSS_CELL_SELECTED);
4204 this.parent.deselectAll();
4206 this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED);
4213 * Event executed when a date is selected in the calendar widget.
4214 * @param {Array} selected An array of date field arrays representing which date or dates were selected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ]
4215 * @deprecated Event handlers for this event should be susbcribed to selectEvent.
4217 onSelect : function(selected) { },
4220 * Event executed before a date is deselected in the calendar widget.
4221 * @deprecated Event handlers for this event should be susbcribed to beforeDeselectEvent.
4223 onBeforeDeselect : function() { },
4226 * Event executed when a date is deselected in the calendar widget.
4227 * @param {Array} selected An array of date field arrays representing which date or dates were deselected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ]
4228 * @deprecated Event handlers for this event should be susbcribed to deselectEvent.
4230 onDeselect : function(deselected) { },
4233 * Event executed when the user navigates to a different calendar page.
4234 * @deprecated Event handlers for this event should be susbcribed to changePageEvent.
4236 onChangePage : function() {
4241 * Event executed when the calendar widget is rendered.
4242 * @deprecated Event handlers for this event should be susbcribed to renderEvent.
4244 onRender : function() { },
4247 * Event executed when the calendar widget is reset to its original state.
4248 * @deprecated Event handlers for this event should be susbcribed to resetEvemt.
4250 onReset : function() { this.render(); },
4253 * Event executed when the calendar widget is completely cleared to the current month with no selections.
4254 * @deprecated Event handlers for this event should be susbcribed to clearEvent.
4256 onClear : function() { this.render(); },
4259 * Validates the calendar widget. This method has no default implementation
4260 * and must be extended by subclassing the widget.
4261 * @return Should return true if the widget validates, and false if
4265 validate : function() { return true; },
4267 // END EVENT HANDLERS
4269 // BEGIN DATE PARSE METHODS
4272 * Converts a date string to a date field array
4274 * @param {String} sDate Date string. Valid formats are mm/dd and mm/dd/yyyy.
4275 * @return A date field array representing the string passed to the method
4276 * @type Array[](Number[])
4278 _parseDate : function(sDate) {
4279 var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER),
4282 if (aDate.length == 2) {
4283 rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
4284 rArray.type = Calendar.MONTH_DAY;
4286 rArray = [aDate[this.Locale.MDY_YEAR_POSITION-1] - this.Locale.YEAR_OFFSET, aDate[this.Locale.MDY_MONTH_POSITION-1],aDate[this.Locale.MDY_DAY_POSITION-1]];
4287 rArray.type = Calendar.DATE;
4290 for (var i=0;i<rArray.length;i++) {
4291 rArray[i] = parseInt(rArray[i], 10);
4298 * Converts a multi or single-date string to an array of date field arrays
4300 * @param {String} sDates Date string with one or more comma-delimited dates. Valid formats are mm/dd, mm/dd/yyyy, mm/dd/yyyy-mm/dd/yyyy
4301 * @return An array of date field arrays
4302 * @type Array[](Number[])
4304 _parseDates : function(sDates) {
4306 aDates = sDates.split(this.Locale.DATE_DELIMITER);
4308 for (var d=0;d<aDates.length;++d) {
4309 var sDate = aDates[d];
4311 if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) {
4313 var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER),
4314 dateStart = this._parseDate(aRange[0]),
4315 dateEnd = this._parseDate(aRange[1]),
4316 fullRange = this._parseRange(dateStart, dateEnd);
4318 aReturn = aReturn.concat(fullRange);
4320 // This is not a range
4321 var aDate = this._parseDate(sDate);
4322 aReturn.push(aDate);
4329 * Converts a date range to the full list of included dates
4331 * @param {Number[]} startDate Date field array representing the first date in the range
4332 * @param {Number[]} endDate Date field array representing the last date in the range
4333 * @return An array of date field arrays
4334 * @type Array[](Number[])
4336 _parseRange : function(startDate, endDate) {
4337 var dCurrent = DateMath.add(DateMath.getDate(startDate[0],startDate[1]-1,startDate[2]),DateMath.DAY,1),
4338 dEnd = DateMath.getDate(endDate[0], endDate[1]-1, endDate[2]),
4341 results.push(startDate);
4342 while (dCurrent.getTime() <= dEnd.getTime()) {
4343 results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
4344 dCurrent = DateMath.add(dCurrent,DateMath.DAY,1);
4349 // END DATE PARSE METHODS
4351 // BEGIN RENDERER METHODS
4354 * Resets the render stack of the current calendar to its original pre-render value.
4356 resetRenderers : function() {
4357 this.renderStack = this._renderStack.concat();
4361 * Removes all custom renderers added to the Calendar through the addRenderer, addMonthRenderer and
4362 * addWeekdayRenderer methods. Calendar's render method needs to be called after removing renderers
4363 * to re-render the Calendar without custom renderers applied.
4365 removeRenderers : function() {
4366 this._renderStack = [];
4367 this.renderStack = [];
4371 * Clears the inner HTML, CSS class and style information from the specified cell.
4372 * @method clearElement
4373 * @param {HTMLTableCellElement} cell The cell to clear
4375 clearElement : function(cell) {
4376 cell.innerHTML = " ";
4381 * Adds a renderer to the render stack. The function reference passed to this method will be executed
4382 * when a date cell matches the conditions specified in the date string for this renderer.
4383 * @method addRenderer
4384 * @param {String} sDates A date string to associate with the specified renderer. Valid formats
4385 * include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
4386 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4388 addRenderer : function(sDates, fnRender) {
4389 var aDates = this._parseDates(sDates);
4390 for (var i=0;i<aDates.length;++i) {
4391 var aDate = aDates[i];
4393 if (aDate.length == 2) { // this is either a range or a month/day combo
4394 if (aDate[0] instanceof Array) { // this is a range
4395 this._addRenderer(Calendar.RANGE,aDate,fnRender);
4396 } else { // this is a month/day combo
4397 this._addRenderer(Calendar.MONTH_DAY,aDate,fnRender);
4399 } else if (aDate.length == 3) {
4400 this._addRenderer(Calendar.DATE,aDate,fnRender);
4406 * The private method used for adding cell renderers to the local render stack.
4407 * This method is called by other methods that set the renderer type prior to the method call.
4408 * @method _addRenderer
4410 * @param {String} type The type string that indicates the type of date renderer being added.
4411 * Values are YAHOO.widget.Calendar.DATE, YAHOO.widget.Calendar.MONTH_DAY, YAHOO.widget.Calendar.WEEKDAY,
4412 * YAHOO.widget.Calendar.RANGE, YAHOO.widget.Calendar.MONTH
4413 * @param {Array} aDates An array of dates used to construct the renderer. The format varies based
4414 * on the renderer type
4415 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4417 _addRenderer : function(type, aDates, fnRender) {
4418 var add = [type,aDates,fnRender];
4419 this.renderStack.unshift(add);
4420 this._renderStack = this.renderStack.concat();
4424 * Adds a month to the render stack. The function reference passed to this method will be executed
4425 * when a date cell matches the month passed to this method.
4426 * @method addMonthRenderer
4427 * @param {Number} month The month (1-12) to associate with this renderer
4428 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4430 addMonthRenderer : function(month, fnRender) {
4431 this._addRenderer(Calendar.MONTH,[month],fnRender);
4435 * Adds a weekday to the render stack. The function reference passed to this method will be executed
4436 * when a date cell matches the weekday passed to this method.
4437 * @method addWeekdayRenderer
4438 * @param {Number} weekday The weekday (Sunday = 1, Monday = 2 ... Saturday = 7) to associate with this renderer
4439 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4441 addWeekdayRenderer : function(weekday, fnRender) {
4442 this._addRenderer(Calendar.WEEKDAY,[weekday],fnRender);
4445 // END RENDERER METHODS
4447 // BEGIN CSS METHODS
4450 * Removes all styles from all body cells in the current calendar table.
4451 * @method clearAllBodyCellStyles
4452 * @param {style} style The CSS class name to remove from all calendar body cells
4454 clearAllBodyCellStyles : function(style) {
4455 for (var c=0;c<this.cells.length;++c) {
4456 Dom.removeClass(this.cells[c],style);
4462 // BEGIN GETTER/SETTER METHODS
4464 * Sets the calendar's month explicitly
4466 * @param {Number} month The numeric month, from 0 (January) to 11 (December)
4468 setMonth : function(month) {
4469 var cfgPageDate = DEF_CFG.PAGEDATE.key,
4470 current = this.cfg.getProperty(cfgPageDate);
4471 current.setMonth(parseInt(month, 10));
4472 this.cfg.setProperty(cfgPageDate, current);
4476 * Sets the calendar's year explicitly.
4478 * @param {Number} year The numeric 4-digit year
4480 setYear : function(year) {
4481 var cfgPageDate = DEF_CFG.PAGEDATE.key,
4482 current = this.cfg.getProperty(cfgPageDate);
4484 current.setFullYear(parseInt(year, 10) - this.Locale.YEAR_OFFSET);
4485 this.cfg.setProperty(cfgPageDate, current);
4489 * Gets the list of currently selected dates from the calendar.
4490 * @method getSelectedDates
4491 * @return {Date[]} An array of currently selected JavaScript Date objects.
4493 getSelectedDates : function() {
4494 var returnDates = [],
4495 selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
4497 for (var d=0;d<selected.length;++d) {
4498 var dateArray = selected[d];
4500 var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
4501 returnDates.push(date);
4504 returnDates.sort( function(a,b) { return a-b; } );
4508 /// END GETTER/SETTER METHODS ///
4511 * Hides the Calendar's outer container from view.
4515 if (this.beforeHideEvent.fire()) {
4516 this.oDomContainer.style.display = "none";
4517 this.hideEvent.fire();
4522 * Shows the Calendar's outer container.
4526 if (this.beforeShowEvent.fire()) {
4527 this.oDomContainer.style.display = "block";
4528 this.showEvent.fire();
4533 * Returns a string representing the current browser.
4534 * @deprecated As of 2.3.0, environment information is available in YAHOO.env.ua
4539 browser : (function() {
4540 var ua = navigator.userAgent.toLowerCase();
4541 if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof)
4543 } else if (ua.indexOf('msie 7')!=-1) { // IE7
4545 } else if (ua.indexOf('msie') !=-1) { // IE
4547 } else if (ua.indexOf('safari')!=-1) { // Safari (check before Gecko because it includes "like Gecko")
4549 } else if (ua.indexOf('gecko') != -1) { // Gecko
4556 * Returns a string representation of the object.
4558 * @return {String} A string representation of the Calendar object.
4560 toString : function() {
4561 return "Calendar " + this.id;
4565 * Destroys the Calendar instance. The method will remove references
4566 * to HTML elements, remove any event listeners added by the Calendar,
4567 * and destroy the Config and CalendarNavigator instances it has created.
4571 destroy : function() {
4573 if (this.beforeDestroyEvent.fire()) {
4577 if (cal.navigator) {
4578 cal.navigator.destroy();
4585 // DOM event listeners
4586 Event.purgeElement(cal.oDomContainer, true);
4588 // Generated markup/DOM - Not removing the container DIV since we didn't create it.
4589 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_WITH_TITLE);
4590 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_CONTAINER);
4591 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_SINGLE);
4592 cal.oDomContainer.innerHTML = "";
4594 // JS-to-DOM references
4595 cal.oDomContainer = null;
4598 this.destroyEvent.fire();
4603 YAHOO.widget.Calendar = Calendar;
4606 * @namespace YAHOO.widget
4607 * @class Calendar_Core
4608 * @extends YAHOO.widget.Calendar
4609 * @deprecated The old Calendar_Core class is no longer necessary.
4611 YAHOO.widget.Calendar_Core = YAHOO.widget.Calendar;
4613 YAHOO.widget.Cal_Core = YAHOO.widget.Calendar;
4618 var Dom = YAHOO.util.Dom,
4619 DateMath = YAHOO.widget.DateMath,
4620 Event = YAHOO.util.Event,
4622 Calendar = YAHOO.widget.Calendar;
4625 * YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar. This class facilitates
4626 * the ability to have multi-page calendar views that share a single dataset and are
4627 * dependent on each other.
4629 * The calendar group instance will refer to each of its elements using a 0-based index.
4630 * For example, to construct the placeholder for a calendar group widget with id "cal1" and
4631 * containerId of "cal1Container", the markup would be as follows:
4633 * <div id="cal1Container_0"></div>
4634 * <div id="cal1Container_1"></div>
4636 * The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers.
4639 * <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
4640 * The CalendarGroup can be constructed by simply providing a container ID string,
4641 * or a reference to a container DIV HTMLElement (the element needs to exist
4646 * var c = new YAHOO.widget.CalendarGroup("calContainer", configOptions);
4650 * var containerDiv = YAHOO.util.Dom.get("calContainer");
4651 * var c = new YAHOO.widget.CalendarGroup(containerDiv, configOptions);
4655 * If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
4656 * 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".
4659 * @namespace YAHOO.widget
4660 * @class CalendarGroup
4662 * @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.
4663 * @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.
4664 * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
4666 function CalendarGroup(id, containerId, config) {
4667 if (arguments.length > 0) {
4668 this.init.apply(this, arguments);
4673 * The set of default Config property keys and values for the CalendarGroup.
4676 * NOTE: This property is made public in order to allow users to change
4677 * the default values of configuration properties. Users should not
4678 * modify the key string, unless they are overriding the Calendar implementation
4681 * @property YAHOO.widget.CalendarGroup.DEFAULT_CONFIG
4683 * @type Object An object with key/value pairs, the key being the
4684 * uppercase configuration property name and the value being an objec
4685 * literal with a key string property, and a value property, specifying the
4686 * default value of the property
4690 * The set of default Config property keys and values for the CalendarGroup
4691 * @property YAHOO.widget.CalendarGroup._DEFAULT_CONFIG
4692 * @deprecated Made public. See the public DEFAULT_CONFIG property for details
4697 CalendarGroup.DEFAULT_CONFIG = CalendarGroup._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG;
4698 CalendarGroup.DEFAULT_CONFIG.PAGES = {key:"pages", value:2};
4700 var DEF_CFG = CalendarGroup.DEFAULT_CONFIG;
4702 CalendarGroup.prototype = {
4705 * Initializes the calendar group. All subclasses must call this method in order for the
4706 * group to be initialized properly.
4708 * @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.
4709 * @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.
4710 * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
4712 init : function(id, container, config) {
4714 // Normalize 2.4.0, pre 2.4.0 args
4715 var nArgs = this._parseArgs(arguments);
4718 container = nArgs.container;
4719 config = nArgs.config;
4721 this.oDomContainer = Dom.get(container);
4722 if (!this.oDomContainer) { this.logger.log("Container not found in document.", "error"); }
4724 if (!this.oDomContainer.id) {
4725 this.oDomContainer.id = Dom.generateId();
4728 id = this.oDomContainer.id + "_t";
4732 * The unique id associated with the CalendarGroup
4739 * The unique id associated with the CalendarGroup container
4740 * @property containerId
4743 this.containerId = this.oDomContainer.id;
4745 this.logger = new YAHOO.widget.LogWriter("CalendarGroup " + this.id);
4750 * The collection of Calendar pages contained within the CalendarGroup
4752 * @type YAHOO.widget.Calendar[]
4756 Dom.addClass(this.oDomContainer, CalendarGroup.CSS_CONTAINER);
4757 Dom.addClass(this.oDomContainer, CalendarGroup.CSS_MULTI_UP);
4760 * The Config object used to hold the configuration variables for the CalendarGroup
4762 * @type YAHOO.util.Config
4764 this.cfg = new YAHOO.util.Config(this);
4767 * The local object which contains the CalendarGroup's options
4774 * The local object which contains the CalendarGroup's locale settings
4783 this.cfg.applyConfig(config, true);
4786 this.cfg.fireQueue();
4788 // OPERA HACK FOR MISWRAPPED FLOATS
4789 if (YAHOO.env.ua.opera){
4790 this.renderEvent.subscribe(this._fixWidth, this, true);
4791 this.showEvent.subscribe(this._fixWidth, this, true);
4794 this.logger.log("Initialized " + this.pages.length + "-page CalendarGroup", "info");
4797 setupConfig : function() {
4802 * The number of pages to include in the CalendarGroup. This value can only be set once, in the CalendarGroup's constructor arguments.
4807 cfg.addProperty(DEF_CFG.PAGES.key, { value:DEF_CFG.PAGES.value, validator:cfg.checkNumber, handler:this.configPages } );
4810 * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to
4811 * be used when displaying or parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by
4812 * methods will always represent the Gregorian year, in order to maintain date/month/week values.
4814 * @config year_offset
4818 cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, handler: this.delegateConfig, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, suppressEvent:true } );
4821 * The date to use to represent "Today".
4825 * @default Today's date
4827 cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler: this.configToday, suppressEvent:false } );
4830 * The month/year representing the current visible Calendar date (mm/yyyy)
4832 * @type String | Date
4833 * @default Today's date
4835 cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } );
4838 * The date or range of dates representing the current Calendar selection
4844 cfg.addProperty(DEF_CFG.SELECTED.key, { value:[], handler:this.configSelected } );
4847 * The title to display above the CalendarGroup's month header
4852 cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
4855 * Whether or not a close button should be displayed for this CalendarGroup
4860 cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
4863 * 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.
4864 * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be
4865 * enabled if required.
4869 * @default true for IE6 and below, false for all other browsers
4871 cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
4874 * The minimum selectable date in the current Calendar (mm/dd/yyyy)
4876 * @type String | Date
4879 cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.delegateConfig } );
4882 * The maximum selectable date in the current Calendar (mm/dd/yyyy)
4884 * @type String | Date
4887 cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.delegateConfig } );
4889 // Options properties
4892 * True if the Calendar should allow multiple selections. False by default.
4893 * @config MULTI_SELECT
4897 cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4900 * The weekday the week begins on. Default is 0 (Sunday).
4901 * @config START_WEEKDAY
4905 cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4908 * True if the Calendar should show weekday labels. True by default.
4909 * @config SHOW_WEEKDAYS
4913 cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4916 * True if the Calendar should show week row headers. False by default.
4917 * @config SHOW_WEEK_HEADER
4921 cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key,{ value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4924 * True if the Calendar should show week row footers. False by default.
4925 * @config SHOW_WEEK_FOOTER
4929 cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4932 * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
4933 * @config HIDE_BLANK_WEEKS
4937 cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key,{ value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4940 * The image that should be used for the left navigation arrow.
4941 * @config NAV_ARROW_LEFT
4943 * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
4946 cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.delegateConfig } );
4949 * The image that should be used for the right navigation arrow.
4950 * @config NAV_ARROW_RIGHT
4952 * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
4955 cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.delegateConfig } );
4957 // Locale properties
4960 * The short month labels for the current locale.
4961 * @config MONTHS_SHORT
4963 * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
4965 cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.delegateConfig } );
4968 * The long month labels for the current locale.
4969 * @config MONTHS_LONG
4971 * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
4973 cfg.addProperty(DEF_CFG.MONTHS_LONG.key, { value:DEF_CFG.MONTHS_LONG.value, handler:this.delegateConfig } );
4976 * The 1-character weekday labels for the current locale.
4977 * @config WEEKDAYS_1CHAR
4979 * @default ["S", "M", "T", "W", "T", "F", "S"]
4981 cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.delegateConfig } );
4984 * The short weekday labels for the current locale.
4985 * @config WEEKDAYS_SHORT
4987 * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
4989 cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.delegateConfig } );
4992 * The medium weekday labels for the current locale.
4993 * @config WEEKDAYS_MEDIUM
4995 * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
4997 cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.delegateConfig } );
5000 * The long weekday labels for the current locale.
5001 * @config WEEKDAYS_LONG
5003 * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
5005 cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.delegateConfig } );
5008 * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
5009 * @config LOCALE_MONTHS
5013 cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.delegateConfig } );
5016 * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
5017 * @config LOCALE_WEEKDAYS
5021 cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.delegateConfig } );
5024 * The value used to delimit individual dates in a date string passed to various Calendar functions.
5025 * @config DATE_DELIMITER
5029 cfg.addProperty(DEF_CFG.DATE_DELIMITER.key, { value:DEF_CFG.DATE_DELIMITER.value, handler:this.delegateConfig } );
5032 * The value used to delimit date fields in a date string passed to various Calendar functions.
5033 * @config DATE_FIELD_DELIMITER
5037 cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key,{ value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.delegateConfig } );
5040 * The value used to delimit date ranges in a date string passed to various Calendar functions.
5041 * @config DATE_RANGE_DELIMITER
5045 cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key,{ value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.delegateConfig } );
5048 * The position of the month in a month/year date string
5049 * @config MY_MONTH_POSITION
5053 cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5056 * The position of the year in a month/year date string
5057 * @config MY_YEAR_POSITION
5061 cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5064 * The position of the month in a month/day date string
5065 * @config MD_MONTH_POSITION
5069 cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5072 * The position of the day in a month/year date string
5073 * @config MD_DAY_POSITION
5077 cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key, { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5080 * The position of the month in a month/day/year date string
5081 * @config MDY_MONTH_POSITION
5085 cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5088 * The position of the day in a month/day/year date string
5089 * @config MDY_DAY_POSITION
5093 cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5096 * The position of the year in a month/day/year date string
5097 * @config MDY_YEAR_POSITION
5101 cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5104 * The position of the month in the month year label string used as the Calendar header
5105 * @config MY_LABEL_MONTH_POSITION
5109 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5112 * The position of the year in the month year label string used as the Calendar header
5113 * @config MY_LABEL_YEAR_POSITION
5117 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5120 * The suffix used after the month when rendering the Calendar header
5121 * @config MY_LABEL_MONTH_SUFFIX
5125 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.delegateConfig } );
5128 * The suffix used after the year when rendering the Calendar header
5129 * @config MY_LABEL_YEAR_SUFFIX
5133 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.delegateConfig } );
5136 * Configuration for the Month Year Navigation UI. By default it is disabled
5141 cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
5144 * The map of UI strings which the CalendarGroup UI uses.
5148 * @default An object with the properties shown below:
5150 * <dt>previousMonth</dt><dd><em>String</em> : The string to use for the "Previous Month" navigation UI. Defaults to "Previous Month".</dd>
5151 * <dt>nextMonth</dt><dd><em>String</em> : The string to use for the "Next Month" navigation UI. Defaults to "Next Month".</dd>
5152 * <dt>close</dt><dd><em>String</em> : The string to use for the close button label. Defaults to "Close".</dd>
5155 cfg.addProperty(DEF_CFG.STRINGS.key, {
5156 value:DEF_CFG.STRINGS.value,
5157 handler:this.configStrings,
5158 validator: function(val) {
5159 return Lang.isObject(val);
5161 supercedes: DEF_CFG.STRINGS.supercedes
5166 * Initializes CalendarGroup's built-in CustomEvents
5167 * @method initEvents
5169 initEvents : function() {
5173 CE = YAHOO.util.CustomEvent;
5176 * Proxy subscriber to subscribe to the CalendarGroup's child Calendars' CustomEvents
5179 * @param {Function} fn The function to subscribe to this CustomEvent
5180 * @param {Object} obj The CustomEvent's scope object
5181 * @param {Boolean} bOverride Whether or not to apply scope correction
5183 var sub = function(fn, obj, bOverride) {
5184 for (var p=0;p<me.pages.length;++p) {
5185 var cal = me.pages[p];
5186 cal[this.type + strEvent].subscribe(fn, obj, bOverride);
5191 * Proxy unsubscriber to unsubscribe from the CalendarGroup's child Calendars' CustomEvents
5194 * @param {Function} fn The function to subscribe to this CustomEvent
5195 * @param {Object} obj The CustomEvent's scope object
5197 var unsub = function(fn, obj) {
5198 for (var p=0;p<me.pages.length;++p) {
5199 var cal = me.pages[p];
5200 cal[this.type + strEvent].unsubscribe(fn, obj);
5204 var defEvents = Calendar._EVENT_TYPES;
5207 * Fired before a date selection is made
5208 * @event beforeSelectEvent
5210 me.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
5211 me.beforeSelectEvent.subscribe = sub; me.beforeSelectEvent.unsubscribe = unsub;
5214 * Fired when a date selection is made
5215 * @event selectEvent
5216 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
5218 me.selectEvent = new CE(defEvents.SELECT);
5219 me.selectEvent.subscribe = sub; me.selectEvent.unsubscribe = unsub;
5222 * Fired before a date or set of dates is deselected
5223 * @event beforeDeselectEvent
5225 me.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
5226 me.beforeDeselectEvent.subscribe = sub; me.beforeDeselectEvent.unsubscribe = unsub;
5229 * Fired when a date or set of dates has been deselected
5230 * @event deselectEvent
5231 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
5233 me.deselectEvent = new CE(defEvents.DESELECT);
5234 me.deselectEvent.subscribe = sub; me.deselectEvent.unsubscribe = unsub;
5237 * Fired when the Calendar page is changed
5238 * @event changePageEvent
5240 me.changePageEvent = new CE(defEvents.CHANGE_PAGE);
5241 me.changePageEvent.subscribe = sub; me.changePageEvent.unsubscribe = unsub;
5244 * Fired before the Calendar is rendered
5245 * @event beforeRenderEvent
5247 me.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
5248 me.beforeRenderEvent.subscribe = sub; me.beforeRenderEvent.unsubscribe = unsub;
5251 * Fired when the Calendar is rendered
5252 * @event renderEvent
5254 me.renderEvent = new CE(defEvents.RENDER);
5255 me.renderEvent.subscribe = sub; me.renderEvent.unsubscribe = unsub;
5258 * Fired when the Calendar is reset
5261 me.resetEvent = new CE(defEvents.RESET);
5262 me.resetEvent.subscribe = sub; me.resetEvent.unsubscribe = unsub;
5265 * Fired when the Calendar is cleared
5268 me.clearEvent = new CE(defEvents.CLEAR);
5269 me.clearEvent.subscribe = sub; me.clearEvent.unsubscribe = unsub;
5272 * Fired just before the CalendarGroup is to be shown
5273 * @event beforeShowEvent
5275 me.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
5278 * Fired after the CalendarGroup is shown
5281 me.showEvent = new CE(defEvents.SHOW);
5284 * Fired just before the CalendarGroup is to be hidden
5285 * @event beforeHideEvent
5287 me.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
5290 * Fired after the CalendarGroup is hidden
5293 me.hideEvent = new CE(defEvents.HIDE);
5296 * Fired just before the CalendarNavigator is to be shown
5297 * @event beforeShowNavEvent
5299 me.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
5302 * Fired after the CalendarNavigator is shown
5303 * @event showNavEvent
5305 me.showNavEvent = new CE(defEvents.SHOW_NAV);
5308 * Fired just before the CalendarNavigator is to be hidden
5309 * @event beforeHideNavEvent
5311 me.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
5314 * Fired after the CalendarNavigator is hidden
5315 * @event hideNavEvent
5317 me.hideNavEvent = new CE(defEvents.HIDE_NAV);
5320 * Fired just before the CalendarNavigator is to be rendered
5321 * @event beforeRenderNavEvent
5323 me.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
5326 * Fired after the CalendarNavigator is rendered
5327 * @event renderNavEvent
5329 me.renderNavEvent = new CE(defEvents.RENDER_NAV);
5332 * Fired just before the CalendarGroup is to be destroyed
5333 * @event beforeDestroyEvent
5335 me.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
5338 * Fired after the CalendarGroup is destroyed. This event should be used
5339 * for notification only. When this event is fired, important CalendarGroup instance
5340 * properties, dom references and event listeners have already been
5341 * removed/dereferenced, and hence the CalendarGroup instance is not in a usable
5344 * @event destroyEvent
5346 me.destroyEvent = new CE(defEvents.DESTROY);
5350 * The default Config handler for the "pages" property
5351 * @method configPages
5352 * @param {String} type The CustomEvent type (usually the property name)
5353 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5354 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5356 configPages : function(type, args, obj) {
5357 var pageCount = args[0],
5358 cfgPageDate = DEF_CFG.PAGEDATE.key,
5361 firstPageDate = null,
5362 groupCalClass = "groupcal",
5363 firstClass = "first-of-type",
5364 lastClass = "last-of-type";
5366 for (var p=0;p<pageCount;++p) {
5367 var calId = this.id + sep + p,
5368 calContainerId = this.containerId + sep + p,
5369 childConfig = this.cfg.getConfig();
5371 childConfig.close = false;
5372 childConfig.title = false;
5373 childConfig.navigator = null;
5376 caldate = new Date(firstPageDate);
5377 this._setMonthOnDate(caldate, caldate.getMonth() + p);
5378 childConfig.pageDate = caldate;
5381 var cal = this.constructChild(calId, calContainerId, childConfig);
5383 Dom.removeClass(cal.oDomContainer, this.Style.CSS_SINGLE);
5384 Dom.addClass(cal.oDomContainer, groupCalClass);
5387 firstPageDate = cal.cfg.getProperty(cfgPageDate);
5388 Dom.addClass(cal.oDomContainer, firstClass);
5391 if (p==(pageCount-1)) {
5392 Dom.addClass(cal.oDomContainer, lastClass);
5398 this.pages[this.pages.length] = cal;
5403 * The default Config handler for the "pagedate" property
5404 * @method configPageDate
5405 * @param {String} type The CustomEvent type (usually the property name)
5406 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5407 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5409 configPageDate : function(type, args, obj) {
5413 var cfgPageDate = DEF_CFG.PAGEDATE.key;
5415 for (var p=0;p<this.pages.length;++p) {
5416 var cal = this.pages[p];
5418 firstPageDate = cal._parsePageDate(val);
5419 cal.cfg.setProperty(cfgPageDate, firstPageDate);
5421 var pageDate = new Date(firstPageDate);
5422 this._setMonthOnDate(pageDate, pageDate.getMonth() + p);
5423 cal.cfg.setProperty(cfgPageDate, pageDate);
5429 * The default Config handler for the CalendarGroup "selected" property
5430 * @method configSelected
5431 * @param {String} type The CustomEvent type (usually the property name)
5432 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5433 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5435 configSelected : function(type, args, obj) {
5436 var cfgSelected = DEF_CFG.SELECTED.key;
5437 this.delegateConfig(type, args, obj);
5438 var selected = (this.pages.length > 0) ? this.pages[0].cfg.getProperty(cfgSelected) : [];
5439 this.cfg.setProperty(cfgSelected, selected, true);
5444 * Delegates a configuration property to the CustomEvents associated with the CalendarGroup's children
5445 * @method delegateConfig
5446 * @param {String} type The CustomEvent type (usually the property name)
5447 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5448 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5450 delegateConfig : function(type, args, obj) {
5454 for (var p=0;p<this.pages.length;p++) {
5455 cal = this.pages[p];
5456 cal.cfg.setProperty(type, val);
5461 * Adds a function to all child Calendars within this CalendarGroup.
5462 * @method setChildFunction
5463 * @param {String} fnName The name of the function
5464 * @param {Function} fn The function to apply to each Calendar page object
5466 setChildFunction : function(fnName, fn) {
5467 var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
5469 for (var p=0;p<pageCount;++p) {
5470 this.pages[p][fnName] = fn;
5475 * Calls a function within all child Calendars within this CalendarGroup.
5476 * @method callChildFunction
5477 * @param {String} fnName The name of the function
5478 * @param {Array} args The arguments to pass to the function
5480 callChildFunction : function(fnName, args) {
5481 var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
5483 for (var p=0;p<pageCount;++p) {
5484 var page = this.pages[p];
5486 var fn = page[fnName];
5487 fn.call(page, args);
5493 * Constructs a child calendar. This method can be overridden if a subclassed version of the default
5494 * calendar is to be used.
5495 * @method constructChild
5496 * @param {String} id The id of the table element that will represent the calendar widget
5497 * @param {String} containerId The id of the container div element that will wrap the calendar table
5498 * @param {Object} config The configuration object containing the Calendar's arguments
5499 * @return {YAHOO.widget.Calendar} The YAHOO.widget.Calendar instance that is constructed
5501 constructChild : function(id,containerId,config) {
5502 var container = document.getElementById(containerId);
5504 container = document.createElement("div");
5505 container.id = containerId;
5506 this.oDomContainer.appendChild(container);
5508 return new Calendar(id,containerId,config);
5512 * Sets the calendar group's month explicitly. This month will be set into the first
5513 * page of the multi-page calendar, and all other months will be iterated appropriately.
5515 * @param {Number} month The numeric month, from 0 (January) to 11 (December)
5517 setMonth : function(month) {
5518 month = parseInt(month, 10);
5521 var cfgPageDate = DEF_CFG.PAGEDATE.key;
5523 for (var p=0; p<this.pages.length; ++p) {
5524 var cal = this.pages[p];
5525 var pageDate = cal.cfg.getProperty(cfgPageDate);
5527 currYear = pageDate.getFullYear();
5529 pageDate.setFullYear(currYear);
5531 this._setMonthOnDate(pageDate, month+p);
5532 cal.cfg.setProperty(cfgPageDate, pageDate);
5537 * Sets the calendar group's year explicitly. This year will be set into the first
5538 * page of the multi-page calendar, and all other months will be iterated appropriately.
5540 * @param {Number} year The numeric 4-digit year
5542 setYear : function(year) {
5544 var cfgPageDate = DEF_CFG.PAGEDATE.key;
5546 year = parseInt(year, 10);
5547 for (var p=0;p<this.pages.length;++p) {
5548 var cal = this.pages[p];
5549 var pageDate = cal.cfg.getProperty(cfgPageDate);
5551 if ((pageDate.getMonth()+1) == 1 && p>0) {
5559 * Calls the render function of all child calendars within the group.
5562 render : function() {
5563 this.renderHeader();
5564 for (var p=0;p<this.pages.length;++p) {
5565 var cal = this.pages[p];
5568 this.renderFooter();
5572 * Selects a date or a collection of dates on the current calendar. This method, by default,
5573 * does not call the render method explicitly. Once selection has completed, render must be
5574 * called for the changes to be reflected visually.
5576 * @param {String/Date/Date[]} date The date string of dates to select in the current calendar. Valid formats are
5577 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
5578 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
5579 * This method can also take a JavaScript Date object or an array of Date objects.
5580 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5582 select : function(date) {
5583 for (var p=0;p<this.pages.length;++p) {
5584 var cal = this.pages[p];
5587 return this.getSelectedDates();
5591 * Selects dates in the CalendarGroup based on the cell index provided. This method is used to select cells without having to do a full render. The selected style is applied to the cells directly.
5592 * The value of the MULTI_SELECT Configuration attribute will determine the set of dates which get selected.
5594 * <li>If MULTI_SELECT is false, selectCell will select the cell at the specified index for only the last displayed Calendar page.</li>
5595 * <li>If MULTI_SELECT is true, selectCell will select the cell at the specified index, on each displayed Calendar page.</li>
5597 * @method selectCell
5598 * @param {Number} cellIndex The index of the cell to be selected.
5599 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5601 selectCell : function(cellIndex) {
5602 for (var p=0;p<this.pages.length;++p) {
5603 var cal = this.pages[p];
5604 cal.selectCell(cellIndex);
5606 return this.getSelectedDates();
5610 * Deselects a date or a collection of dates on the current calendar. This method, by default,
5611 * does not call the render method explicitly. Once deselection has completed, render must be
5612 * called for the changes to be reflected visually.
5614 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
5615 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
5616 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
5617 * This method can also take a JavaScript Date object or an array of Date objects.
5618 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5620 deselect : function(date) {
5621 for (var p=0;p<this.pages.length;++p) {
5622 var cal = this.pages[p];
5625 return this.getSelectedDates();
5629 * Deselects all dates on the current calendar.
5630 * @method deselectAll
5631 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5632 * Assuming that this function executes properly, the return value should be an empty array.
5633 * However, the empty array is returned for the sake of being able to check the selection status
5636 deselectAll : function() {
5637 for (var p=0;p<this.pages.length;++p) {
5638 var cal = this.pages[p];
5641 return this.getSelectedDates();
5645 * Deselects dates in the CalendarGroup based on the cell index provided. This method is used to select cells without having to do a full render. The selected style is applied to the cells directly.
5646 * deselectCell will deselect the cell at the specified index on each displayed Calendar page.
5648 * @method deselectCell
5649 * @param {Number} cellIndex The index of the cell to deselect.
5650 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5652 deselectCell : function(cellIndex) {
5653 for (var p=0;p<this.pages.length;++p) {
5654 var cal = this.pages[p];
5655 cal.deselectCell(cellIndex);
5657 return this.getSelectedDates();
5661 * Resets the calendar widget to the originally selected month and year, and
5662 * sets the calendar to the initial selection(s).
5665 reset : function() {
5666 for (var p=0;p<this.pages.length;++p) {
5667 var cal = this.pages[p];
5673 * Clears the selected dates in the current calendar widget and sets the calendar
5674 * to the current month and year.
5677 clear : function() {
5678 for (var p=0;p<this.pages.length;++p) {
5679 var cal = this.pages[p];
5683 this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
5684 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.pages[0].today.getTime()));
5689 * Navigates to the next month page in the calendar widget.
5692 nextMonth : function() {
5693 for (var p=0;p<this.pages.length;++p) {
5694 var cal = this.pages[p];
5700 * Navigates to the previous month page in the calendar widget.
5701 * @method previousMonth
5703 previousMonth : function() {
5704 for (var p=this.pages.length-1;p>=0;--p) {
5705 var cal = this.pages[p];
5706 cal.previousMonth();
5711 * Navigates to the next year in the currently selected month in the calendar widget.
5714 nextYear : function() {
5715 for (var p=0;p<this.pages.length;++p) {
5716 var cal = this.pages[p];
5722 * Navigates to the previous year in the currently selected month in the calendar widget.
5723 * @method previousYear
5725 previousYear : function() {
5726 for (var p=0;p<this.pages.length;++p) {
5727 var cal = this.pages[p];
5733 * Gets the list of currently selected dates from the calendar.
5734 * @return An array of currently selected JavaScript Date objects.
5737 getSelectedDates : function() {
5738 var returnDates = [];
5739 var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
5740 for (var d=0;d<selected.length;++d) {
5741 var dateArray = selected[d];
5743 var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
5744 returnDates.push(date);
5747 returnDates.sort( function(a,b) { return a-b; } );
5752 * Adds a renderer to the render stack. The function reference passed to this method will be executed
5753 * when a date cell matches the conditions specified in the date string for this renderer.
5754 * @method addRenderer
5755 * @param {String} sDates A date string to associate with the specified renderer. Valid formats
5756 * include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
5757 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5759 addRenderer : function(sDates, fnRender) {
5760 for (var p=0;p<this.pages.length;++p) {
5761 var cal = this.pages[p];
5762 cal.addRenderer(sDates, fnRender);
5767 * Adds a month to the render stack. The function reference passed to this method will be executed
5768 * when a date cell matches the month passed to this method.
5769 * @method addMonthRenderer
5770 * @param {Number} month The month (1-12) to associate with this renderer
5771 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5773 addMonthRenderer : function(month, fnRender) {
5774 for (var p=0;p<this.pages.length;++p) {
5775 var cal = this.pages[p];
5776 cal.addMonthRenderer(month, fnRender);
5781 * Adds a weekday to the render stack. The function reference passed to this method will be executed
5782 * when a date cell matches the weekday passed to this method.
5783 * @method addWeekdayRenderer
5784 * @param {Number} weekday The weekday (1-7) to associate with this renderer. 1=Sunday, 2=Monday etc.
5785 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5787 addWeekdayRenderer : function(weekday, fnRender) {
5788 for (var p=0;p<this.pages.length;++p) {
5789 var cal = this.pages[p];
5790 cal.addWeekdayRenderer(weekday, fnRender);
5795 * Removes all custom renderers added to the CalendarGroup through the addRenderer, addMonthRenderer and
5796 * addWeekRenderer methods. CalendarGroup's render method needs to be called to after removing renderers
5797 * to see the changes applied.
5799 * @method removeRenderers
5801 removeRenderers : function() {
5802 this.callChildFunction("removeRenderers");
5806 * Renders the header for the CalendarGroup.
5807 * @method renderHeader
5809 renderHeader : function() {
5810 // EMPTY DEFAULT IMPL
5814 * Renders a footer for the 2-up calendar container. By default, this method is
5816 * @method renderFooter
5818 renderFooter : function() {
5819 // EMPTY DEFAULT IMPL
5823 * Adds the designated number of months to the current calendar month, and sets the current
5824 * calendar page date to the new month.
5826 * @param {Number} count The number of months to add to the current calendar
5828 addMonths : function(count) {
5829 this.callChildFunction("addMonths", count);
5833 * Subtracts the designated number of months from the current calendar month, and sets the current
5834 * calendar page date to the new month.
5835 * @method subtractMonths
5836 * @param {Number} count The number of months to subtract from the current calendar
5838 subtractMonths : function(count) {
5839 this.callChildFunction("subtractMonths", count);
5843 * Adds the designated number of years to the current calendar, and sets the current
5844 * calendar page date to the new month.
5846 * @param {Number} count The number of years to add to the current calendar
5848 addYears : function(count) {
5849 this.callChildFunction("addYears", count);
5853 * Subtcats the designated number of years from the current calendar, and sets the current
5854 * calendar page date to the new month.
5855 * @method subtractYears
5856 * @param {Number} count The number of years to subtract from the current calendar
5858 subtractYears : function(count) {
5859 this.callChildFunction("subtractYears", count);
5863 * Returns the Calendar page instance which has a pagedate (month/year) matching the given date.
5864 * Returns null if no match is found.
5866 * @method getCalendarPage
5867 * @param {Date} date The JavaScript Date object for which a Calendar page is to be found.
5868 * @return {Calendar} The Calendar page instance representing the month to which the date
5871 getCalendarPage : function(date) {
5874 var y = date.getFullYear(),
5875 m = date.getMonth();
5877 var pages = this.pages;
5878 for (var i = 0; i < pages.length; ++i) {
5879 var pageDate = pages[i].cfg.getProperty("pagedate");
5880 if (pageDate.getFullYear() === y && pageDate.getMonth() === m) {
5890 * Sets the month on a Date object, taking into account year rollover if the month is less than 0 or greater than 11.
5891 * The Date object passed in is modified. It should be cloned before passing it into this method if the original value needs to be maintained
5892 * @method _setMonthOnDate
5894 * @param {Date} date The Date object on which to set the month index
5895 * @param {Number} iMonth The month index to set
5897 _setMonthOnDate : function(date, iMonth) {
5898 // Bug in Safari 1.3, 2.0 (WebKit build < 420), Date.setMonth does not work consistently if iMonth is not 0-11
5899 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420 && (iMonth < 0 || iMonth > 11)) {
5900 var newDate = DateMath.add(date, DateMath.MONTH, iMonth-date.getMonth());
5901 date.setTime(newDate.getTime());
5903 date.setMonth(iMonth);
5908 * Fixes the width of the CalendarGroup container element, to account for miswrapped floats
5912 _fixWidth : function() {
5914 for (var p=0;p<this.pages.length;++p) {
5915 var cal = this.pages[p];
5916 w += cal.oDomContainer.offsetWidth;
5919 this.oDomContainer.style.width = w + "px";
5924 * Returns a string representation of the object.
5926 * @return {String} A string representation of the CalendarGroup object.
5928 toString : function() {
5929 return "CalendarGroup " + this.id;
5933 * Destroys the CalendarGroup instance. The method will remove references
5934 * to HTML elements, remove any event listeners added by the CalendarGroup.
5936 * It will also destroy the Config and CalendarNavigator instances created by the
5937 * CalendarGroup and the individual Calendar instances created for each page.
5941 destroy : function() {
5943 if (this.beforeDestroyEvent.fire()) {
5948 if (cal.navigator) {
5949 cal.navigator.destroy();
5956 // DOM event listeners
5957 Event.purgeElement(cal.oDomContainer, true);
5959 // Generated markup/DOM - Not removing the container DIV since we didn't create it.
5960 Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_CONTAINER);
5961 Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_MULTI_UP);
5963 for (var i = 0, l = cal.pages.length; i < l; i++) {
5964 cal.pages[i].destroy();
5965 cal.pages[i] = null;
5968 cal.oDomContainer.innerHTML = "";
5970 // JS-to-DOM references
5971 cal.oDomContainer = null;
5973 this.destroyEvent.fire();
5979 * CSS class representing the container for the calendar
5980 * @property YAHOO.widget.CalendarGroup.CSS_CONTAINER
5985 CalendarGroup.CSS_CONTAINER = "yui-calcontainer";
5988 * CSS class representing the container for the calendar
5989 * @property YAHOO.widget.CalendarGroup.CSS_MULTI_UP
5994 CalendarGroup.CSS_MULTI_UP = "multi";
5997 * CSS class representing the title for the 2-up calendar
5998 * @property YAHOO.widget.CalendarGroup.CSS_2UPTITLE
6003 CalendarGroup.CSS_2UPTITLE = "title";
6006 * CSS class representing the close icon for the 2-up calendar
6007 * @property YAHOO.widget.CalendarGroup.CSS_2UPCLOSE
6010 * @deprecated Along with Calendar.IMG_ROOT and NAV_ARROW_LEFT, NAV_ARROW_RIGHT configuration properties.
6011 * Calendar's <a href="YAHOO.widget.Calendar.html#Style.CSS_CLOSE">Style.CSS_CLOSE</a> property now represents the CSS class used to render the close icon
6014 CalendarGroup.CSS_2UPCLOSE = "close-icon";
6016 YAHOO.lang.augmentProto(CalendarGroup, Calendar, "buildDayLabel",
6018 "renderOutOfBoundsDate",
6021 "renderCellDefault",
6023 "renderCellStyleHighlight1",
6024 "renderCellStyleHighlight2",
6025 "renderCellStyleHighlight3",
6026 "renderCellStyleHighlight4",
6027 "renderCellStyleToday",
6028 "renderCellStyleSelected",
6029 "renderCellNotThisMonth",
6030 "renderBodyCellRestricted",
6039 "createCloseButton",
6041 "removeCloseButton",
6049 YAHOO.widget.CalGrp = CalendarGroup;
6050 YAHOO.widget.CalendarGroup = CalendarGroup;
6053 * @class YAHOO.widget.Calendar2up
6054 * @extends YAHOO.widget.CalendarGroup
6055 * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
6057 YAHOO.widget.Calendar2up = function(id, containerId, config) {
6058 this.init(id, containerId, config);
6061 YAHOO.extend(YAHOO.widget.Calendar2up, CalendarGroup);
6064 * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
6066 YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up;
6070 * The CalendarNavigator is used along with a Calendar/CalendarGroup to
6071 * provide a Month/Year popup navigation control, allowing the user to navigate
6072 * to a specific month/year in the Calendar/CalendarGroup without having to
6073 * scroll through months sequentially
6075 * @namespace YAHOO.widget
6076 * @class CalendarNavigator
6078 * @param {Calendar|CalendarGroup} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached.
6080 YAHOO.widget.CalendarNavigator = function(cal) {
6085 // Setup static properties (inside anon fn, so that we can use shortcuts)
6086 var CN = YAHOO.widget.CalendarNavigator;
6089 * YAHOO.widget.CalendarNavigator.CLASSES contains constants
6090 * for the class values applied to the CalendarNaviatgator's
6092 * @property YAHOO.widget.CalendarNavigator.CLASSES
6098 * Class applied to the Calendar Navigator's bounding box
6099 * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV
6105 * Class applied to the Calendar/CalendarGroup's bounding box to indicate
6106 * the Navigator is currently visible
6107 * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV_VISIBLE
6111 NAV_VISIBLE: "yui-cal-nav-visible",
6113 * Class applied to the Navigator mask's bounding box
6114 * @property YAHOO.widget.CalendarNavigator.CLASSES.MASK
6118 MASK : "yui-cal-nav-mask",
6120 * Class applied to the year label/control bounding box
6121 * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR
6125 YEAR : "yui-cal-nav-y",
6127 * Class applied to the month label/control bounding box
6128 * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH
6132 MONTH : "yui-cal-nav-m",
6134 * Class applied to the submit/cancel button's bounding box
6135 * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTONS
6139 BUTTONS : "yui-cal-nav-b",
6141 * Class applied to buttons wrapping element
6142 * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTON
6146 BUTTON : "yui-cal-nav-btn",
6148 * Class applied to the validation error area's bounding box
6149 * @property YAHOO.widget.CalendarNavigator.CLASSES.ERROR
6153 ERROR : "yui-cal-nav-e",
6155 * Class applied to the year input control
6156 * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR_CTRL
6160 YEAR_CTRL : "yui-cal-nav-yc",
6162 * Class applied to the month input control
6163 * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH_CTRL
6167 MONTH_CTRL : "yui-cal-nav-mc",
6169 * Class applied to controls with invalid data (e.g. a year input field with invalid an year)
6170 * @property YAHOO.widget.CalendarNavigator.CLASSES.INVALID
6174 INVALID : "yui-invalid",
6176 * Class applied to default controls
6177 * @property YAHOO.widget.CalendarNavigator.CLASSES.DEFAULT
6181 DEFAULT : "yui-default"
6185 * Object literal containing the default configuration values for the CalendarNavigator
6186 * The configuration object is expected to follow the format below, with the properties being
6190 * <dd><em>Object</em> : An object with the properties shown below, defining the string labels to use in the Navigator's UI
6192 * <dt>month</dt><dd><em>String</em> : The string to use for the month label. Defaults to "Month".</dd>
6193 * <dt>year</dt><dd><em>String</em> : The string to use for the year label. Defaults to "Year".</dd>
6194 * <dt>submit</dt><dd><em>String</em> : The string to use for the submit button label. Defaults to "Okay".</dd>
6195 * <dt>cancel</dt><dd><em>String</em> : The string to use for the cancel button label. Defaults to "Cancel".</dd>
6196 * <dt>invalidYear</dt><dd><em>String</em> : The string to use for invalid year values. Defaults to "Year needs to be a number".</dd>
6199 * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd>
6200 * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
6202 * @property DEFAULT_CONFIG
6206 CN.DEFAULT_CONFIG = {
6212 invalidYear : "Year needs to be a number"
6214 monthFormat: YAHOO.widget.Calendar.LONG,
6215 initialFocus: "year"
6219 * Object literal containing the default configuration values for the CalendarNavigator
6220 * @property _DEFAULT_CFG
6222 * @deprecated Made public. See the public DEFAULT_CONFIG property
6226 CN._DEFAULT_CFG = CN.DEFAULT_CONFIG;
6230 * The suffix added to the Calendar/CalendarGroup's ID, to generate
6231 * a unique ID for the Navigator and it's bounding box.
6232 * @property YAHOO.widget.CalendarNavigator.ID_SUFFIX
6237 CN.ID_SUFFIX = "_nav";
6239 * The suffix added to the Navigator's ID, to generate
6240 * a unique ID for the month control.
6241 * @property YAHOO.widget.CalendarNavigator.MONTH_SUFFIX
6246 CN.MONTH_SUFFIX = "_month";
6248 * The suffix added to the Navigator's ID, to generate
6249 * a unique ID for the year control.
6250 * @property YAHOO.widget.CalendarNavigator.YEAR_SUFFIX
6255 CN.YEAR_SUFFIX = "_year";
6257 * The suffix added to the Navigator's ID, to generate
6258 * a unique ID for the error bounding box.
6259 * @property YAHOO.widget.CalendarNavigator.ERROR_SUFFIX
6264 CN.ERROR_SUFFIX = "_error";
6266 * The suffix added to the Navigator's ID, to generate
6267 * a unique ID for the "Cancel" button.
6268 * @property YAHOO.widget.CalendarNavigator.CANCEL_SUFFIX
6273 CN.CANCEL_SUFFIX = "_cancel";
6275 * The suffix added to the Navigator's ID, to generate
6276 * a unique ID for the "Submit" button.
6277 * @property YAHOO.widget.CalendarNavigator.SUBMIT_SUFFIX
6282 CN.SUBMIT_SUFFIX = "_submit";
6285 * The number of digits to which the year input control is to be limited.
6286 * @property YAHOO.widget.CalendarNavigator.YR_MAX_DIGITS
6290 CN.YR_MAX_DIGITS = 4;
6293 * The amount by which to increment the current year value,
6294 * when the arrow up/down key is pressed on the year control
6295 * @property YAHOO.widget.CalendarNavigator.YR_MINOR_INC
6299 CN.YR_MINOR_INC = 1;
6302 * The amount by which to increment the current year value,
6303 * when the page up/down key is pressed on the year control
6304 * @property YAHOO.widget.CalendarNavigator.YR_MAJOR_INC
6308 CN.YR_MAJOR_INC = 10;
6311 * Artificial delay (in ms) between the time the Navigator is hidden
6312 * and the Calendar/CalendarGroup state is updated. Allows the user
6313 * the see the Calendar/CalendarGroup page changing. If set to 0
6314 * the Calendar/CalendarGroup page will be updated instantly
6315 * @property YAHOO.widget.CalendarNavigator.UPDATE_DELAY
6319 CN.UPDATE_DELAY = 50;
6322 * Regular expression used to validate the year input
6323 * @property YAHOO.widget.CalendarNavigator.YR_PATTERN
6327 CN.YR_PATTERN = /^\d+$/;
6329 * Regular expression used to trim strings
6330 * @property YAHOO.widget.CalendarNavigator.TRIM
6334 CN.TRIM = /^\s*(.*?)\s*$/;
6337 YAHOO.widget.CalendarNavigator.prototype = {
6340 * The unique ID for this CalendarNavigator instance
6347 * The Calendar/CalendarGroup instance to which the navigator belongs
6349 * @type {Calendar|CalendarGroup}
6354 * Reference to the HTMLElement used to render the navigator's bounding box
6361 * Reference to the HTMLElement used to render the navigator's mask
6368 * Reference to the HTMLElement used to input the year
6375 * Reference to the HTMLElement used to input the month
6382 * Reference to the HTMLElement used to display validation errors
6389 * Reference to the HTMLElement used to update the Calendar/Calendar group
6390 * with the month/year values
6391 * @property submitEl
6397 * Reference to the HTMLElement used to hide the navigator without updating the
6398 * Calendar/Calendar group
6399 * @property cancelEl
6405 * Reference to the first focusable control in the navigator (by default monthEl)
6406 * @property firstCtrl
6412 * Reference to the last focusable control in the navigator (by default cancelEl)
6413 * @property lastCtrl
6419 * The document containing the Calendar/Calendar group instance
6422 * @type HTMLDocument
6427 * Internal state property for the current year displayed in the navigator
6435 * Internal state property for the current month index displayed in the navigator
6443 * Private internal state property which indicates whether or not the
6444 * Navigator has been rendered.
6446 * @property __rendered
6452 * Init lifecycle method called as part of construction
6455 * @param {Calendar} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached
6457 init : function(cal) {
6458 var calBox = cal.oDomContainer;
6461 this.id = calBox.id + YAHOO.widget.CalendarNavigator.ID_SUFFIX;
6462 this._doc = calBox.ownerDocument;
6465 * Private flag, to identify IE Quirks
6467 * @property __isIEQuirks
6469 var ie = YAHOO.env.ua.ie;
6470 this.__isIEQuirks = (ie && ((ie <= 6) || (this._doc.compatMode == "BackCompat")));
6474 * Displays the navigator and mask, updating the input controls to reflect the
6475 * currently set month and year. The show method will invoke the render method
6476 * if the navigator has not been renderered already, allowing for lazy rendering
6479 * The show method will fire the Calendar/CalendarGroup's beforeShowNav and showNav events
6484 var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
6486 if (this.cal.beforeShowNavEvent.fire()) {
6487 if (!this.__rendered) {
6492 this._updateMonthUI();
6493 this._updateYearUI();
6494 this._show(this.navEl, true);
6496 this.setInitialFocus();
6499 YAHOO.util.Dom.addClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
6500 this.cal.showNavEvent.fire();
6505 * Hides the navigator and mask
6507 * The show method will fire the Calendar/CalendarGroup's beforeHideNav event and hideNav events
6511 var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
6513 if (this.cal.beforeHideNavEvent.fire()) {
6514 this._show(this.navEl, false);
6516 YAHOO.util.Dom.removeClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
6517 this.cal.hideNavEvent.fire();
6523 * Displays the navigator's mask element
6527 showMask : function() {
6528 this._show(this.maskEl, true);
6529 if (this.__isIEQuirks) {
6535 * Hides the navigator's mask element
6539 hideMask : function() {
6540 this._show(this.maskEl, false);
6544 * Returns the current month set on the navigator
6546 * Note: This may not be the month set in the UI, if
6547 * the UI contains an invalid value.
6550 * @return {Number} The Navigator's current month index
6552 getMonth: function() {
6557 * Returns the current year set on the navigator
6559 * Note: This may not be the year set in the UI, if
6560 * the UI contains an invalid value.
6563 * @return {Number} The Navigator's current year value
6565 getYear: function() {
6570 * Sets the current month on the Navigator, and updates the UI
6573 * @param {Number} nMonth The month index, from 0 (Jan) through 11 (Dec).
6575 setMonth : function(nMonth) {
6576 if (nMonth >= 0 && nMonth < 12) {
6577 this._month = nMonth;
6579 this._updateMonthUI();
6583 * Sets the current year on the Navigator, and updates the UI. If the
6584 * provided year is invalid, it will not be set.
6587 * @param {Number} nYear The full year value to set the Navigator to.
6589 setYear : function(nYear) {
6590 var yrPattern = YAHOO.widget.CalendarNavigator.YR_PATTERN;
6591 if (YAHOO.lang.isNumber(nYear) && yrPattern.test(nYear+"")) {
6594 this._updateYearUI();
6598 * Renders the HTML for the navigator, adding it to the
6599 * document and attaches event listeners if it has not
6600 * already been rendered.
6604 render: function() {
6605 this.cal.beforeRenderNavEvent.fire();
6606 if (!this.__rendered) {
6609 this.applyListeners();
6610 this.__rendered = true;
6612 this.cal.renderNavEvent.fire();
6616 * Creates the navigator's containing HTMLElement, it's contents, and appends
6617 * the containg element to the Calendar/CalendarGroup's container.
6621 createNav : function() {
6622 var NAV = YAHOO.widget.CalendarNavigator;
6623 var doc = this._doc;
6625 var d = doc.createElement("div");
6626 d.className = NAV.CLASSES.NAV;
6628 var htmlBuf = this.renderNavContents([]);
6630 d.innerHTML = htmlBuf.join('');
6631 this.cal.oDomContainer.appendChild(d);
6635 this.yearEl = doc.getElementById(this.id + NAV.YEAR_SUFFIX);
6636 this.monthEl = doc.getElementById(this.id + NAV.MONTH_SUFFIX);
6637 this.errorEl = doc.getElementById(this.id + NAV.ERROR_SUFFIX);
6638 this.submitEl = doc.getElementById(this.id + NAV.SUBMIT_SUFFIX);
6639 this.cancelEl = doc.getElementById(this.id + NAV.CANCEL_SUFFIX);
6641 if (YAHOO.env.ua.gecko && this.yearEl && this.yearEl.type == "text") {
6642 // Avoid XUL error on focus, select [ https://bugzilla.mozilla.org/show_bug.cgi?id=236791,
6643 // supposedly fixed in 1.8.1, but there are reports of it still being around for methods other than blur ]
6644 this.yearEl.setAttribute("autocomplete", "off");
6647 this._setFirstLastElements();
6651 * Creates the Mask HTMLElement and appends it to the Calendar/CalendarGroups
6654 * @method createMask
6656 createMask : function() {
6657 var C = YAHOO.widget.CalendarNavigator.CLASSES;
6659 var d = this._doc.createElement("div");
6660 d.className = C.MASK;
6662 this.cal.oDomContainer.appendChild(d);
6667 * Used to set the width/height of the mask in pixels to match the Calendar Container.
6668 * Currently only used for IE6 or IE in quirks mode. The other A-Grade browser are handled using CSS (width/height 100%).
6670 * The method is also registered as an HTMLElement resize listener on the Calendars container element.
6675 _syncMask : function() {
6676 var c = this.cal.oDomContainer;
6677 if (c && this.maskEl) {
6678 var r = YAHOO.util.Dom.getRegion(c);
6679 YAHOO.util.Dom.setStyle(this.maskEl, "width", r.right - r.left + "px");
6680 YAHOO.util.Dom.setStyle(this.maskEl, "height", r.bottom - r.top + "px");
6685 * Renders the contents of the navigator
6687 * @method renderNavContents
6689 * @param {Array} html The HTML buffer to append the HTML to.
6690 * @return {Array} A reference to the buffer passed in.
6692 renderNavContents : function(html) {
6693 var NAV = YAHOO.widget.CalendarNavigator,
6695 h = html; // just to use a shorter name
6697 h[h.length] = '<div class="' + C.MONTH + '">';
6698 this.renderMonth(h);
6699 h[h.length] = '</div>';
6700 h[h.length] = '<div class="' + C.YEAR + '">';
6702 h[h.length] = '</div>';
6703 h[h.length] = '<div class="' + C.BUTTONS + '">';
6704 this.renderButtons(h);
6705 h[h.length] = '</div>';
6706 h[h.length] = '<div class="' + C.ERROR + '" id="' + this.id + NAV.ERROR_SUFFIX + '"></div>';
6712 * Renders the month label and control for the navigator
6714 * @method renderNavContents
6715 * @param {Array} html The HTML buffer to append the HTML to.
6716 * @return {Array} A reference to the buffer passed in.
6718 renderMonth : function(html) {
6719 var NAV = YAHOO.widget.CalendarNavigator,
6722 var id = this.id + NAV.MONTH_SUFFIX,
6723 mf = this.__getCfg("monthFormat"),
6724 months = this.cal.cfg.getProperty((mf == YAHOO.widget.Calendar.SHORT) ? "MONTHS_SHORT" : "MONTHS_LONG"),
6727 if (months && months.length > 0) {
6728 h[h.length] = '<label for="' + id + '">';
6729 h[h.length] = this.__getCfg("month", true);
6730 h[h.length] = '</label>';
6731 h[h.length] = '<select name="' + id + '" id="' + id + '" class="' + C.MONTH_CTRL + '">';
6732 for (var i = 0; i < months.length; i++) {
6733 h[h.length] = '<option value="' + i + '">';
6734 h[h.length] = months[i];
6735 h[h.length] = '</option>';
6737 h[h.length] = '</select>';
6743 * Renders the year label and control for the navigator
6745 * @method renderYear
6746 * @param {Array} html The HTML buffer to append the HTML to.
6747 * @return {Array} A reference to the buffer passed in.
6749 renderYear : function(html) {
6750 var NAV = YAHOO.widget.CalendarNavigator,
6753 var id = this.id + NAV.YEAR_SUFFIX,
6754 size = NAV.YR_MAX_DIGITS,
6757 h[h.length] = '<label for="' + id + '">';
6758 h[h.length] = this.__getCfg("year", true);
6759 h[h.length] = '</label>';
6760 h[h.length] = '<input type="text" name="' + id + '" id="' + id + '" class="' + C.YEAR_CTRL + '" maxlength="' + size + '"/>';
6765 * Renders the submit/cancel buttons for the navigator
6767 * @method renderButton
6768 * @return {String} The HTML created for the Button UI
6770 renderButtons : function(html) {
6771 var C = YAHOO.widget.CalendarNavigator.CLASSES;
6774 h[h.length] = '<span class="' + C.BUTTON + ' ' + C.DEFAULT + '">';
6775 h[h.length] = '<button type="button" id="' + this.id + '_submit' + '">';
6776 h[h.length] = this.__getCfg("submit", true);
6777 h[h.length] = '</button>';
6778 h[h.length] = '</span>';
6779 h[h.length] = '<span class="' + C.BUTTON +'">';
6780 h[h.length] = '<button type="button" id="' + this.id + '_cancel' + '">';
6781 h[h.length] = this.__getCfg("cancel", true);
6782 h[h.length] = '</button>';
6783 h[h.length] = '</span>';
6789 * Attaches DOM event listeners to the rendered elements
6791 * The method will call applyKeyListeners, to setup keyboard specific
6794 * @method applyListeners
6796 applyListeners : function() {
6797 var E = YAHOO.util.Event;
6799 function yearUpdateHandler() {
6800 if (this.validate()) {
6801 this.setYear(this._getYearFromUI());
6805 function monthUpdateHandler() {
6806 this.setMonth(this._getMonthFromUI());
6809 E.on(this.submitEl, "click", this.submit, this, true);
6810 E.on(this.cancelEl, "click", this.cancel, this, true);
6811 E.on(this.yearEl, "blur", yearUpdateHandler, this, true);
6812 E.on(this.monthEl, "change", monthUpdateHandler, this, true);
6814 if (this.__isIEQuirks) {
6815 YAHOO.util.Event.on(this.cal.oDomContainer, "resize", this._syncMask, this, true);
6818 this.applyKeyListeners();
6822 * Removes/purges DOM event listeners from the rendered elements
6824 * @method purgeListeners
6826 purgeListeners : function() {
6827 var E = YAHOO.util.Event;
6828 E.removeListener(this.submitEl, "click", this.submit);
6829 E.removeListener(this.cancelEl, "click", this.cancel);
6830 E.removeListener(this.yearEl, "blur");
6831 E.removeListener(this.monthEl, "change");
6832 if (this.__isIEQuirks) {
6833 E.removeListener(this.cal.oDomContainer, "resize", this._syncMask);
6836 this.purgeKeyListeners();
6840 * Attaches DOM listeners for keyboard support.
6841 * Tab/Shift-Tab looping, Enter Key Submit on Year element,
6842 * Up/Down/PgUp/PgDown year increment on Year element
6844 * NOTE: MacOSX Safari 2.x doesn't let you tab to buttons and
6845 * MacOSX Gecko does not let you tab to buttons or select controls,
6846 * so for these browsers, Tab/Shift-Tab looping is limited to the
6847 * elements which can be reached using the tab key.
6849 * @method applyKeyListeners
6851 applyKeyListeners : function() {
6852 var E = YAHOO.util.Event,
6855 // IE/Safari 3.1 doesn't fire keypress for arrow/pg keys (non-char keys)
6856 var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
6858 // - IE/Safari 3.1 doesn't fire keypress for non-char keys
6859 // - Opera doesn't allow us to cancel keydown or keypress for tab, but
6860 // changes focus successfully on keydown (keypress is too late to change focus - opera's already moved on).
6861 var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
6863 // Everyone likes keypress for Enter (char keys) - whoo hoo!
6864 E.on(this.yearEl, "keypress", this._handleEnterKey, this, true);
6866 E.on(this.yearEl, arrowEvt, this._handleDirectionKeys, this, true);
6867 E.on(this.lastCtrl, tabEvt, this._handleTabKey, this, true);
6868 E.on(this.firstCtrl, tabEvt, this._handleShiftTabKey, this, true);
6872 * Removes/purges DOM listeners for keyboard support
6874 * @method purgeKeyListeners
6876 purgeKeyListeners : function() {
6877 var E = YAHOO.util.Event,
6880 var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
6881 var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
6883 E.removeListener(this.yearEl, "keypress", this._handleEnterKey);
6884 E.removeListener(this.yearEl, arrowEvt, this._handleDirectionKeys);
6885 E.removeListener(this.lastCtrl, tabEvt, this._handleTabKey);
6886 E.removeListener(this.firstCtrl, tabEvt, this._handleShiftTabKey);
6890 * Updates the Calendar/CalendarGroup's pagedate with the currently set month and year if valid.
6892 * If the currently set month/year is invalid, a validation error will be displayed and the
6893 * Calendar/CalendarGroup's pagedate will not be updated.
6897 submit : function() {
6898 if (this.validate()) {
6901 this.setMonth(this._getMonthFromUI());
6902 this.setYear(this._getYearFromUI());
6906 // Artificial delay, just to help the user see something changed
6907 var delay = YAHOO.widget.CalendarNavigator.UPDATE_DELAY;
6910 window.setTimeout(function(){ nav._update(cal); }, delay);
6918 * Updates the Calendar rendered state, based on the state of the CalendarNavigator
6920 * @param cal The Calendar instance to update
6923 _update : function(cal) {
6924 var date = YAHOO.widget.DateMath.getDate(this.getYear() - cal.cfg.getProperty("YEAR_OFFSET"), this.getMonth(), 1);
6925 cal.cfg.setProperty("pagedate", date);
6930 * Hides the navigator and mask, without updating the Calendar/CalendarGroup's state
6934 cancel : function() {
6939 * Validates the current state of the UI controls
6942 * @return {Boolean} true, if the current UI state contains valid values, false if not
6944 validate : function() {
6945 if (this._getYearFromUI() !== null) {
6949 this.setYearError();
6950 this.setError(this.__getCfg("invalidYear", true));
6956 * Displays an error message in the Navigator's error panel
6958 * @param {String} msg The error message to display
6960 setError : function(msg) {
6962 this.errorEl.innerHTML = msg;
6963 this._show(this.errorEl, true);
6968 * Clears the navigator's error message and hides the error panel
6969 * @method clearError
6971 clearError : function() {
6973 this.errorEl.innerHTML = "";
6974 this._show(this.errorEl, false);
6979 * Displays the validation error UI for the year control
6980 * @method setYearError
6982 setYearError : function() {
6983 YAHOO.util.Dom.addClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
6987 * Removes the validation error UI for the year control
6988 * @method clearYearError
6990 clearYearError : function() {
6991 YAHOO.util.Dom.removeClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
6995 * Clears all validation and error messages in the UI
6996 * @method clearErrors
6998 clearErrors : function() {
7000 this.clearYearError();
7004 * Sets the initial focus, based on the configured value
7005 * @method setInitialFocus
7007 setInitialFocus : function() {
7008 var el = this.submitEl,
7009 f = this.__getCfg("initialFocus");
7011 if (f && f.toLowerCase) {
7012 f = f.toLowerCase();
7016 this.yearEl.select();
7020 } else if (f == "month") {
7025 if (el && YAHOO.lang.isFunction(el.focus)) {
7028 } catch (focusErr) {
7029 // TODO: Fall back if focus fails?
7035 * Removes all renderered HTML elements for the Navigator from
7036 * the DOM, purges event listeners and clears (nulls) any property
7037 * references to HTML references
7040 erase : function() {
7041 if (this.__rendered) {
7042 this.purgeListeners();
7044 // Clear out innerHTML references
7046 this.monthEl = null;
7047 this.errorEl = null;
7048 this.submitEl = null;
7049 this.cancelEl = null;
7050 this.firstCtrl = null;
7051 this.lastCtrl = null;
7053 this.navEl.innerHTML = "";
7056 var p = this.navEl.parentNode;
7058 p.removeChild(this.navEl);
7062 var pm = this.maskEl.parentNode;
7064 pm.removeChild(this.maskEl);
7067 this.__rendered = false;
7072 * Destroys the Navigator object and any HTML references
7075 destroy : function() {
7083 * Protected implementation to handle how UI elements are
7089 _show : function(el, bShow) {
7091 YAHOO.util.Dom.setStyle(el, "display", (bShow) ? "block" : "none");
7096 * Returns the month value (index), from the month UI element
7098 * @method _getMonthFromUI
7099 * @return {Number} The month index, or 0 if a UI element for the month
7102 _getMonthFromUI : function() {
7104 return this.monthEl.selectedIndex;
7106 return 0; // Default to Jan
7111 * Returns the year value, from the Navitator's year UI element
7113 * @method _getYearFromUI
7114 * @return {Number} The year value set in the UI, if valid. null is returned if
7115 * the UI does not contain a valid year value.
7117 _getYearFromUI : function() {
7118 var NAV = YAHOO.widget.CalendarNavigator;
7122 var value = this.yearEl.value;
7123 value = value.replace(NAV.TRIM, "$1");
7125 if (NAV.YR_PATTERN.test(value)) {
7126 yr = parseInt(value, 10);
7133 * Updates the Navigator's year UI, based on the year value set on the Navigator object
7135 * @method _updateYearUI
7137 _updateYearUI : function() {
7138 if (this.yearEl && this._year !== null) {
7139 this.yearEl.value = this._year;
7144 * Updates the Navigator's month UI, based on the month value set on the Navigator object
7146 * @method _updateMonthUI
7148 _updateMonthUI : function() {
7150 this.monthEl.selectedIndex = this._month;
7155 * Sets up references to the first and last focusable element in the Navigator's UI
7156 * in terms of tab order (Naviagator's firstEl and lastEl properties). The references
7157 * are used to control modality by looping around from the first to the last control
7158 * and visa versa for tab/shift-tab navigation.
7160 * See <a href="#applyKeyListeners">applyKeyListeners</a>
7163 * @method _setFirstLastElements
7165 _setFirstLastElements : function() {
7166 this.firstCtrl = this.monthEl;
7167 this.lastCtrl = this.cancelEl;
7169 // Special handling for MacOSX.
7170 // - Safari 2.x can't focus on buttons
7171 // - Gecko can't focus on select boxes or buttons
7173 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420){
7174 this.firstCtrl = this.monthEl;
7175 this.lastCtrl = this.yearEl;
7177 if (YAHOO.env.ua.gecko) {
7178 this.firstCtrl = this.yearEl;
7179 this.lastCtrl = this.yearEl;
7185 * Default Keyboard event handler to capture Enter
7186 * on the Navigator's year control (yearEl)
7188 * @method _handleEnterKey
7190 * @param {Event} e The DOM event being handled
7192 _handleEnterKey : function(e) {
7193 var KEYS = YAHOO.util.KeyListener.KEY;
7195 if (YAHOO.util.Event.getCharCode(e) == KEYS.ENTER) {
7196 YAHOO.util.Event.preventDefault(e);
7202 * Default Keyboard event handler to capture up/down/pgup/pgdown
7203 * on the Navigator's year control (yearEl).
7205 * @method _handleDirectionKeys
7207 * @param {Event} e The DOM event being handled
7209 _handleDirectionKeys : function(e) {
7210 var E = YAHOO.util.Event,
7211 KEYS = YAHOO.util.KeyListener.KEY,
7212 NAV = YAHOO.widget.CalendarNavigator;
7214 var value = (this.yearEl.value) ? parseInt(this.yearEl.value, 10) : null;
7215 if (isFinite(value)) {
7217 switch(E.getCharCode(e)) {
7219 this.yearEl.value = value + NAV.YR_MINOR_INC;
7223 this.yearEl.value = Math.max(value - NAV.YR_MINOR_INC, 0);
7227 this.yearEl.value = value + NAV.YR_MAJOR_INC;
7230 case KEYS.PAGE_DOWN:
7231 this.yearEl.value = Math.max(value - NAV.YR_MAJOR_INC, 0);
7238 E.preventDefault(e);
7240 this.yearEl.select();
7249 * Default Keyboard event handler to capture Tab
7250 * on the last control (lastCtrl) in the Navigator.
7252 * @method _handleTabKey
7254 * @param {Event} e The DOM event being handled
7256 _handleTabKey : function(e) {
7257 var E = YAHOO.util.Event,
7258 KEYS = YAHOO.util.KeyListener.KEY;
7260 if (E.getCharCode(e) == KEYS.TAB && !e.shiftKey) {
7262 E.preventDefault(e);
7263 this.firstCtrl.focus();
7265 // Ignore - mainly for focus edge cases
7271 * Default Keyboard event handler to capture Shift-Tab
7272 * on the first control (firstCtrl) in the Navigator.
7274 * @method _handleShiftTabKey
7276 * @param {Event} e The DOM event being handled
7278 _handleShiftTabKey : function(e) {
7279 var E = YAHOO.util.Event,
7280 KEYS = YAHOO.util.KeyListener.KEY;
7282 if (e.shiftKey && E.getCharCode(e) == KEYS.TAB) {
7284 E.preventDefault(e);
7285 this.lastCtrl.focus();
7287 // Ignore - mainly for focus edge cases
7293 * Retrieve Navigator configuration values from
7294 * the parent Calendar/CalendarGroup's config value.
7296 * If it has not been set in the user provided configuration, the method will
7297 * return the default value of the configuration property, as set in DEFAULT_CONFIG
7301 * @param {String} Case sensitive property name.
7302 * @param {Boolean} true, if the property is a string property, false if not.
7303 * @return The value of the configuration property
7305 __getCfg : function(prop, bIsStr) {
7306 var DEF_CFG = YAHOO.widget.CalendarNavigator.DEFAULT_CONFIG;
7307 var cfg = this.cal.cfg.getProperty("navigator");
7310 return (cfg !== true && cfg.strings && cfg.strings[prop]) ? cfg.strings[prop] : DEF_CFG.strings[prop];
7312 return (cfg !== true && cfg[prop]) ? cfg[prop] : DEF_CFG[prop];
7317 * Private flag, to identify MacOS
7321 __isMac : (navigator.userAgent.toLowerCase().indexOf("macintosh") != -1)
7324 YAHOO.register("calendar", YAHOO.widget.Calendar, {version: "2.8.0r4", build: "2449"});