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) {
30 var Lang = YAHOO.lang,
31 CustomEvent = YAHOO.util.CustomEvent,
32 Config = YAHOO.util.Config;
36 * Constant representing the CustomEvent type for the config changed event.
37 * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT
42 Config.CONFIG_CHANGED_EVENT = "configChanged";
45 * Constant representing the boolean type string
46 * @property YAHOO.util.Config.BOOLEAN_TYPE
51 Config.BOOLEAN_TYPE = "boolean";
56 * Object reference to the owner of this Config Object
63 * Boolean flag that specifies whether a queue is currently
65 * @property queueInProgress
68 queueInProgress: false,
71 * Maintains the local collection of configuration property objects and
72 * their specified values
80 * Maintains the local collection of configuration property objects as
81 * they were initially applied.
82 * This object is used when resetting a property.
83 * @property initialConfig
90 * Maintains the local, normalized CustomEvent queue
91 * @property eventQueue
98 * Custom Event, notifying subscribers when Config properties are set
99 * (setProperty is called without the silent flag
100 * @event configChangedEvent
102 configChangedEvent: null,
105 * Initializes the configuration Object and all of its local members.
107 * @param {Object} owner The owner Object to which this Config
110 init: function (owner) {
114 this.configChangedEvent =
115 this.createEvent(Config.CONFIG_CHANGED_EVENT);
117 this.configChangedEvent.signature = CustomEvent.LIST;
118 this.queueInProgress = false;
120 this.initialConfig = {};
121 this.eventQueue = [];
126 * Validates that the value passed in is a Boolean.
127 * @method checkBoolean
128 * @param {Object} val The value to validate
129 * @return {Boolean} true, if the value is valid
131 checkBoolean: function (val) {
132 return (typeof val == Config.BOOLEAN_TYPE);
136 * Validates that the value passed in is a number.
137 * @method checkNumber
138 * @param {Object} val The value to validate
139 * @return {Boolean} true, if the value is valid
141 checkNumber: function (val) {
142 return (!isNaN(val));
146 * Fires a configuration property event using the specified value.
149 * @param {String} key The configuration property's name
150 * @param {value} Object The value of the correct type for the property
152 fireEvent: function ( key, value ) {
153 var property = this.config[key];
155 if (property && property.event) {
156 property.event.fire(value);
161 * Adds a property to the Config Object's private config hash.
162 * @method addProperty
163 * @param {String} key The configuration property's name
164 * @param {Object} propertyObject The Object containing all of this
165 * property's arguments
167 addProperty: function ( key, propertyObject ) {
168 key = key.toLowerCase();
170 this.config[key] = propertyObject;
172 propertyObject.event = this.createEvent(key, { scope: this.owner });
173 propertyObject.event.signature = CustomEvent.LIST;
176 propertyObject.key = key;
178 if (propertyObject.handler) {
179 propertyObject.event.subscribe(propertyObject.handler,
183 this.setProperty(key, propertyObject.value, true);
185 if (! propertyObject.suppressEvent) {
186 this.queueProperty(key, propertyObject.value);
192 * Returns a key-value configuration map of the values currently set in
195 * @return {Object} The current config, represented in a key-value map
197 getConfig: function () {
200 currCfg = this.config,
204 for (prop in currCfg) {
205 if (Lang.hasOwnProperty(currCfg, prop)) {
206 property = currCfg[prop];
207 if (property && property.event) {
208 cfg[prop] = property.value;
217 * Returns the value of specified property.
218 * @method getProperty
219 * @param {String} key The name of the property
220 * @return {Object} The value of the specified property
222 getProperty: function (key) {
223 var property = this.config[key.toLowerCase()];
224 if (property && property.event) {
225 return property.value;
232 * Resets the specified property's value to its initial value.
233 * @method resetProperty
234 * @param {String} key The name of the property
235 * @return {Boolean} True is the property was reset, false if not
237 resetProperty: function (key) {
239 key = key.toLowerCase();
241 var property = this.config[key];
243 if (property && property.event) {
245 if (this.initialConfig[key] &&
246 !Lang.isUndefined(this.initialConfig[key])) {
248 this.setProperty(key, this.initialConfig[key]);
262 * Sets the value of a property. If the silent property is passed as
263 * true, the property's event will not be fired.
264 * @method setProperty
265 * @param {String} key The name of the property
266 * @param {String} value The value to set the property to
267 * @param {Boolean} silent Whether the value should be set silently,
268 * without firing the property event.
269 * @return {Boolean} True, if the set was successful, false if it failed.
271 setProperty: function (key, value, silent) {
275 key = key.toLowerCase();
277 if (this.queueInProgress && ! silent) {
278 // Currently running through a queue...
279 this.queueProperty(key,value);
283 property = this.config[key];
284 if (property && property.event) {
285 if (property.validator && !property.validator(value)) {
288 property.value = value;
290 this.fireEvent(key, value);
291 this.configChangedEvent.fire([key, value]);
302 * Sets the value of a property and queues its event to execute. If the
303 * event is already scheduled to execute, it is
304 * moved from its current position to the end of the queue.
305 * @method queueProperty
306 * @param {String} key The name of the property
307 * @param {String} value The value to set the property to
308 * @return {Boolean} true, if the set was successful, false if
311 queueProperty: function (key, value) {
313 key = key.toLowerCase();
315 var property = this.config[key],
316 foundDuplicate = false,
331 if (property && property.event) {
333 if (!Lang.isUndefined(value) && property.validator &&
334 !property.validator(value)) { // validator
338 if (!Lang.isUndefined(value)) {
339 property.value = value;
341 value = property.value;
344 foundDuplicate = false;
345 iLen = this.eventQueue.length;
347 for (i = 0; i < iLen; i++) {
348 queueItem = this.eventQueue[i];
351 queueItemKey = queueItem[0];
352 queueItemValue = queueItem[1];
354 if (queueItemKey == key) {
357 found a dupe... push to end of queue, null
358 current item, and break
361 this.eventQueue[i] = null;
363 this.eventQueue.push(
364 [key, (!Lang.isUndefined(value) ?
365 value : queueItemValue)]);
367 foundDuplicate = true;
373 // this is a refire, or a new property in the queue
375 if (! foundDuplicate && !Lang.isUndefined(value)) {
376 this.eventQueue.push([key, value]);
380 if (property.supercedes) {
382 sLen = property.supercedes.length;
384 for (s = 0; s < sLen; s++) {
386 supercedesCheck = property.supercedes[s];
387 qLen = this.eventQueue.length;
389 for (q = 0; q < qLen; q++) {
390 queueItemCheck = this.eventQueue[q];
392 if (queueItemCheck) {
393 queueItemCheckKey = queueItemCheck[0];
394 queueItemCheckValue = queueItemCheck[1];
396 if (queueItemCheckKey ==
397 supercedesCheck.toLowerCase() ) {
399 this.eventQueue.push([queueItemCheckKey,
400 queueItemCheckValue]);
402 this.eventQueue[q] = null;
419 * Fires the event for a property using the property's current value.
420 * @method refireEvent
421 * @param {String} key The name of the property
423 refireEvent: function (key) {
425 key = key.toLowerCase();
427 var property = this.config[key];
429 if (property && property.event &&
431 !Lang.isUndefined(property.value)) {
433 if (this.queueInProgress) {
435 this.queueProperty(key);
439 this.fireEvent(key, property.value);
447 * Applies a key-value Object literal to the configuration, replacing
448 * any existing values, and queueing the property events.
449 * Although the values will be set, fireQueue() must be called for their
450 * associated events to execute.
451 * @method applyConfig
452 * @param {Object} userConfig The configuration Object literal
453 * @param {Boolean} init When set to true, the initialConfig will
454 * be set to the userConfig passed in, so that calling a reset will
455 * reset the properties to the passed values.
457 applyConfig: function (userConfig, init) {
464 for (sKey in userConfig) {
465 if (Lang.hasOwnProperty(userConfig, sKey)) {
466 oConfig[sKey.toLowerCase()] = userConfig[sKey];
469 this.initialConfig = oConfig;
472 for (sKey in userConfig) {
473 if (Lang.hasOwnProperty(userConfig, sKey)) {
474 this.queueProperty(sKey, userConfig[sKey]);
480 * Refires the events for all configuration properties using their
484 refresh: function () {
488 for (prop in this.config) {
489 if (Lang.hasOwnProperty(this.config, prop)) {
490 this.refireEvent(prop);
496 * Fires the normalized list of queued property change events
499 fireQueue: function () {
507 this.queueInProgress = true;
508 for (i = 0;i < this.eventQueue.length; i++) {
509 queueItem = this.eventQueue[i];
513 value = queueItem[1];
514 property = this.config[key];
516 property.value = value;
518 // Clear out queue entry, to avoid it being
519 // re-added to the queue by any queueProperty/supercedes
520 // calls which are invoked during fireEvent
521 this.eventQueue[i] = null;
523 this.fireEvent(key,value);
527 this.queueInProgress = false;
528 this.eventQueue = [];
532 * Subscribes an external handler to the change event for any
534 * @method subscribeToConfigEvent
535 * @param {String} key The property name
536 * @param {Function} handler The handler function to use subscribe to
537 * the property's event
538 * @param {Object} obj The Object to use for scoping the event handler
539 * (see CustomEvent documentation)
540 * @param {Boolean} overrideContext Optional. If true, will override
541 * "this" within the handler to map to the scope Object passed into the
543 * @return {Boolean} True, if the subscription was successful,
546 subscribeToConfigEvent: function (key, handler, obj, overrideContext) {
548 var property = this.config[key.toLowerCase()];
550 if (property && property.event) {
551 if (!Config.alreadySubscribed(property.event, handler, obj)) {
552 property.event.subscribe(handler, obj, overrideContext);
562 * Unsubscribes an external handler from the change event for any
564 * @method unsubscribeFromConfigEvent
565 * @param {String} key The property name
566 * @param {Function} handler The handler function to use subscribe to
567 * the property's event
568 * @param {Object} obj The Object to use for scoping the event
569 * handler (see CustomEvent documentation)
570 * @return {Boolean} True, if the unsubscription was successful,
573 unsubscribeFromConfigEvent: function (key, handler, obj) {
574 var property = this.config[key.toLowerCase()];
575 if (property && property.event) {
576 return property.event.unsubscribe(handler, obj);
583 * Returns a string representation of the Config object
585 * @return {String} The Config object in string format.
587 toString: function () {
588 var output = "Config";
590 output += " [" + this.owner.toString() + "]";
596 * Returns a string representation of the Config object's current
598 * @method outputEventQueue
599 * @return {String} The string list of CustomEvents currently queued
602 outputEventQueue: function () {
607 nQueue = this.eventQueue.length;
609 for (q = 0; q < nQueue; q++) {
610 queueItem = this.eventQueue[q];
612 output += queueItem[0] + "=" + queueItem[1] + ", ";
619 * Sets all properties to null, unsubscribes all listeners from each
620 * property's change event and all listeners from the configChangedEvent.
623 destroy: function () {
625 var oConfig = this.config,
630 for (sProperty in oConfig) {
632 if (Lang.hasOwnProperty(oConfig, sProperty)) {
634 oProperty = oConfig[sProperty];
636 oProperty.event.unsubscribeAll();
637 oProperty.event = null;
643 this.configChangedEvent.unsubscribeAll();
645 this.configChangedEvent = null;
648 this.initialConfig = null;
649 this.eventQueue = null;
658 * Checks to determine if a particular function/Object pair are already
659 * subscribed to the specified CustomEvent
660 * @method YAHOO.util.Config.alreadySubscribed
662 * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check
664 * @param {Function} fn The function to look for in the subscribers list
665 * @param {Object} obj The execution scope Object for the subscription
666 * @return {Boolean} true, if the function/Object pair is already subscribed
667 * to the CustomEvent passed in
669 Config.alreadySubscribed = function (evt, fn, obj) {
671 var nSubscribers = evt.subscribers.length,
675 if (nSubscribers > 0) {
676 i = nSubscribers - 1;
678 subsc = evt.subscribers[i];
679 if (subsc && subsc.obj == obj && subsc.fn == fn) {
690 YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider);
694 * The datemath module provides utility methods for basic JavaScript Date object manipulation and
701 * YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility
702 * used for adding, subtracting, and comparing dates.
703 * @namespace YAHOO.widget
706 YAHOO.widget.DateMath = {
708 * Constant field representing Day
717 * Constant field representing Week
726 * Constant field representing Year
735 * Constant field representing Month
744 * Constant field representing one day, in milliseconds
745 * @property ONE_DAY_MS
750 ONE_DAY_MS : 1000*60*60*24,
753 * Constant field representing the date in first week of January
754 * which identifies the first week of the year.
756 * In the U.S, Jan 1st is normally used based on a Sunday start of week.
757 * ISO 8601, used widely throughout Europe, uses Jan 4th, based on a Monday start of week.
759 * @property WEEK_ONE_JAN_DATE
763 WEEK_ONE_JAN_DATE : 1,
766 * Adds the specified amount of time to the this instance.
768 * @param {Date} date The JavaScript Date object to perform addition on
769 * @param {String} field The field constant to be used for performing addition.
770 * @param {Number} amount The number of units (measured in the field constant) to add to the date.
771 * @return {Date} The resulting Date object
773 add : function(date, field, amount) {
774 var d = new Date(date.getTime());
777 var newMonth = date.getMonth() + amount;
781 while (newMonth < 0) {
785 } else if (newMonth > 11) {
786 while (newMonth > 11) {
792 d.setMonth(newMonth);
793 d.setFullYear(date.getFullYear() + years);
796 this._addDays(d, amount);
797 // d.setDate(date.getDate() + amount);
800 d.setFullYear(date.getFullYear() + amount);
803 this._addDays(d, (amount * 7));
804 // d.setDate(date.getDate() + (amount * 7));
811 * Private helper method to account for bug in Safari 2 (webkit < 420)
812 * when Date.setDate(n) is called with n less than -128 or greater than 127.
814 * Fix approach and original findings are available here:
815 * http://brianary.blogspot.com/2006/03/safari-date-bug.html
818 * @param {Date} d JavaScript date object
819 * @param {Number} nDays The number of days to add to the date object (can be negative)
822 _addDays : function(d, nDays) {
823 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420) {
825 // Ensure we don't go below -128 (getDate() is always 1 to 31, so we won't go above 127)
826 for(var min = -128; nDays < min; nDays -= min) {
827 d.setDate(d.getDate() + min);
830 // Ensure we don't go above 96 + 31 = 127
831 for(var max = 96; nDays > max; nDays -= max) {
832 d.setDate(d.getDate() + max);
835 // nDays should be remainder between -128 and 96
837 d.setDate(d.getDate() + nDays);
841 * Subtracts the specified amount of time from the this instance.
843 * @param {Date} date The JavaScript Date object to perform subtraction on
844 * @param {Number} field The this field constant to be used for performing subtraction.
845 * @param {Number} amount The number of units (measured in the field constant) to subtract from the date.
846 * @return {Date} The resulting Date object
848 subtract : function(date, field, amount) {
849 return this.add(date, field, (amount*-1));
853 * Determines whether a given date is before another date on the calendar.
855 * @param {Date} date The Date object to compare with the compare argument
856 * @param {Date} compareTo The Date object to use for the comparison
857 * @return {Boolean} true if the date occurs before the compared date; false if not.
859 before : function(date, compareTo) {
860 var ms = compareTo.getTime();
861 if (date.getTime() < ms) {
869 * Determines whether a given date is after another date on the calendar.
871 * @param {Date} date The Date object to compare with the compare argument
872 * @param {Date} compareTo The Date object to use for the comparison
873 * @return {Boolean} true if the date occurs after the compared date; false if not.
875 after : function(date, compareTo) {
876 var ms = compareTo.getTime();
877 if (date.getTime() > ms) {
885 * Determines whether a given date is between two other dates on the calendar.
887 * @param {Date} date The date to check for
888 * @param {Date} dateBegin The start of the range
889 * @param {Date} dateEnd The end of the range
890 * @return {Boolean} true if the date occurs between the compared dates; false if not.
892 between : function(date, dateBegin, dateEnd) {
893 if (this.after(date, dateBegin) && this.before(date, dateEnd)) {
901 * Retrieves a JavaScript Date object representing January 1 of any given year.
903 * @param {Number} calendarYear The calendar year for which to retrieve January 1
904 * @return {Date} January 1 of the calendar year specified.
906 getJan1 : function(calendarYear) {
907 return this.getDate(calendarYear,0,1);
911 * Calculates the number of days the specified date is from January 1 of the specified calendar year.
912 * Passing January 1 to this function would return an offset value of zero.
913 * @method getDayOffset
914 * @param {Date} date The JavaScript date for which to find the offset
915 * @param {Number} calendarYear The calendar year to use for determining the offset
916 * @return {Number} The number of days since January 1 of the given year
918 getDayOffset : function(date, calendarYear) {
919 var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1.
921 // Find the number of days the passed in date is away from the calendar year start
922 var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS);
927 * Calculates the week number for the given date. Can currently support standard
928 * U.S. week numbers, based on Jan 1st defining the 1st week of the year, and
929 * ISO8601 week numbers, based on Jan 4th defining the 1st week of the year.
931 * @method getWeekNumber
932 * @param {Date} date The JavaScript date for which to find the week number
933 * @param {Number} firstDayOfWeek The index of the first day of the week (0 = Sun, 1 = Mon ... 6 = Sat).
935 * @param {Number} janDate The date in the first week of January which defines week one for the year
936 * Defaults to the value of YAHOO.widget.DateMath.WEEK_ONE_JAN_DATE, which is 1 (Jan 1st).
937 * For the U.S, this is normally Jan 1st. ISO8601 uses Jan 4th to define the first week of the year.
939 * @return {Number} The number of the week containing the given date.
941 getWeekNumber : function(date, firstDayOfWeek, janDate) {
944 firstDayOfWeek = firstDayOfWeek || 0;
945 janDate = janDate || this.WEEK_ONE_JAN_DATE;
947 var targetDate = this.clearTime(date),
951 if (targetDate.getDay() === firstDayOfWeek) {
952 startOfWeek = targetDate;
954 startOfWeek = this.getFirstDayOfWeek(targetDate, firstDayOfWeek);
957 var startYear = startOfWeek.getFullYear();
959 // DST shouldn't be a problem here, math is quicker than setDate();
960 endOfWeek = new Date(startOfWeek.getTime() + 6*this.ONE_DAY_MS);
963 if (startYear !== endOfWeek.getFullYear() && endOfWeek.getDate() >= janDate) {
964 // If years don't match, endOfWeek is in Jan. and if the
965 // week has WEEK_ONE_JAN_DATE in it, it's week one by definition.
968 // Get the 1st day of the 1st week, and
969 // find how many days away we are from it.
970 var weekOne = this.clearTime(this.getDate(startYear, 0, janDate)),
971 weekOneDayOne = this.getFirstDayOfWeek(weekOne, firstDayOfWeek);
973 // Round days to smoothen out 1 hr DST diff
974 var daysDiff = Math.round((targetDate.getTime() - weekOneDayOne.getTime())/this.ONE_DAY_MS);
977 var rem = daysDiff % 7;
978 var weeksDiff = (daysDiff - rem)/7;
979 weekNum = weeksDiff + 1;
985 * Get the first day of the week, for the give date.
986 * @param {Date} dt The date in the week for which the first day is required.
987 * @param {Number} startOfWeek The index for the first day of the week, 0 = Sun, 1 = Mon ... 6 = Sat (defaults to 0)
988 * @return {Date} The first day of the week
990 getFirstDayOfWeek : function (dt, startOfWeek) {
991 startOfWeek = startOfWeek || 0;
992 var dayOfWeekIndex = dt.getDay(),
993 dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7;
995 return this.subtract(dt, this.DAY, dayOfWeek);
999 * Determines if a given week overlaps two different years.
1000 * @method isYearOverlapWeek
1001 * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week.
1002 * @return {Boolean} true if the date overlaps two different years.
1004 isYearOverlapWeek : function(weekBeginDate) {
1005 var overlaps = false;
1006 var nextWeek = this.add(weekBeginDate, this.DAY, 6);
1007 if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) {
1014 * Determines if a given week overlaps two different months.
1015 * @method isMonthOverlapWeek
1016 * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week.
1017 * @return {Boolean} true if the date overlaps two different months.
1019 isMonthOverlapWeek : function(weekBeginDate) {
1020 var overlaps = false;
1021 var nextWeek = this.add(weekBeginDate, this.DAY, 6);
1022 if (nextWeek.getMonth() != weekBeginDate.getMonth()) {
1029 * Gets the first day of a month containing a given date.
1030 * @method findMonthStart
1031 * @param {Date} date The JavaScript Date used to calculate the month start
1032 * @return {Date} The JavaScript Date representing the first day of the month
1034 findMonthStart : function(date) {
1035 var start = this.getDate(date.getFullYear(), date.getMonth(), 1);
1040 * Gets the last day of a month containing a given date.
1041 * @method findMonthEnd
1042 * @param {Date} date The JavaScript Date used to calculate the month end
1043 * @return {Date} The JavaScript Date representing the last day of the month
1045 findMonthEnd : function(date) {
1046 var start = this.findMonthStart(date);
1047 var nextMonth = this.add(start, this.MONTH, 1);
1048 var end = this.subtract(nextMonth, this.DAY, 1);
1053 * Clears the time fields from a given date, effectively setting the time to 12 noon.
1055 * @param {Date} date The JavaScript Date for which the time fields will be cleared
1056 * @return {Date} The JavaScript Date cleared of all time fields
1058 clearTime : function(date) {
1059 date.setHours(12,0,0,0);
1064 * Returns a new JavaScript Date object, representing the given year, month and date. Time fields (hr, min, sec, ms) on the new Date object
1065 * 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
1066 * set the year to 19xx if a year (xx) which is less than 100 is provided.
1068 * <em>NOTE:</em>Validation on argument values is not performed. It is the caller's responsibility to ensure
1069 * arguments are valid as per the ECMAScript-262 Date object specification for the new Date(year, month[, date]) constructor.
1072 * @param {Number} y Year.
1073 * @param {Number} m Month index from 0 (Jan) to 11 (Dec).
1074 * @param {Number} d (optional) Date from 1 to 31. If not provided, defaults to 1.
1075 * @return {Date} The JavaScript date object with year, month, date set as provided.
1077 getDate : function(y, m, d) {
1079 if (YAHOO.lang.isUndefined(d)) {
1083 dt = new Date(y, m, d);
1089 dt.setHours(0,0,0,0);
1095 * 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
1096 * multi-month interface. Calendars are generated entirely via script and can be navigated without any page refreshes.
1099 * @namespace YAHOO.widget
1100 * @requires yahoo,dom,event
1104 var Dom = YAHOO.util.Dom,
1105 Event = YAHOO.util.Event,
1107 DateMath = YAHOO.widget.DateMath;
1110 * Calendar is the base class for the Calendar widget. In its most basic
1111 * implementation, it has the ability to render a calendar widget on the page
1112 * that can be manipulated to select a single date, move back and forth between
1114 * <p>To construct the placeholder for the calendar widget, the code is as
1117 * <div id="calContainer"></div>
1121 * <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
1122 * The Calendar can be constructed by simply providing a container ID string,
1123 * or a reference to a container DIV HTMLElement (the element needs to exist
1128 * var c = new YAHOO.widget.Calendar("calContainer", configOptions);
1132 * var containerDiv = YAHOO.util.Dom.get("calContainer");
1133 * var c = new YAHOO.widget.Calendar(containerDiv, configOptions);
1137 * If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
1138 * 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".
1141 * @namespace YAHOO.widget
1144 * @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.
1145 * @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.
1146 * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
1148 function Calendar(id, containerId, config) {
1149 this.init.apply(this, arguments);
1153 * The path to be used for images loaded for the Calendar
1154 * @property YAHOO.widget.Calendar.IMG_ROOT
1156 * @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
1159 Calendar.IMG_ROOT = null;
1162 * Type constant used for renderers to represent an individual date (M/D/Y)
1163 * @property YAHOO.widget.Calendar.DATE
1168 Calendar.DATE = "D";
1171 * Type constant used for renderers to represent an individual date across any year (M/D)
1172 * @property YAHOO.widget.Calendar.MONTH_DAY
1177 Calendar.MONTH_DAY = "MD";
1180 * Type constant used for renderers to represent a weekday
1181 * @property YAHOO.widget.Calendar.WEEKDAY
1186 Calendar.WEEKDAY = "WD";
1189 * Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
1190 * @property YAHOO.widget.Calendar.RANGE
1195 Calendar.RANGE = "R";
1198 * Type constant used for renderers to represent a month across any year
1199 * @property YAHOO.widget.Calendar.MONTH
1204 Calendar.MONTH = "M";
1207 * Constant that represents the total number of date cells that are displayed in a given month
1208 * @property YAHOO.widget.Calendar.DISPLAY_DAYS
1213 Calendar.DISPLAY_DAYS = 42;
1216 * Constant used for halting the execution of the remainder of the render stack
1217 * @property YAHOO.widget.Calendar.STOP_RENDER
1222 Calendar.STOP_RENDER = "S";
1225 * Constant used to represent short date field string formats (e.g. Tu or Feb)
1226 * @property YAHOO.widget.Calendar.SHORT
1231 Calendar.SHORT = "short";
1234 * Constant used to represent long date field string formats (e.g. Monday or February)
1235 * @property YAHOO.widget.Calendar.LONG
1240 Calendar.LONG = "long";
1243 * Constant used to represent medium date field string formats (e.g. Mon)
1244 * @property YAHOO.widget.Calendar.MEDIUM
1249 Calendar.MEDIUM = "medium";
1252 * Constant used to represent single character date field string formats (e.g. M, T, W)
1253 * @property YAHOO.widget.Calendar.ONE_CHAR
1258 Calendar.ONE_CHAR = "1char";
1261 * The set of default Config property keys and values for the Calendar.
1264 * NOTE: This property is made public in order to allow users to change
1265 * the default values of configuration properties. Users should not
1266 * modify the key string, unless they are overriding the Calendar implementation
1270 * The property is an object with key/value pairs, the key being the
1271 * uppercase configuration property name and the value being an object
1272 * literal with a key string property, and a value property, specifying the
1273 * default value of the property. To override a default value, you can set
1274 * the value property, for example, <code>YAHOO.widget.Calendar.DEFAULT_CONFIG.MULTI_SELECT.value = true;</code>
1276 * @property YAHOO.widget.Calendar.DEFAULT_CONFIG
1281 Calendar.DEFAULT_CONFIG = {
1282 YEAR_OFFSET : {key:"year_offset", value:0, supercedes:["pagedate", "selected", "mindate","maxdate"]},
1283 TODAY : {key:"today", value:new Date(), supercedes:["pagedate"]},
1284 PAGEDATE : {key:"pagedate", value:null},
1285 SELECTED : {key:"selected", value:[]},
1286 TITLE : {key:"title", value:""},
1287 CLOSE : {key:"close", value:false},
1288 IFRAME : {key:"iframe", value:(YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) ? true : false},
1289 MINDATE : {key:"mindate", value:null},
1290 MAXDATE : {key:"maxdate", value:null},
1291 MULTI_SELECT : {key:"multi_select", value:false},
1292 START_WEEKDAY : {key:"start_weekday", value:0},
1293 SHOW_WEEKDAYS : {key:"show_weekdays", value:true},
1294 SHOW_WEEK_HEADER : {key:"show_week_header", value:false},
1295 SHOW_WEEK_FOOTER : {key:"show_week_footer", value:false},
1296 HIDE_BLANK_WEEKS : {key:"hide_blank_weeks", value:false},
1297 NAV_ARROW_LEFT: {key:"nav_arrow_left", value:null} ,
1298 NAV_ARROW_RIGHT : {key:"nav_arrow_right", value:null} ,
1299 MONTHS_SHORT : {key:"months_short", value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]},
1300 MONTHS_LONG: {key:"months_long", value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]},
1301 WEEKDAYS_1CHAR: {key:"weekdays_1char", value:["S", "M", "T", "W", "T", "F", "S"]},
1302 WEEKDAYS_SHORT: {key:"weekdays_short", value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]},
1303 WEEKDAYS_MEDIUM: {key:"weekdays_medium", value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]},
1304 WEEKDAYS_LONG: {key:"weekdays_long", value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]},
1305 LOCALE_MONTHS:{key:"locale_months", value:"long"},
1306 LOCALE_WEEKDAYS:{key:"locale_weekdays", value:"short"},
1307 DATE_DELIMITER:{key:"date_delimiter", value:","},
1308 DATE_FIELD_DELIMITER:{key:"date_field_delimiter", value:"/"},
1309 DATE_RANGE_DELIMITER:{key:"date_range_delimiter", value:"-"},
1310 MY_MONTH_POSITION:{key:"my_month_position", value:1},
1311 MY_YEAR_POSITION:{key:"my_year_position", value:2},
1312 MD_MONTH_POSITION:{key:"md_month_position", value:1},
1313 MD_DAY_POSITION:{key:"md_day_position", value:2},
1314 MDY_MONTH_POSITION:{key:"mdy_month_position", value:1},
1315 MDY_DAY_POSITION:{key:"mdy_day_position", value:2},
1316 MDY_YEAR_POSITION:{key:"mdy_year_position", value:3},
1317 MY_LABEL_MONTH_POSITION:{key:"my_label_month_position", value:1},
1318 MY_LABEL_YEAR_POSITION:{key:"my_label_year_position", value:2},
1319 MY_LABEL_MONTH_SUFFIX:{key:"my_label_month_suffix", value:" "},
1320 MY_LABEL_YEAR_SUFFIX:{key:"my_label_year_suffix", value:""},
1321 NAV: {key:"navigator", value: null},
1325 previousMonth : "Previous Month",
1326 nextMonth : "Next Month",
1329 supercedes : ["close", "title"]
1334 * The set of default Config property keys and values for the Calendar
1335 * @property YAHOO.widget.Calendar._DEFAULT_CONFIG
1336 * @deprecated Made public. See the public DEFAULT_CONFIG property for details
1342 Calendar._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG;
1344 var DEF_CFG = Calendar.DEFAULT_CONFIG;
1347 * The set of Custom Event types supported by the Calendar
1348 * @property YAHOO.widget.Calendar._EVENT_TYPES
1354 Calendar._EVENT_TYPES = {
1355 BEFORE_SELECT : "beforeSelect",
1357 BEFORE_DESELECT : "beforeDeselect",
1358 DESELECT : "deselect",
1359 CHANGE_PAGE : "changePage",
1360 BEFORE_RENDER : "beforeRender",
1362 BEFORE_DESTROY : "beforeDestroy",
1363 DESTROY : "destroy",
1366 BEFORE_HIDE : "beforeHide",
1368 BEFORE_SHOW : "beforeShow",
1370 BEFORE_HIDE_NAV : "beforeHideNav",
1371 HIDE_NAV : "hideNav",
1372 BEFORE_SHOW_NAV : "beforeShowNav",
1373 SHOW_NAV : "showNav",
1374 BEFORE_RENDER_NAV : "beforeRenderNav",
1375 RENDER_NAV : "renderNav"
1379 * The set of default style constants for the Calendar
1380 * @property YAHOO.widget.Calendar.STYLES
1382 * @type Object An object with name/value pairs for the class name identifier/value.
1385 CSS_ROW_HEADER: "calrowhead",
1386 CSS_ROW_FOOTER: "calrowfoot",
1387 CSS_CELL : "calcell",
1388 CSS_CELL_SELECTOR : "selector",
1389 CSS_CELL_SELECTED : "selected",
1390 CSS_CELL_SELECTABLE : "selectable",
1391 CSS_CELL_RESTRICTED : "restricted",
1392 CSS_CELL_TODAY : "today",
1393 CSS_CELL_OOM : "oom",
1394 CSS_CELL_OOB : "previous",
1395 CSS_HEADER : "calheader",
1396 CSS_HEADER_TEXT : "calhead",
1397 CSS_BODY : "calbody",
1398 CSS_WEEKDAY_CELL : "calweekdaycell",
1399 CSS_WEEKDAY_ROW : "calweekdayrow",
1400 CSS_FOOTER : "calfoot",
1401 CSS_CALENDAR : "yui-calendar",
1402 CSS_SINGLE : "single",
1403 CSS_CONTAINER : "yui-calcontainer",
1404 CSS_NAV_LEFT : "calnavleft",
1405 CSS_NAV_RIGHT : "calnavright",
1407 CSS_CLOSE : "calclose",
1408 CSS_CELL_TOP : "calcelltop",
1409 CSS_CELL_LEFT : "calcellleft",
1410 CSS_CELL_RIGHT : "calcellright",
1411 CSS_CELL_BOTTOM : "calcellbottom",
1412 CSS_CELL_HOVER : "calcellhover",
1413 CSS_CELL_HIGHLIGHT1 : "highlight1",
1414 CSS_CELL_HIGHLIGHT2 : "highlight2",
1415 CSS_CELL_HIGHLIGHT3 : "highlight3",
1416 CSS_CELL_HIGHLIGHT4 : "highlight4",
1417 CSS_WITH_TITLE: "withtitle",
1418 CSS_FIXED_SIZE: "fixedsize",
1419 CSS_LINK_CLOSE: "link-close"
1423 * The set of default style constants for the Calendar
1424 * @property YAHOO.widget.Calendar._STYLES
1425 * @deprecated Made public. See the public STYLES property for details
1431 Calendar._STYLES = Calendar.STYLES;
1433 Calendar.prototype = {
1436 * The configuration object used to set up the calendars various locale and style options.
1439 * @deprecated Configuration properties should be set by calling Calendar.cfg.setProperty.
1445 * The parent CalendarGroup, only to be set explicitly by the parent group
1447 * @type CalendarGroup
1452 * The index of this item in the parent group
1459 * The collection of calendar table cells
1461 * @type HTMLTableCellElement[]
1466 * 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].
1467 * @property cellDates
1468 * @type Array[](Number[])
1473 * The id that uniquely identifies this Calendar.
1480 * The unique id associated with the Calendar's container
1481 * @property containerId
1487 * 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.
1488 * @property oDomContainer
1491 oDomContainer : null,
1494 * A Date object representing today's date.
1495 * @deprecated Use the "today" configuration property
1502 * The list of render functions, along with required parameters, used to render cells.
1503 * @property renderStack
1509 * A copy of the initial render functions created before rendering.
1510 * @property _renderStack
1514 _renderStack : null,
1517 * A reference to the CalendarNavigator instance created for this Calendar.
1518 * Will be null if the "navigator" configuration property has not been set
1519 * @property oNavigator
1520 * @type CalendarNavigator
1525 * The private list of initially selected dates.
1526 * @property _selectedDates
1530 _selectedDates : null,
1533 * A map of DOM event handlers to attach to cells associated with specific CSS class names
1534 * @property domEventMap
1540 * Protected helper used to parse Calendar constructor/init arguments.
1542 * As of 2.4.0, Calendar supports a simpler constructor
1543 * signature. This method reconciles arguments
1544 * received in the pre 2.4.0 and 2.4.0 formats.
1547 * @method _parseArgs
1548 * @param {Array} Function "arguments" array
1549 * @return {Object} Object with id, container, config properties containing
1550 * the reconciled argument values.
1552 _parseArgs : function(args) {
1554 2.4.0 Constructors signatures
1556 new Calendar(String)
1557 new Calendar(HTMLElement)
1558 new Calendar(String, ConfigObject)
1559 new Calendar(HTMLElement, ConfigObject)
1561 Pre 2.4.0 Constructor signatures
1563 new Calendar(String, String)
1564 new Calendar(String, HTMLElement)
1565 new Calendar(String, String, ConfigObject)
1566 new Calendar(String, HTMLElement, ConfigObject)
1568 var nArgs = {id:null, container:null, config:null};
1570 if (args && args.length && args.length > 0) {
1571 switch (args.length) {
1574 nArgs.container = args[0];
1575 nArgs.config = null;
1578 if (Lang.isObject(args[1]) && !args[1].tagName && !(args[1] instanceof String)) {
1580 nArgs.container = args[0];
1581 nArgs.config = args[1];
1584 nArgs.container = args[1];
1585 nArgs.config = null;
1590 nArgs.container = args[1];
1591 nArgs.config = args[2];
1600 * Initializes the Calendar widget.
1603 * @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.
1604 * @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.
1605 * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
1607 init : function(id, container, config) {
1608 // Normalize 2.4.0, pre 2.4.0 args
1609 var nArgs = this._parseArgs(arguments);
1612 container = nArgs.container;
1613 config = nArgs.config;
1615 this.oDomContainer = Dom.get(container);
1617 if (!this.oDomContainer.id) {
1618 this.oDomContainer.id = Dom.generateId();
1621 id = this.oDomContainer.id + "_t";
1625 this.containerId = this.oDomContainer.id;
1630 * The Config object used to hold the configuration variables for the Calendar
1632 * @type YAHOO.util.Config
1634 this.cfg = new YAHOO.util.Config(this);
1637 * The local object which contains the Calendar's options
1644 * The local object which contains the Calendar's locale settings
1652 Dom.addClass(this.oDomContainer, this.Style.CSS_CONTAINER);
1653 Dom.addClass(this.oDomContainer, this.Style.CSS_SINGLE);
1655 this.cellDates = [];
1657 this.renderStack = [];
1658 this._renderStack = [];
1663 this.cfg.applyConfig(config, true);
1666 this.cfg.fireQueue();
1668 this.today = this.cfg.getProperty("today");
1672 * Default Config listener for the iframe property. If the iframe config property is set to true,
1673 * renders the built-in IFRAME shim if the container is relatively or absolutely positioned.
1675 * @method configIframe
1677 configIframe : function(type, args, obj) {
1678 var useIframe = args[0];
1681 if (Dom.inDocument(this.oDomContainer)) {
1683 var pos = Dom.getStyle(this.oDomContainer, "position");
1685 if (pos == "absolute" || pos == "relative") {
1687 if (!Dom.inDocument(this.iframe)) {
1688 this.iframe = document.createElement("iframe");
1689 this.iframe.src = "javascript:false;";
1691 Dom.setStyle(this.iframe, "opacity", "0");
1693 if (YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) {
1694 Dom.addClass(this.iframe, this.Style.CSS_FIXED_SIZE);
1697 this.oDomContainer.insertBefore(this.iframe, this.oDomContainer.firstChild);
1702 if (this.iframe.parentNode) {
1703 this.iframe.parentNode.removeChild(this.iframe);
1713 * Default handler for the "title" property
1714 * @method configTitle
1716 configTitle : function(type, args, obj) {
1717 var title = args[0];
1719 // "" disables title bar
1721 this.createTitleBar(title);
1723 var close = this.cfg.getProperty(DEF_CFG.CLOSE.key);
1725 this.removeTitleBar();
1727 this.createTitleBar(" ");
1733 * Default handler for the "close" property
1734 * @method configClose
1736 configClose : function(type, args, obj) {
1737 var close = args[0],
1738 title = this.cfg.getProperty(DEF_CFG.TITLE.key);
1742 this.createTitleBar(" ");
1744 this.createCloseButton();
1746 this.removeCloseButton();
1748 this.removeTitleBar();
1754 * Initializes Calendar's built-in CustomEvents
1755 * @method initEvents
1757 initEvents : function() {
1759 var defEvents = Calendar._EVENT_TYPES,
1760 CE = YAHOO.util.CustomEvent,
1761 cal = this; // To help with minification
1764 * Fired before a date selection is made
1765 * @event beforeSelectEvent
1767 cal.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
1770 * Fired when a date selection is made
1771 * @event selectEvent
1772 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
1774 cal.selectEvent = new CE(defEvents.SELECT);
1777 * Fired before a date or set of dates is deselected
1778 * @event beforeDeselectEvent
1780 cal.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
1783 * Fired when a date or set of dates is deselected
1784 * @event deselectEvent
1785 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
1787 cal.deselectEvent = new CE(defEvents.DESELECT);
1790 * Fired when the Calendar page is changed
1791 * @event changePageEvent
1792 * @param {Date} prevDate The date before the page was changed
1793 * @param {Date} newDate The date after the page was changed
1795 cal.changePageEvent = new CE(defEvents.CHANGE_PAGE);
1798 * Fired before the Calendar is rendered
1799 * @event beforeRenderEvent
1801 cal.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
1804 * Fired when the Calendar is rendered
1805 * @event renderEvent
1807 cal.renderEvent = new CE(defEvents.RENDER);
1810 * Fired just before the Calendar is to be destroyed
1811 * @event beforeDestroyEvent
1813 cal.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
1816 * Fired after the Calendar is destroyed. This event should be used
1817 * for notification only. When this event is fired, important Calendar instance
1818 * properties, dom references and event listeners have already been
1819 * removed/dereferenced, and hence the Calendar instance is not in a usable
1822 * @event destroyEvent
1824 cal.destroyEvent = new CE(defEvents.DESTROY);
1827 * Fired when the Calendar is reset
1830 cal.resetEvent = new CE(defEvents.RESET);
1833 * Fired when the Calendar is cleared
1836 cal.clearEvent = new CE(defEvents.CLEAR);
1839 * Fired just before the Calendar is to be shown
1840 * @event beforeShowEvent
1842 cal.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
1845 * Fired after the Calendar is shown
1848 cal.showEvent = new CE(defEvents.SHOW);
1851 * Fired just before the Calendar is to be hidden
1852 * @event beforeHideEvent
1854 cal.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
1857 * Fired after the Calendar is hidden
1860 cal.hideEvent = new CE(defEvents.HIDE);
1863 * Fired just before the CalendarNavigator is to be shown
1864 * @event beforeShowNavEvent
1866 cal.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
1869 * Fired after the CalendarNavigator is shown
1870 * @event showNavEvent
1872 cal.showNavEvent = new CE(defEvents.SHOW_NAV);
1875 * Fired just before the CalendarNavigator is to be hidden
1876 * @event beforeHideNavEvent
1878 cal.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
1881 * Fired after the CalendarNavigator is hidden
1882 * @event hideNavEvent
1884 cal.hideNavEvent = new CE(defEvents.HIDE_NAV);
1887 * Fired just before the CalendarNavigator is to be rendered
1888 * @event beforeRenderNavEvent
1890 cal.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
1893 * Fired after the CalendarNavigator is rendered
1894 * @event renderNavEvent
1896 cal.renderNavEvent = new CE(defEvents.RENDER_NAV);
1898 cal.beforeSelectEvent.subscribe(cal.onBeforeSelect, this, true);
1899 cal.selectEvent.subscribe(cal.onSelect, this, true);
1900 cal.beforeDeselectEvent.subscribe(cal.onBeforeDeselect, this, true);
1901 cal.deselectEvent.subscribe(cal.onDeselect, this, true);
1902 cal.changePageEvent.subscribe(cal.onChangePage, this, true);
1903 cal.renderEvent.subscribe(cal.onRender, this, true);
1904 cal.resetEvent.subscribe(cal.onReset, this, true);
1905 cal.clearEvent.subscribe(cal.onClear, this, true);
1909 * The default event handler for clicks on the "Previous Month" navigation UI
1911 * @method doPreviousMonthNav
1912 * @param {DOMEvent} e The DOM event
1913 * @param {Calendar} cal A reference to the calendar
1915 doPreviousMonthNav : function(e, cal) {
1916 Event.preventDefault(e);
1917 // previousMonth invoked in a timeout, to allow
1918 // event to bubble up, with correct target. Calling
1919 // previousMonth, will call render which will remove
1920 // HTML which generated the event, resulting in an
1921 // invalid event target in certain browsers.
1922 setTimeout(function() {
1923 cal.previousMonth();
1924 var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_LEFT, "a", cal.oDomContainer);
1925 if (navs && navs[0]) {
1936 * The default event handler for clicks on the "Next Month" navigation UI
1938 * @method doNextMonthNav
1939 * @param {DOMEvent} e The DOM event
1940 * @param {Calendar} cal A reference to the calendar
1942 doNextMonthNav : function(e, cal) {
1943 Event.preventDefault(e);
1944 setTimeout(function() {
1946 var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_RIGHT, "a", cal.oDomContainer);
1947 if (navs && navs[0]) {
1958 * The default event handler for date cell selection. Currently attached to
1959 * the Calendar's bounding box, referenced by it's <a href="#property_oDomContainer">oDomContainer</a> property.
1961 * @method doSelectCell
1962 * @param {DOMEvent} e The DOM event
1963 * @param {Calendar} cal A reference to the calendar
1965 doSelectCell : function(e, cal) {
1966 var cell, d, date, index;
1968 var target = Event.getTarget(e),
1969 tagName = target.tagName.toLowerCase(),
1970 defSelector = false;
1972 while (tagName != "td" && !Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1974 if (!defSelector && tagName == "a" && Dom.hasClass(target, cal.Style.CSS_CELL_SELECTOR)) {
1978 target = target.parentNode;
1979 tagName = target.tagName.toLowerCase();
1981 if (target == this.oDomContainer || tagName == "html") {
1987 // Stop link href navigation for default renderer
1988 Event.preventDefault(e);
1993 if (Dom.hasClass(cell, cal.Style.CSS_CELL_SELECTABLE)) {
1994 index = cal.getIndexFromId(cell.id);
1996 d = cal.cellDates[index];
1998 date = DateMath.getDate(d[0],d[1]-1,d[2]);
2002 if (cal.Options.MULTI_SELECT) {
2003 link = cell.getElementsByTagName("a")[0];
2008 var cellDate = cal.cellDates[index];
2009 var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
2011 if (cellDateIndex > -1) {
2012 cal.deselectCell(index);
2014 cal.selectCell(index);
2018 link = cell.getElementsByTagName("a")[0];
2022 cal.selectCell(index);
2030 * The event that is executed when the user hovers over a cell
2031 * @method doCellMouseOver
2032 * @param {DOMEvent} e The event
2033 * @param {Calendar} cal A reference to the calendar passed by the Event utility
2035 doCellMouseOver : function(e, cal) {
2038 target = Event.getTarget(e);
2043 while (target.tagName && target.tagName.toLowerCase() != "td") {
2044 target = target.parentNode;
2045 if (!target.tagName || target.tagName.toLowerCase() == "html") {
2050 if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
2051 Dom.addClass(target, cal.Style.CSS_CELL_HOVER);
2056 * The event that is executed when the user moves the mouse out of a cell
2057 * @method doCellMouseOut
2058 * @param {DOMEvent} e The event
2059 * @param {Calendar} cal A reference to the calendar passed by the Event utility
2061 doCellMouseOut : function(e, cal) {
2064 target = Event.getTarget(e);
2069 while (target.tagName && target.tagName.toLowerCase() != "td") {
2070 target = target.parentNode;
2071 if (!target.tagName || target.tagName.toLowerCase() == "html") {
2076 if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
2077 Dom.removeClass(target, cal.Style.CSS_CELL_HOVER);
2081 setupConfig : function() {
2086 * The date to use to represent "Today".
2090 * @default The client side date (new Date()) when the Calendar is instantiated.
2092 cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler:this.configToday, suppressEvent:true } );
2095 * The month/year representing the current visible Calendar date (mm/yyyy)
2097 * @type String | Date
2098 * @default Today's date
2100 cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } );
2103 * The date or range of dates representing the current Calendar selection
2108 cfg.addProperty(DEF_CFG.SELECTED.key, { value:DEF_CFG.SELECTED.value.concat(), handler:this.configSelected } );
2111 * The title to display above the Calendar's month header
2116 cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
2119 * Whether or not a close button should be displayed for this Calendar
2124 cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
2127 * 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.
2128 * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be
2129 * enabled if required.
2133 * @default true for IE6 and below, false for all other browsers
2135 cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
2138 * The minimum selectable date in the current Calendar (mm/dd/yyyy)
2140 * @type String | Date
2143 cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.configMinDate } );
2146 * The maximum selectable date in the current Calendar (mm/dd/yyyy)
2148 * @type String | Date
2151 cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.configMaxDate } );
2153 // Options properties
2156 * True if the Calendar should allow multiple selections. False by default.
2157 * @config MULTI_SELECT
2161 cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2164 * The weekday the week begins on. Default is 0 (Sunday = 0, Monday = 1 ... Saturday = 6).
2165 * @config START_WEEKDAY
2169 cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.configOptions, validator:cfg.checkNumber } );
2172 * True if the Calendar should show weekday labels. True by default.
2173 * @config SHOW_WEEKDAYS
2177 cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2180 * True if the Calendar should show week row headers. False by default.
2181 * @config SHOW_WEEK_HEADER
2185 cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key, { value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2188 * True if the Calendar should show week row footers. False by default.
2189 * @config SHOW_WEEK_FOOTER
2193 cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2196 * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
2197 * @config HIDE_BLANK_WEEKS
2201 cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key, { value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2204 * The image that should be used for the left navigation arrow.
2205 * @config NAV_ARROW_LEFT
2207 * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
2210 cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.configOptions } );
2213 * The image that should be used for the right navigation arrow.
2214 * @config NAV_ARROW_RIGHT
2216 * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
2219 cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.configOptions } );
2221 // Locale properties
2224 * The short month labels for the current locale.
2225 * @config MONTHS_SHORT
2227 * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
2229 cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.configLocale } );
2232 * The long month labels for the current locale.
2233 * @config MONTHS_LONG
2235 * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
2237 cfg.addProperty(DEF_CFG.MONTHS_LONG.key, { value:DEF_CFG.MONTHS_LONG.value, handler:this.configLocale } );
2240 * The 1-character weekday labels for the current locale.
2241 * @config WEEKDAYS_1CHAR
2243 * @default ["S", "M", "T", "W", "T", "F", "S"]
2245 cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.configLocale } );
2248 * The short weekday labels for the current locale.
2249 * @config WEEKDAYS_SHORT
2251 * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
2253 cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.configLocale } );
2256 * The medium weekday labels for the current locale.
2257 * @config WEEKDAYS_MEDIUM
2259 * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
2261 cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.configLocale } );
2264 * The long weekday labels for the current locale.
2265 * @config WEEKDAYS_LONG
2267 * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
2269 cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.configLocale } );
2272 * Refreshes the locale values used to build the Calendar.
2273 * @method refreshLocale
2276 var refreshLocale = function() {
2277 cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
2278 cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
2281 cfg.subscribeToConfigEvent(DEF_CFG.START_WEEKDAY.key, refreshLocale, this, true);
2282 cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_SHORT.key, refreshLocale, this, true);
2283 cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_LONG.key, refreshLocale, this, true);
2284 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_1CHAR.key, refreshLocale, this, true);
2285 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_SHORT.key, refreshLocale, this, true);
2286 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_MEDIUM.key, refreshLocale, this, true);
2287 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_LONG.key, refreshLocale, this, true);
2290 * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
2291 * @config LOCALE_MONTHS
2295 cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.configLocaleValues } );
2298 * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
2299 * @config LOCALE_WEEKDAYS
2303 cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.configLocaleValues } );
2306 * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to
2307 * be used when displaying and parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by
2308 * methods will always represent the Gregorian year, in order to maintain date/month/week values.
2310 * @config YEAR_OFFSET
2314 cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, handler:this.configLocale } );
2317 * The value used to delimit individual dates in a date string passed to various Calendar functions.
2318 * @config DATE_DELIMITER
2322 cfg.addProperty(DEF_CFG.DATE_DELIMITER.key, { value:DEF_CFG.DATE_DELIMITER.value, handler:this.configLocale } );
2325 * The value used to delimit date fields in a date string passed to various Calendar functions.
2326 * @config DATE_FIELD_DELIMITER
2330 cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key, { value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.configLocale } );
2333 * The value used to delimit date ranges in a date string passed to various Calendar functions.
2334 * @config DATE_RANGE_DELIMITER
2338 cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key, { value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.configLocale } );
2341 * The position of the month in a month/year date string
2342 * @config MY_MONTH_POSITION
2346 cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2349 * The position of the year in a month/year date string
2350 * @config MY_YEAR_POSITION
2354 cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2357 * The position of the month in a month/day date string
2358 * @config MD_MONTH_POSITION
2362 cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2365 * The position of the day in a month/year date string
2366 * @config MD_DAY_POSITION
2370 cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key, { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2373 * The position of the month in a month/day/year date string
2374 * @config MDY_MONTH_POSITION
2378 cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2381 * The position of the day in a month/day/year date string
2382 * @config MDY_DAY_POSITION
2386 cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2389 * The position of the year in a month/day/year date string
2390 * @config MDY_YEAR_POSITION
2394 cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2397 * The position of the month in the month year label string used as the Calendar header
2398 * @config MY_LABEL_MONTH_POSITION
2402 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2405 * The position of the year in the month year label string used as the Calendar header
2406 * @config MY_LABEL_YEAR_POSITION
2410 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2413 * The suffix used after the month when rendering the Calendar header
2414 * @config MY_LABEL_MONTH_SUFFIX
2418 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.configLocale } );
2421 * The suffix used after the year when rendering the Calendar header
2422 * @config MY_LABEL_YEAR_SUFFIX
2426 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.configLocale } );
2429 * Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a
2430 * specific Month/Year without having to scroll sequentially through months.
2432 * Setting this property to null (default value) or false, will disable the CalendarNavigator UI.
2435 * Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values.
2438 * This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI.
2439 * The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object.
2440 * Any properties which are not provided will use the default values (defined in the CalendarNavigator class).
2444 * <dd><em>Object</em> : An object with the properties shown below, defining the string labels to use in the Navigator's UI
2446 * <dt>month</dt><dd><em>String</em> : The string to use for the month label. Defaults to "Month".</dd>
2447 * <dt>year</dt><dd><em>String</em> : The string to use for the year label. Defaults to "Year".</dd>
2448 * <dt>submit</dt><dd><em>String</em> : The string to use for the submit button label. Defaults to "Okay".</dd>
2449 * <dt>cancel</dt><dd><em>String</em> : The string to use for the cancel button label. Defaults to "Cancel".</dd>
2450 * <dt>invalidYear</dt><dd><em>String</em> : The string to use for invalid year values. Defaults to "Year needs to be a number".</dd>
2453 * <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>
2454 * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
2460 * month:"Calendar Month",
2461 * year:"Calendar Year",
2464 * invalidYear: "Please enter a valid year"
2466 * monthFormat: YAHOO.widget.Calendar.SHORT,
2467 * initialFocus: "month"
2471 * @type {Object|Boolean}
2474 cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
2477 * The map of UI strings which the Calendar UI uses.
2481 * @default An object with the properties shown below:
2483 * <dt>previousMonth</dt><dd><em>String</em> : The string to use for the "Previous Month" navigation UI. Defaults to "Previous Month".</dd>
2484 * <dt>nextMonth</dt><dd><em>String</em> : The string to use for the "Next Month" navigation UI. Defaults to "Next Month".</dd>
2485 * <dt>close</dt><dd><em>String</em> : The string to use for the close button label. Defaults to "Close".</dd>
2488 cfg.addProperty(DEF_CFG.STRINGS.key, {
2489 value:DEF_CFG.STRINGS.value,
2490 handler:this.configStrings,
2491 validator: function(val) {
2492 return Lang.isObject(val);
2494 supercedes:DEF_CFG.STRINGS.supercedes
2499 * The default handler for the "strings" property
2500 * @method configStrings
2502 configStrings : function(type, args, obj) {
2503 var val = Lang.merge(DEF_CFG.STRINGS.value, args[0]);
2504 this.cfg.setProperty(DEF_CFG.STRINGS.key, val, true);
2508 * The default handler for the "pagedate" property
2509 * @method configPageDate
2511 configPageDate : function(type, args, obj) {
2512 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, this._parsePageDate(args[0]), true);
2516 * The default handler for the "mindate" property
2517 * @method configMinDate
2519 configMinDate : function(type, args, obj) {
2521 if (Lang.isString(val)) {
2522 val = this._parseDate(val);
2523 this.cfg.setProperty(DEF_CFG.MINDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
2528 * The default handler for the "maxdate" property
2529 * @method configMaxDate
2531 configMaxDate : function(type, args, obj) {
2533 if (Lang.isString(val)) {
2534 val = this._parseDate(val);
2535 this.cfg.setProperty(DEF_CFG.MAXDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
2540 * The default handler for the "today" property
2541 * @method configToday
2543 configToday : function(type, args, obj) {
2544 // Only do this for initial set. Changing the today property after the initial
2545 // set, doesn't affect pagedate
2547 if (Lang.isString(val)) {
2548 val = this._parseDate(val);
2550 var today = DateMath.clearTime(val);
2551 if (!this.cfg.initialConfig[DEF_CFG.PAGEDATE.key]) {
2552 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, today);
2555 this.cfg.setProperty(DEF_CFG.TODAY.key, today, true);
2559 * The default handler for the "selected" property
2560 * @method configSelected
2562 configSelected : function(type, args, obj) {
2563 var selected = args[0],
2564 cfgSelected = DEF_CFG.SELECTED.key;
2567 if (Lang.isString(selected)) {
2568 this.cfg.setProperty(cfgSelected, this._parseDates(selected), true);
2571 if (! this._selectedDates) {
2572 this._selectedDates = this.cfg.getProperty(cfgSelected);
2577 * The default handler for all configuration options properties
2578 * @method configOptions
2580 configOptions : function(type, args, obj) {
2581 this.Options[type.toUpperCase()] = args[0];
2585 * The default handler for all configuration locale properties
2586 * @method configLocale
2588 configLocale : function(type, args, obj) {
2589 this.Locale[type.toUpperCase()] = args[0];
2591 this.cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
2592 this.cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
2596 * The default handler for all configuration locale field length properties
2597 * @method configLocaleValues
2599 configLocaleValues : function(type, args, obj) {
2601 type = type.toLowerCase();
2605 Locale = this.Locale;
2608 case DEF_CFG.LOCALE_MONTHS.key:
2610 case Calendar.SHORT:
2611 Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_SHORT.key).concat();
2614 Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_LONG.key).concat();
2618 case DEF_CFG.LOCALE_WEEKDAYS.key:
2620 case Calendar.ONE_CHAR:
2621 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_1CHAR.key).concat();
2623 case Calendar.SHORT:
2624 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_SHORT.key).concat();
2626 case Calendar.MEDIUM:
2627 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_MEDIUM.key).concat();
2630 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_LONG.key).concat();
2634 var START_WEEKDAY = cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
2636 if (START_WEEKDAY > 0) {
2637 for (var w=0; w < START_WEEKDAY; ++w) {
2638 Locale.LOCALE_WEEKDAYS.push(Locale.LOCALE_WEEKDAYS.shift());
2646 * The default handler for the "navigator" property
2647 * @method configNavigator
2649 configNavigator : function(type, args, obj) {
2651 if (YAHOO.widget.CalendarNavigator && (val === true || Lang.isObject(val))) {
2652 if (!this.oNavigator) {
2653 this.oNavigator = new YAHOO.widget.CalendarNavigator(this);
2654 // Cleanup DOM Refs/Events before innerHTML is removed.
2655 this.beforeRenderEvent.subscribe(function () {
2657 this.oNavigator.erase();
2662 if (this.oNavigator) {
2663 this.oNavigator.destroy();
2664 this.oNavigator = null;
2670 * Defines the style constants for the Calendar
2671 * @method initStyles
2673 initStyles : function() {
2675 var defStyle = Calendar.STYLES;
2679 * @property Style.CSS_ROW_HEADER
2681 CSS_ROW_HEADER: defStyle.CSS_ROW_HEADER,
2683 * @property Style.CSS_ROW_FOOTER
2685 CSS_ROW_FOOTER: defStyle.CSS_ROW_FOOTER,
2687 * @property Style.CSS_CELL
2689 CSS_CELL : defStyle.CSS_CELL,
2691 * @property Style.CSS_CELL_SELECTOR
2693 CSS_CELL_SELECTOR : defStyle.CSS_CELL_SELECTOR,
2695 * @property Style.CSS_CELL_SELECTED
2697 CSS_CELL_SELECTED : defStyle.CSS_CELL_SELECTED,
2699 * @property Style.CSS_CELL_SELECTABLE
2701 CSS_CELL_SELECTABLE : defStyle.CSS_CELL_SELECTABLE,
2703 * @property Style.CSS_CELL_RESTRICTED
2705 CSS_CELL_RESTRICTED : defStyle.CSS_CELL_RESTRICTED,
2707 * @property Style.CSS_CELL_TODAY
2709 CSS_CELL_TODAY : defStyle.CSS_CELL_TODAY,
2711 * @property Style.CSS_CELL_OOM
2713 CSS_CELL_OOM : defStyle.CSS_CELL_OOM,
2715 * @property Style.CSS_CELL_OOB
2717 CSS_CELL_OOB : defStyle.CSS_CELL_OOB,
2719 * @property Style.CSS_HEADER
2721 CSS_HEADER : defStyle.CSS_HEADER,
2723 * @property Style.CSS_HEADER_TEXT
2725 CSS_HEADER_TEXT : defStyle.CSS_HEADER_TEXT,
2727 * @property Style.CSS_BODY
2729 CSS_BODY : defStyle.CSS_BODY,
2731 * @property Style.CSS_WEEKDAY_CELL
2733 CSS_WEEKDAY_CELL : defStyle.CSS_WEEKDAY_CELL,
2735 * @property Style.CSS_WEEKDAY_ROW
2737 CSS_WEEKDAY_ROW : defStyle.CSS_WEEKDAY_ROW,
2739 * @property Style.CSS_FOOTER
2741 CSS_FOOTER : defStyle.CSS_FOOTER,
2743 * @property Style.CSS_CALENDAR
2745 CSS_CALENDAR : defStyle.CSS_CALENDAR,
2747 * @property Style.CSS_SINGLE
2749 CSS_SINGLE : defStyle.CSS_SINGLE,
2751 * @property Style.CSS_CONTAINER
2753 CSS_CONTAINER : defStyle.CSS_CONTAINER,
2755 * @property Style.CSS_NAV_LEFT
2757 CSS_NAV_LEFT : defStyle.CSS_NAV_LEFT,
2759 * @property Style.CSS_NAV_RIGHT
2761 CSS_NAV_RIGHT : defStyle.CSS_NAV_RIGHT,
2763 * @property Style.CSS_NAV
2765 CSS_NAV : defStyle.CSS_NAV,
2767 * @property Style.CSS_CLOSE
2769 CSS_CLOSE : defStyle.CSS_CLOSE,
2771 * @property Style.CSS_CELL_TOP
2773 CSS_CELL_TOP : defStyle.CSS_CELL_TOP,
2775 * @property Style.CSS_CELL_LEFT
2777 CSS_CELL_LEFT : defStyle.CSS_CELL_LEFT,
2779 * @property Style.CSS_CELL_RIGHT
2781 CSS_CELL_RIGHT : defStyle.CSS_CELL_RIGHT,
2783 * @property Style.CSS_CELL_BOTTOM
2785 CSS_CELL_BOTTOM : defStyle.CSS_CELL_BOTTOM,
2787 * @property Style.CSS_CELL_HOVER
2789 CSS_CELL_HOVER : defStyle.CSS_CELL_HOVER,
2791 * @property Style.CSS_CELL_HIGHLIGHT1
2793 CSS_CELL_HIGHLIGHT1 : defStyle.CSS_CELL_HIGHLIGHT1,
2795 * @property Style.CSS_CELL_HIGHLIGHT2
2797 CSS_CELL_HIGHLIGHT2 : defStyle.CSS_CELL_HIGHLIGHT2,
2799 * @property Style.CSS_CELL_HIGHLIGHT3
2801 CSS_CELL_HIGHLIGHT3 : defStyle.CSS_CELL_HIGHLIGHT3,
2803 * @property Style.CSS_CELL_HIGHLIGHT4
2805 CSS_CELL_HIGHLIGHT4 : defStyle.CSS_CELL_HIGHLIGHT4,
2807 * @property Style.CSS_WITH_TITLE
2809 CSS_WITH_TITLE : defStyle.CSS_WITH_TITLE,
2811 * @property Style.CSS_FIXED_SIZE
2813 CSS_FIXED_SIZE : defStyle.CSS_FIXED_SIZE,
2815 * @property Style.CSS_LINK_CLOSE
2817 CSS_LINK_CLOSE : defStyle.CSS_LINK_CLOSE
2822 * Builds the date label that will be displayed in the calendar header or
2823 * footer, depending on configuration.
2824 * @method buildMonthLabel
2825 * @return {String} The formatted calendar month label
2827 buildMonthLabel : function() {
2828 return this._buildMonthLabel(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
2832 * Helper method, to format a Month Year string, given a JavaScript Date, based on the
2833 * Calendar localization settings
2835 * @method _buildMonthLabel
2837 * @param {Date} date
2838 * @return {String} Formated month, year string
2840 _buildMonthLabel : function(date) {
2841 var monthLabel = this.Locale.LOCALE_MONTHS[date.getMonth()] + this.Locale.MY_LABEL_MONTH_SUFFIX,
2842 yearLabel = (date.getFullYear() + this.Locale.YEAR_OFFSET) + this.Locale.MY_LABEL_YEAR_SUFFIX;
2844 if (this.Locale.MY_LABEL_MONTH_POSITION == 2 || this.Locale.MY_LABEL_YEAR_POSITION == 1) {
2845 return yearLabel + monthLabel;
2847 return monthLabel + yearLabel;
2852 * Builds the date digit that will be displayed in calendar cells
2853 * @method buildDayLabel
2854 * @param {Date} workingDate The current working date
2855 * @return {String} The formatted day label
2857 buildDayLabel : function(workingDate) {
2858 return workingDate.getDate();
2862 * Creates the title bar element and adds it to Calendar container DIV
2864 * @method createTitleBar
2865 * @param {String} strTitle The title to display in the title bar
2866 * @return The title bar element
2868 createTitleBar : function(strTitle) {
2869 var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || document.createElement("div");
2870 tDiv.className = YAHOO.widget.CalendarGroup.CSS_2UPTITLE;
2871 tDiv.innerHTML = strTitle;
2872 this.oDomContainer.insertBefore(tDiv, this.oDomContainer.firstChild);
2874 Dom.addClass(this.oDomContainer, this.Style.CSS_WITH_TITLE);
2880 * Removes the title bar element from the DOM
2882 * @method removeTitleBar
2884 removeTitleBar : function() {
2885 var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || null;
2887 Event.purgeElement(tDiv);
2888 this.oDomContainer.removeChild(tDiv);
2890 Dom.removeClass(this.oDomContainer, this.Style.CSS_WITH_TITLE);
2894 * Creates the close button HTML element and adds it to Calendar container DIV
2896 * @method createCloseButton
2897 * @return The close HTML element created
2899 createCloseButton : function() {
2900 var cssClose = YAHOO.widget.CalendarGroup.CSS_2UPCLOSE,
2901 cssLinkClose = this.Style.CSS_LINK_CLOSE,
2902 DEPR_CLOSE_PATH = "us/my/bn/x_d.gif",
2904 lnk = Dom.getElementsByClassName(cssLinkClose, "a", this.oDomContainer)[0],
2905 strings = this.cfg.getProperty(DEF_CFG.STRINGS.key),
2906 closeStr = (strings && strings.close) ? strings.close : "";
2909 lnk = document.createElement("a");
2910 Event.addListener(lnk, "click", function(e, cal) {
2912 Event.preventDefault(e);
2917 lnk.className = cssLinkClose;
2919 if (Calendar.IMG_ROOT !== null) {
2920 var img = Dom.getElementsByClassName(cssClose, "img", lnk)[0] || document.createElement("img");
2921 img.src = Calendar.IMG_ROOT + DEPR_CLOSE_PATH;
2922 img.className = cssClose;
2923 lnk.appendChild(img);
2925 lnk.innerHTML = '<span class="' + cssClose + ' ' + this.Style.CSS_CLOSE + '">' + closeStr + '</span>';
2927 this.oDomContainer.appendChild(lnk);
2933 * Removes the close button HTML element from the DOM
2935 * @method removeCloseButton
2937 removeCloseButton : function() {
2938 var btn = Dom.getElementsByClassName(this.Style.CSS_LINK_CLOSE, "a", this.oDomContainer)[0] || null;
2940 Event.purgeElement(btn);
2941 this.oDomContainer.removeChild(btn);
2946 * Renders the calendar header.
2947 * @method renderHeader
2948 * @param {Array} html The current working HTML array
2949 * @return {Array} The current working HTML array
2951 renderHeader : function(html) {
2955 DEPR_NAV_LEFT = "us/tr/callt.gif",
2956 DEPR_NAV_RIGHT = "us/tr/calrt.gif",
2958 pageDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
2959 strings= cfg.getProperty(DEF_CFG.STRINGS.key),
2960 prevStr = (strings && strings.previousMonth) ? strings.previousMonth : "",
2961 nextStr = (strings && strings.nextMonth) ? strings.nextMonth : "",
2964 if (cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
2968 if (cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
2972 html[html.length] = "<thead>";
2973 html[html.length] = "<tr>";
2974 html[html.length] = '<th colspan="' + colSpan + '" class="' + this.Style.CSS_HEADER_TEXT + '">';
2975 html[html.length] = '<div class="' + this.Style.CSS_HEADER + '">';
2977 var renderLeft, renderRight = false;
2980 if (this.index === 0) {
2983 if (this.index == (this.parent.cfg.getProperty("pages") -1)) {
2992 monthLabel = this._buildMonthLabel(DateMath.subtract(pageDate, DateMath.MONTH, 1));
2994 var leftArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_LEFT.key);
2995 // 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
2996 if (leftArrow === null && Calendar.IMG_ROOT !== null) {
2997 leftArrow = Calendar.IMG_ROOT + DEPR_NAV_LEFT;
2999 var leftStyle = (leftArrow === null) ? "" : ' style="background-image:url(' + leftArrow + ')"';
3000 html[html.length] = '<a class="' + this.Style.CSS_NAV_LEFT + '"' + leftStyle + ' href="#">' + prevStr + ' (' + monthLabel + ')' + '</a>';
3003 var lbl = this.buildMonthLabel();
3004 var cal = this.parent || this;
3005 if (cal.cfg.getProperty("navigator")) {
3006 lbl = "<a class=\"" + this.Style.CSS_NAV + "\" href=\"#\">" + lbl + "</a>";
3008 html[html.length] = lbl;
3011 monthLabel = this._buildMonthLabel(DateMath.add(pageDate, DateMath.MONTH, 1));
3013 var rightArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_RIGHT.key);
3014 if (rightArrow === null && Calendar.IMG_ROOT !== null) {
3015 rightArrow = Calendar.IMG_ROOT + DEPR_NAV_RIGHT;
3017 var rightStyle = (rightArrow === null) ? "" : ' style="background-image:url(' + rightArrow + ')"';
3018 html[html.length] = '<a class="' + this.Style.CSS_NAV_RIGHT + '"' + rightStyle + ' href="#">' + nextStr + ' (' + monthLabel + ')' + '</a>';
3021 html[html.length] = '</div>\n</th>\n</tr>';
3023 if (cfg.getProperty(DEF_CFG.SHOW_WEEKDAYS.key)) {
3024 html = this.buildWeekdays(html);
3027 html[html.length] = '</thead>';
3033 * Renders the Calendar's weekday headers.
3034 * @method buildWeekdays
3035 * @param {Array} html The current working HTML array
3036 * @return {Array} The current working HTML array
3038 buildWeekdays : function(html) {
3040 html[html.length] = '<tr class="' + this.Style.CSS_WEEKDAY_ROW + '">';
3042 if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
3043 html[html.length] = '<th> </th>';
3046 for(var i=0;i < this.Locale.LOCALE_WEEKDAYS.length; ++i) {
3047 html[html.length] = '<th class="' + this.Style.CSS_WEEKDAY_CELL + '">' + this.Locale.LOCALE_WEEKDAYS[i] + '</th>';
3050 if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
3051 html[html.length] = '<th> </th>';
3054 html[html.length] = '</tr>';
3060 * Renders the calendar body.
3061 * @method renderBody
3062 * @param {Date} workingDate The current working Date being used for the render process
3063 * @param {Array} html The current working HTML array
3064 * @return {Array} The current working HTML array
3066 renderBody : function(workingDate, html) {
3068 var startDay = this.cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
3070 this.preMonthDays = workingDate.getDay();
3072 this.preMonthDays -= startDay;
3074 if (this.preMonthDays < 0) {
3075 this.preMonthDays += 7;
3078 this.monthDays = DateMath.findMonthEnd(workingDate).getDate();
3079 this.postMonthDays = Calendar.DISPLAY_DAYS-this.preMonthDays-this.monthDays;
3082 workingDate = DateMath.subtract(workingDate, DateMath.DAY, this.preMonthDays);
3087 cellPrefix = "_cell",
3088 workingDayPrefix = "wd",
3094 todayYear = t.getFullYear(),
3095 todayMonth = t.getMonth(),
3096 todayDate = t.getDate(),
3097 useDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
3098 hideBlankWeeks = cfg.getProperty(DEF_CFG.HIDE_BLANK_WEEKS.key),
3099 showWeekFooter = cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key),
3100 showWeekHeader = cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key),
3101 mindate = cfg.getProperty(DEF_CFG.MINDATE.key),
3102 maxdate = cfg.getProperty(DEF_CFG.MAXDATE.key),
3103 yearOffset = this.Locale.YEAR_OFFSET;
3106 mindate = DateMath.clearTime(mindate);
3109 maxdate = DateMath.clearTime(maxdate);
3112 html[html.length] = '<tbody class="m' + (useDate.getMonth()+1) + ' ' + this.Style.CSS_BODY + '">';
3115 tempDiv = document.createElement("div"),
3116 cell = document.createElement("td");
3118 tempDiv.appendChild(cell);
3120 var cal = this.parent || this;
3122 for (var r=0;r<6;r++) {
3123 weekNum = DateMath.getWeekNumber(workingDate, startDay);
3124 weekClass = weekPrefix + weekNum;
3126 // Local OOM check for performance, since we already have pagedate
3127 if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth()) {
3130 html[html.length] = '<tr class="' + weekClass + '">';
3132 if (showWeekHeader) { html = this.renderRowHeader(weekNum, html); }
3134 for (var d=0; d < 7; d++){ // Render actual days
3138 this.clearElement(cell);
3139 cell.className = this.Style.CSS_CELL;
3140 cell.id = this.id + cellPrefix + i;
3142 if (workingDate.getDate() == todayDate &&
3143 workingDate.getMonth() == todayMonth &&
3144 workingDate.getFullYear() == todayYear) {
3145 cellRenderers[cellRenderers.length]=cal.renderCellStyleToday;
3148 var workingArray = [workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()];
3149 this.cellDates[this.cellDates.length] = workingArray; // Add this date to cellDates
3151 // Local OOM check for performance, since we already have pagedate
3152 if (workingDate.getMonth() != useDate.getMonth()) {
3153 cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth;
3155 Dom.addClass(cell, workingDayPrefix + workingDate.getDay());
3156 Dom.addClass(cell, dayPrefix + workingDate.getDate());
3158 for (var s=0;s<this.renderStack.length;++s) {
3162 var rArray = this.renderStack[s],
3170 month = rArray[1][1];
3172 year = rArray[1][0];
3174 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == year) {
3175 renderer = rArray[2];
3176 this.renderStack.splice(s,1);
3179 case Calendar.MONTH_DAY:
3180 month = rArray[1][0];
3183 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day) {
3184 renderer = rArray[2];
3185 this.renderStack.splice(s,1);
3188 case Calendar.RANGE:
3189 var date1 = rArray[1][0],
3190 date2 = rArray[1][1],
3194 d1 = DateMath.getDate(d1year, d1month-1, d1day),
3198 d2 = DateMath.getDate(d2year, d2month-1, d2day);
3200 if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime()) {
3201 renderer = rArray[2];
3203 if (workingDate.getTime()==d2.getTime()) {
3204 this.renderStack.splice(s,1);
3208 case Calendar.WEEKDAY:
3209 var weekday = rArray[1][0];
3210 if (workingDate.getDay()+1 == weekday) {
3211 renderer = rArray[2];
3214 case Calendar.MONTH:
3215 month = rArray[1][0];
3216 if (workingDate.getMonth()+1 == month) {
3217 renderer = rArray[2];
3223 cellRenderers[cellRenderers.length]=renderer;
3229 if (this._indexOfSelectedFieldArray(workingArray) > -1) {
3230 cellRenderers[cellRenderers.length]=cal.renderCellStyleSelected;
3233 if ((mindate && (workingDate.getTime() < mindate.getTime())) ||
3234 (maxdate && (workingDate.getTime() > maxdate.getTime()))
3236 cellRenderers[cellRenderers.length]=cal.renderOutOfBoundsDate;
3238 cellRenderers[cellRenderers.length]=cal.styleCellDefault;
3239 cellRenderers[cellRenderers.length]=cal.renderCellDefault;
3242 for (var x=0; x < cellRenderers.length; ++x) {
3243 if (cellRenderers[x].call(cal, workingDate, cell) == Calendar.STOP_RENDER) {
3248 workingDate.setTime(workingDate.getTime() + DateMath.ONE_DAY_MS);
3249 // Just in case we crossed DST/Summertime boundaries
3250 workingDate = DateMath.clearTime(workingDate);
3252 if (i >= 0 && i <= 6) {
3253 Dom.addClass(cell, this.Style.CSS_CELL_TOP);
3255 if ((i % 7) === 0) {
3256 Dom.addClass(cell, this.Style.CSS_CELL_LEFT);
3258 if (((i+1) % 7) === 0) {
3259 Dom.addClass(cell, this.Style.CSS_CELL_RIGHT);
3262 var postDays = this.postMonthDays;
3263 if (hideBlankWeeks && postDays >= 7) {
3264 var blankWeeks = Math.floor(postDays/7);
3265 for (var p=0;p<blankWeeks;++p) {
3270 if (i >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
3271 Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM);
3274 html[html.length] = tempDiv.innerHTML;
3278 if (showWeekFooter) { html = this.renderRowFooter(weekNum, html); }
3280 html[html.length] = '</tr>';
3284 html[html.length] = '</tbody>';
3290 * Renders the calendar footer. In the default implementation, there is
3292 * @method renderFooter
3293 * @param {Array} html The current working HTML array
3294 * @return {Array} The current working HTML array
3296 renderFooter : function(html) { return html; },
3299 * Renders the calendar after it has been configured. The render() method has a specific call chain that will execute
3300 * when the method is called: renderHeader, renderBody, renderFooter.
3301 * Refer to the documentation for those methods for information on
3302 * individual render tasks.
3305 render : function() {
3306 this.beforeRenderEvent.fire();
3308 // Find starting day of the current month
3309 var workingDate = DateMath.findMonthStart(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
3311 this.resetRenderers();
3312 this.cellDates.length = 0;
3314 Event.purgeElement(this.oDomContainer, true);
3318 html[html.length] = '<table cellSpacing="0" class="' + this.Style.CSS_CALENDAR + ' y' + (workingDate.getFullYear() + this.Locale.YEAR_OFFSET) +'" id="' + this.id + '">';
3319 html = this.renderHeader(html);
3320 html = this.renderBody(workingDate, html);
3321 html = this.renderFooter(html);
3322 html[html.length] = '</table>';
3324 this.oDomContainer.innerHTML = html.join("\n");
3326 this.applyListeners();
3327 this.cells = Dom.getElementsByClassName(this.Style.CSS_CELL, "td", this.id);
3329 this.cfg.refireEvent(DEF_CFG.TITLE.key);
3330 this.cfg.refireEvent(DEF_CFG.CLOSE.key);
3331 this.cfg.refireEvent(DEF_CFG.IFRAME.key);
3333 this.renderEvent.fire();
3337 * Applies the Calendar's DOM listeners to applicable elements.
3338 * @method applyListeners
3340 applyListeners : function() {
3341 var root = this.oDomContainer,
3342 cal = this.parent || this,
3346 var linkLeft = Dom.getElementsByClassName(this.Style.CSS_NAV_LEFT, anchor, root),
3347 linkRight = Dom.getElementsByClassName(this.Style.CSS_NAV_RIGHT, anchor, root);
3349 if (linkLeft && linkLeft.length > 0) {
3350 this.linkLeft = linkLeft[0];
3351 Event.addListener(this.linkLeft, click, this.doPreviousMonthNav, cal, true);
3354 if (linkRight && linkRight.length > 0) {
3355 this.linkRight = linkRight[0];
3356 Event.addListener(this.linkRight, click, this.doNextMonthNav, cal, true);
3359 if (cal.cfg.getProperty("navigator") !== null) {
3360 this.applyNavListeners();
3363 if (this.domEventMap) {
3365 for (var cls in this.domEventMap) {
3366 if (Lang.hasOwnProperty(this.domEventMap, cls)) {
3367 var items = this.domEventMap[cls];
3369 if (! (items instanceof Array)) {
3373 for (var i=0;i<items.length;i++) {
3374 var item = items[i];
3375 elements = Dom.getElementsByClassName(cls, item.tag, this.oDomContainer);
3377 for (var c=0;c<elements.length;c++) {
3379 Event.addListener(el, item.event, item.handler, item.scope, item.correct );
3386 Event.addListener(this.oDomContainer, "click", this.doSelectCell, this);
3387 Event.addListener(this.oDomContainer, "mouseover", this.doCellMouseOver, this);
3388 Event.addListener(this.oDomContainer, "mouseout", this.doCellMouseOut, this);
3391 applyNavListeners : function() {
3392 var calParent = this.parent || this,
3394 navBtns = Dom.getElementsByClassName(this.Style.CSS_NAV, "a", this.oDomContainer);
3396 if (navBtns.length > 0) {
3398 Event.addListener(navBtns, "click", function (e, obj) {
3399 var target = Event.getTarget(e);
3401 if (this === target || Dom.isAncestor(this, target)) {
3402 Event.preventDefault(e);
3404 var navigator = calParent.oNavigator;
3406 var pgdate = cal.cfg.getProperty("pagedate");
3407 navigator.setYear(pgdate.getFullYear() + cal.Locale.YEAR_OFFSET);
3408 navigator.setMonth(pgdate.getMonth());
3416 * Retrieves the Date object for the specified Calendar cell
3417 * @method getDateByCellId
3418 * @param {String} id The id of the cell
3419 * @return {Date} The Date object for the specified Calendar cell
3421 getDateByCellId : function(id) {
3422 var date = this.getDateFieldsByCellId(id);
3423 return (date) ? DateMath.getDate(date[0],date[1]-1,date[2]) : null;
3427 * Retrieves the Date object for the specified Calendar cell
3428 * @method getDateFieldsByCellId
3429 * @param {String} id The id of the cell
3430 * @return {Array} The array of Date fields for the specified Calendar cell
3432 getDateFieldsByCellId : function(id) {
3433 id = this.getIndexFromId(id);
3434 return (id > -1) ? this.cellDates[id] : null;
3438 * Find the Calendar's cell index for a given date.
3439 * If the date is not found, the method returns -1.
3441 * The returned index can be used to lookup the cell HTMLElement
3442 * using the Calendar's cells array or passed to selectCell to select
3446 * See <a href="#cells">cells</a>, <a href="#selectCell">selectCell</a>.
3448 * @method getCellIndex
3449 * @param {Date} date JavaScript Date object, for which to find a cell index.
3450 * @return {Number} The index of the date in Calendars cellDates/cells arrays, or -1 if the date
3451 * is not on the curently rendered Calendar page.
3453 getCellIndex : function(date) {
3456 var m = date.getMonth(),
3457 y = date.getFullYear(),
3459 dates = this.cellDates;
3461 for (var i = 0; i < dates.length; ++i) {
3462 var cellDate = dates[i];
3463 if (cellDate[0] === y && cellDate[1] === m+1 && cellDate[2] === d) {
3473 * Given the id used to mark each Calendar cell, this method
3474 * extracts the index number from the id.
3476 * @param {String} strId The cell id
3477 * @return {Number} The index of the cell, or -1 if id does not contain an index number
3479 getIndexFromId : function(strId) {
3481 li = strId.lastIndexOf("_cell");
3484 idx = parseInt(strId.substring(li + 5), 10);
3490 // BEGIN BUILT-IN TABLE CELL RENDERERS
3493 * Renders a cell that falls before the minimum date or after the maximum date.
3495 * @method renderOutOfBoundsDate
3496 * @param {Date} workingDate The current working Date object being used to generate the calendar
3497 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3498 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3499 * should not be terminated
3501 renderOutOfBoundsDate : function(workingDate, cell) {
3502 Dom.addClass(cell, this.Style.CSS_CELL_OOB);
3503 cell.innerHTML = workingDate.getDate();
3504 return Calendar.STOP_RENDER;
3508 * Renders the row header for a week.
3509 * @method renderRowHeader
3510 * @param {Number} weekNum The week number of the current row
3511 * @param {Array} cell The current working HTML array
3513 renderRowHeader : function(weekNum, html) {
3514 html[html.length] = '<th class="' + this.Style.CSS_ROW_HEADER + '">' + weekNum + '</th>';
3519 * Renders the row footer for a week.
3520 * @method renderRowFooter
3521 * @param {Number} weekNum The week number of the current row
3522 * @param {Array} cell The current working HTML array
3524 renderRowFooter : function(weekNum, html) {
3525 html[html.length] = '<th class="' + this.Style.CSS_ROW_FOOTER + '">' + weekNum + '</th>';
3530 * Renders a single standard calendar cell in the calendar widget table.
3531 * All logic for determining how a standard default cell will be rendered is
3532 * encapsulated in this method, and must be accounted for when extending the
3534 * @method renderCellDefault
3535 * @param {Date} workingDate The current working Date object being used to generate the calendar
3536 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3538 renderCellDefault : function(workingDate, cell) {
3539 cell.innerHTML = '<a href="#" class="' + this.Style.CSS_CELL_SELECTOR + '">' + this.buildDayLabel(workingDate) + "</a>";
3543 * Styles a selectable cell.
3544 * @method styleCellDefault
3545 * @param {Date} workingDate The current working Date object being used to generate the calendar
3546 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3548 styleCellDefault : function(workingDate, cell) {
3549 Dom.addClass(cell, this.Style.CSS_CELL_SELECTABLE);
3554 * Renders a single standard calendar cell using the CSS hightlight1 style
3555 * @method renderCellStyleHighlight1
3556 * @param {Date} workingDate The current working Date object being used to generate the calendar
3557 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3559 renderCellStyleHighlight1 : function(workingDate, cell) {
3560 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
3564 * Renders a single standard calendar cell using the CSS hightlight2 style
3565 * @method renderCellStyleHighlight2
3566 * @param {Date} workingDate The current working Date object being used to generate the calendar
3567 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3569 renderCellStyleHighlight2 : function(workingDate, cell) {
3570 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
3574 * Renders a single standard calendar cell using the CSS hightlight3 style
3575 * @method renderCellStyleHighlight3
3576 * @param {Date} workingDate The current working Date object being used to generate the calendar
3577 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3579 renderCellStyleHighlight3 : function(workingDate, cell) {
3580 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
3584 * Renders a single standard calendar cell using the CSS hightlight4 style
3585 * @method renderCellStyleHighlight4
3586 * @param {Date} workingDate The current working Date object being used to generate the calendar
3587 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3589 renderCellStyleHighlight4 : function(workingDate, cell) {
3590 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
3594 * Applies the default style used for rendering today's date to the current calendar cell
3595 * @method renderCellStyleToday
3596 * @param {Date} workingDate The current working Date object being used to generate the calendar
3597 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3599 renderCellStyleToday : function(workingDate, cell) {
3600 Dom.addClass(cell, this.Style.CSS_CELL_TODAY);
3604 * Applies the default style used for rendering selected dates to the current calendar cell
3605 * @method renderCellStyleSelected
3606 * @param {Date} workingDate The current working Date object being used to generate the calendar
3607 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3608 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3609 * should not be terminated
3611 renderCellStyleSelected : function(workingDate, cell) {
3612 Dom.addClass(cell, this.Style.CSS_CELL_SELECTED);
3616 * Applies the default style used for rendering dates that are not a part of the current
3617 * month (preceding or trailing the cells for the current month)
3618 * @method renderCellNotThisMonth
3619 * @param {Date} workingDate The current working Date object being used to generate the calendar
3620 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3621 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3622 * should not be terminated
3624 renderCellNotThisMonth : function(workingDate, cell) {
3625 Dom.addClass(cell, this.Style.CSS_CELL_OOM);
3626 cell.innerHTML=workingDate.getDate();
3627 return Calendar.STOP_RENDER;
3631 * Renders the current calendar cell as a non-selectable "black-out" date using the default
3633 * @method renderBodyCellRestricted
3634 * @param {Date} workingDate The current working Date object being used to generate the calendar
3635 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3636 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3637 * should not be terminated
3639 renderBodyCellRestricted : function(workingDate, cell) {
3640 Dom.addClass(cell, this.Style.CSS_CELL);
3641 Dom.addClass(cell, this.Style.CSS_CELL_RESTRICTED);
3642 cell.innerHTML=workingDate.getDate();
3643 return Calendar.STOP_RENDER;
3646 // END BUILT-IN TABLE CELL RENDERERS
3648 // BEGIN MONTH NAVIGATION METHODS
3651 * Adds the designated number of months to the current calendar month, and sets the current
3652 * calendar page date to the new month.
3654 * @param {Number} count The number of months to add to the current calendar
3656 addMonths : function(count) {
3657 var cfgPageDate = DEF_CFG.PAGEDATE.key,
3659 prevDate = this.cfg.getProperty(cfgPageDate),
3660 newDate = DateMath.add(prevDate, DateMath.MONTH, count);
3662 this.cfg.setProperty(cfgPageDate, newDate);
3663 this.resetRenderers();
3664 this.changePageEvent.fire(prevDate, newDate);
3668 * Subtracts the designated number of months from the current calendar month, and sets the current
3669 * calendar page date to the new month.
3670 * @method subtractMonths
3671 * @param {Number} count The number of months to subtract from the current calendar
3673 subtractMonths : function(count) {
3674 this.addMonths(-1*count);
3678 * Adds the designated number of years to the current calendar, and sets the current
3679 * calendar page date to the new month.
3681 * @param {Number} count The number of years to add to the current calendar
3683 addYears : function(count) {
3684 var cfgPageDate = DEF_CFG.PAGEDATE.key,
3686 prevDate = this.cfg.getProperty(cfgPageDate),
3687 newDate = DateMath.add(prevDate, DateMath.YEAR, count);
3689 this.cfg.setProperty(cfgPageDate, newDate);
3690 this.resetRenderers();
3691 this.changePageEvent.fire(prevDate, newDate);
3695 * Subtcats the designated number of years from the current calendar, and sets the current
3696 * calendar page date to the new month.
3697 * @method subtractYears
3698 * @param {Number} count The number of years to subtract from the current calendar
3700 subtractYears : function(count) {
3701 this.addYears(-1*count);
3705 * Navigates to the next month page in the calendar widget.
3708 nextMonth : function() {
3713 * Navigates to the previous month page in the calendar widget.
3714 * @method previousMonth
3716 previousMonth : function() {
3721 * Navigates to the next year in the currently selected month in the calendar widget.
3724 nextYear : function() {
3729 * Navigates to the previous year in the currently selected month in the calendar widget.
3730 * @method previousYear
3732 previousYear : function() {
3736 // END MONTH NAVIGATION METHODS
3738 // BEGIN SELECTION METHODS
3741 * Resets the calendar widget to the originally selected month and year, and
3742 * sets the calendar to the initial selection(s).
3745 reset : function() {
3746 this.cfg.resetProperty(DEF_CFG.SELECTED.key);
3747 this.cfg.resetProperty(DEF_CFG.PAGEDATE.key);
3748 this.resetEvent.fire();
3752 * Clears the selected dates in the current calendar widget and sets the calendar
3753 * to the current month and year.
3756 clear : function() {
3757 this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
3758 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.today.getTime()));
3759 this.clearEvent.fire();
3763 * Selects a date or a collection of dates on the current calendar. This method, by default,
3764 * does not call the render method explicitly. Once selection has completed, render must be
3765 * called for the changes to be reflected visually.
3767 * Any dates which are OOB (out of bounds, not selectable) will not be selected and the array of
3768 * selected dates passed to the selectEvent will not contain OOB dates.
3770 * If all dates are OOB, the no state change will occur; beforeSelect and select events will not be fired.
3773 * @param {String/Date/Date[]} date The date string of dates to select in the current calendar. Valid formats are
3774 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3775 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3776 * This method can also take a JavaScript Date object or an array of Date objects.
3777 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3779 select : function(date) {
3781 var aToBeSelected = this._toFieldArray(date),
3784 cfgSelected = DEF_CFG.SELECTED.key;
3787 for (var a=0; a < aToBeSelected.length; ++a) {
3788 var toSelect = aToBeSelected[a];
3790 if (!this.isDateOOB(this._toDate(toSelect))) {
3792 if (validDates.length === 0) {
3793 this.beforeSelectEvent.fire();
3794 selected = this.cfg.getProperty(cfgSelected);
3796 validDates.push(toSelect);
3798 if (this._indexOfSelectedFieldArray(toSelect) == -1) {
3799 selected[selected.length] = toSelect;
3805 if (validDates.length > 0) {
3807 this.parent.cfg.setProperty(cfgSelected, selected);
3809 this.cfg.setProperty(cfgSelected, selected);
3811 this.selectEvent.fire(validDates);
3814 return this.getSelectedDates();
3818 * Selects a date on the current calendar by referencing the index of the cell that should be selected.
3819 * This method is used to easily select a single cell (usually with a mouse click) without having to do
3820 * a full render. The selected style is applied to the cell directly.
3822 * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month
3823 * or out of bounds cells), it will not be selected and in such a case beforeSelect and select events will not be fired.
3825 * @method selectCell
3826 * @param {Number} cellIndex The index of the cell to select in the current calendar.
3827 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3829 selectCell : function(cellIndex) {
3831 var cell = this.cells[cellIndex],
3832 cellDate = this.cellDates[cellIndex],
3833 dCellDate = this._toDate(cellDate),
3834 selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3839 this.beforeSelectEvent.fire();
3841 var cfgSelected = DEF_CFG.SELECTED.key;
3842 var selected = this.cfg.getProperty(cfgSelected);
3844 var selectDate = cellDate.concat();
3846 if (this._indexOfSelectedFieldArray(selectDate) == -1) {
3847 selected[selected.length] = selectDate;
3850 this.parent.cfg.setProperty(cfgSelected, selected);
3852 this.cfg.setProperty(cfgSelected, selected);
3854 this.renderCellStyleSelected(dCellDate,cell);
3855 this.selectEvent.fire([selectDate]);
3857 this.doCellMouseOut.call(cell, null, this);
3860 return this.getSelectedDates();
3864 * Deselects a date or a collection of dates on the current calendar. This method, by default,
3865 * does not call the render method explicitly. Once deselection has completed, render must be
3866 * called for the changes to be reflected visually.
3868 * The method will not attempt to deselect any dates which are OOB (out of bounds, and hence not selectable)
3869 * and the array of deselected dates passed to the deselectEvent will not contain any OOB dates.
3871 * If all dates are OOB, beforeDeselect and deselect events will not be fired.
3874 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
3875 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3876 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3877 * This method can also take a JavaScript Date object or an array of Date objects.
3878 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3880 deselect : function(date) {
3882 var aToBeDeselected = this._toFieldArray(date),
3885 cfgSelected = DEF_CFG.SELECTED.key;
3888 for (var a=0; a < aToBeDeselected.length; ++a) {
3889 var toDeselect = aToBeDeselected[a];
3891 if (!this.isDateOOB(this._toDate(toDeselect))) {
3893 if (validDates.length === 0) {
3894 this.beforeDeselectEvent.fire();
3895 selected = this.cfg.getProperty(cfgSelected);
3898 validDates.push(toDeselect);
3900 var index = this._indexOfSelectedFieldArray(toDeselect);
3902 selected.splice(index,1);
3908 if (validDates.length > 0) {
3910 this.parent.cfg.setProperty(cfgSelected, selected);
3912 this.cfg.setProperty(cfgSelected, selected);
3914 this.deselectEvent.fire(validDates);
3917 return this.getSelectedDates();
3921 * Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
3922 * This method is used to easily deselect a single cell (usually with a mouse click) without having to do
3923 * a full render. The selected style is removed from the cell directly.
3925 * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month
3926 * or out of bounds cells), the method will not attempt to deselect it and in such a case, beforeDeselect and
3927 * deselect events will not be fired.
3929 * @method deselectCell
3930 * @param {Number} cellIndex The index of the cell to deselect in the current calendar.
3931 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3933 deselectCell : function(cellIndex) {
3934 var cell = this.cells[cellIndex],
3935 cellDate = this.cellDates[cellIndex],
3936 cellDateIndex = this._indexOfSelectedFieldArray(cellDate);
3938 var selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3942 this.beforeDeselectEvent.fire();
3944 var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key),
3945 dCellDate = this._toDate(cellDate),
3946 selectDate = cellDate.concat();
3948 if (cellDateIndex > -1) {
3949 if (this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth() == dCellDate.getMonth() &&
3950 this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getFullYear() == dCellDate.getFullYear()) {
3951 Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
3953 selected.splice(cellDateIndex, 1);
3957 this.parent.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
3959 this.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
3962 this.deselectEvent.fire([selectDate]);
3965 return this.getSelectedDates();
3969 * Deselects all dates on the current calendar.
3970 * @method deselectAll
3971 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3972 * Assuming that this function executes properly, the return value should be an empty array.
3973 * However, the empty array is returned for the sake of being able to check the selection status
3976 deselectAll : function() {
3977 this.beforeDeselectEvent.fire();
3979 var cfgSelected = DEF_CFG.SELECTED.key,
3980 selected = this.cfg.getProperty(cfgSelected),
3981 count = selected.length,
3982 sel = selected.concat();
3985 this.parent.cfg.setProperty(cfgSelected, []);
3987 this.cfg.setProperty(cfgSelected, []);
3991 this.deselectEvent.fire(sel);
3994 return this.getSelectedDates();
3997 // END SELECTION METHODS
3999 // BEGIN TYPE CONVERSION METHODS
4002 * Converts a date (either a JavaScript Date object, or a date string) to the internal data structure
4003 * used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]].
4004 * @method _toFieldArray
4006 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
4007 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
4008 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
4009 * This method can also take a JavaScript Date object or an array of Date objects.
4010 * @return {Array[](Number[])} Array of date field arrays
4012 _toFieldArray : function(date) {
4013 var returnDate = [];
4015 if (date instanceof Date) {
4016 returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]];
4017 } else if (Lang.isString(date)) {
4018 returnDate = this._parseDates(date);
4019 } else if (Lang.isArray(date)) {
4020 for (var i=0;i<date.length;++i) {
4022 returnDate[returnDate.length] = [d.getFullYear(),d.getMonth()+1,d.getDate()];
4030 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object. The date field array
4031 * is the format in which dates are as provided as arguments to selectEvent and deselectEvent listeners.
4034 * @param {Number[]} dateFieldArray The date field array to convert to a JavaScript Date.
4035 * @return {Date} JavaScript Date object representing the date field array.
4037 toDate : function(dateFieldArray) {
4038 return this._toDate(dateFieldArray);
4042 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
4045 * @deprecated Made public, toDate
4046 * @param {Number[]} dateFieldArray The date field array to convert to a JavaScript Date.
4047 * @return {Date} JavaScript Date object representing the date field array
4049 _toDate : function(dateFieldArray) {
4050 if (dateFieldArray instanceof Date) {
4051 return dateFieldArray;
4053 return DateMath.getDate(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]);
4057 // END TYPE CONVERSION METHODS
4059 // BEGIN UTILITY METHODS
4062 * Determines if 2 field arrays are equal.
4063 * @method _fieldArraysAreEqual
4065 * @param {Number[]} array1 The first date field array to compare
4066 * @param {Number[]} array2 The first date field array to compare
4067 * @return {Boolean} The boolean that represents the equality of the two arrays
4069 _fieldArraysAreEqual : function(array1, array2) {
4072 if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2]) {
4080 * Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates.
4081 * @method _indexOfSelectedFieldArray
4083 * @param {Number[]} find The date field array to search for
4084 * @return {Number} The index of the date field array within the collection of selected dates.
4085 * -1 will be returned if the date is not found.
4087 _indexOfSelectedFieldArray : function(find) {
4089 seldates = this.cfg.getProperty(DEF_CFG.SELECTED.key);
4091 for (var s=0;s<seldates.length;++s) {
4092 var sArray = seldates[s];
4093 if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2]) {
4103 * Determines whether a given date is OOM (out of month).
4105 * @param {Date} date The JavaScript Date object for which to check the OOM status
4106 * @return {Boolean} true if the date is OOM
4108 isDateOOM : function(date) {
4109 return (date.getMonth() != this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth());
4113 * Determines whether a given date is OOB (out of bounds - less than the mindate or more than the maxdate).
4116 * @param {Date} date The JavaScript Date object for which to check the OOB status
4117 * @return {Boolean} true if the date is OOB
4119 isDateOOB : function(date) {
4120 var minDate = this.cfg.getProperty(DEF_CFG.MINDATE.key),
4121 maxDate = this.cfg.getProperty(DEF_CFG.MAXDATE.key),
4125 minDate = dm.clearTime(minDate);
4128 maxDate = dm.clearTime(maxDate);
4131 var clearedDate = new Date(date.getTime());
4132 clearedDate = dm.clearTime(clearedDate);
4134 return ((minDate && clearedDate.getTime() < minDate.getTime()) || (maxDate && clearedDate.getTime() > maxDate.getTime()));
4138 * Parses a pagedate configuration property value. The value can either be specified as a string of form "mm/yyyy" or a Date object
4139 * 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
4140 * @method _parsePageDate
4142 * @param {Date|String} date Pagedate value which needs to be parsed
4143 * @return {Date} The Date object representing the pagedate
4145 _parsePageDate : function(date) {
4149 if (date instanceof Date) {
4150 parsedDate = DateMath.findMonthStart(date);
4152 var month, year, aMonthYear;
4153 aMonthYear = date.split(this.cfg.getProperty(DEF_CFG.DATE_FIELD_DELIMITER.key));
4154 month = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_MONTH_POSITION.key)-1], 10)-1;
4155 year = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_YEAR_POSITION.key)-1], 10) - this.Locale.YEAR_OFFSET;
4157 parsedDate = DateMath.getDate(year, month, 1);
4160 parsedDate = DateMath.getDate(this.today.getFullYear(), this.today.getMonth(), 1);
4165 // END UTILITY METHODS
4167 // BEGIN EVENT HANDLERS
4170 * Event executed before a date is selected in the calendar widget.
4171 * @deprecated Event handlers for this event should be susbcribed to beforeSelectEvent.
4173 onBeforeSelect : function() {
4174 if (this.cfg.getProperty(DEF_CFG.MULTI_SELECT.key) === false) {
4176 this.parent.callChildFunction("clearAllBodyCellStyles", this.Style.CSS_CELL_SELECTED);
4177 this.parent.deselectAll();
4179 this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED);
4186 * Event executed when a date is selected in the calendar widget.
4187 * @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] ]
4188 * @deprecated Event handlers for this event should be susbcribed to selectEvent.
4190 onSelect : function(selected) { },
4193 * Event executed before a date is deselected in the calendar widget.
4194 * @deprecated Event handlers for this event should be susbcribed to beforeDeselectEvent.
4196 onBeforeDeselect : function() { },
4199 * Event executed when a date is deselected in the calendar widget.
4200 * @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] ]
4201 * @deprecated Event handlers for this event should be susbcribed to deselectEvent.
4203 onDeselect : function(deselected) { },
4206 * Event executed when the user navigates to a different calendar page.
4207 * @deprecated Event handlers for this event should be susbcribed to changePageEvent.
4209 onChangePage : function() {
4214 * Event executed when the calendar widget is rendered.
4215 * @deprecated Event handlers for this event should be susbcribed to renderEvent.
4217 onRender : function() { },
4220 * Event executed when the calendar widget is reset to its original state.
4221 * @deprecated Event handlers for this event should be susbcribed to resetEvemt.
4223 onReset : function() { this.render(); },
4226 * Event executed when the calendar widget is completely cleared to the current month with no selections.
4227 * @deprecated Event handlers for this event should be susbcribed to clearEvent.
4229 onClear : function() { this.render(); },
4232 * Validates the calendar widget. This method has no default implementation
4233 * and must be extended by subclassing the widget.
4234 * @return Should return true if the widget validates, and false if
4238 validate : function() { return true; },
4240 // END EVENT HANDLERS
4242 // BEGIN DATE PARSE METHODS
4245 * Converts a date string to a date field array
4247 * @param {String} sDate Date string. Valid formats are mm/dd and mm/dd/yyyy.
4248 * @return A date field array representing the string passed to the method
4249 * @type Array[](Number[])
4251 _parseDate : function(sDate) {
4252 var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER),
4255 if (aDate.length == 2) {
4256 rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
4257 rArray.type = Calendar.MONTH_DAY;
4259 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]];
4260 rArray.type = Calendar.DATE;
4263 for (var i=0;i<rArray.length;i++) {
4264 rArray[i] = parseInt(rArray[i], 10);
4271 * Converts a multi or single-date string to an array of date field arrays
4273 * @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
4274 * @return An array of date field arrays
4275 * @type Array[](Number[])
4277 _parseDates : function(sDates) {
4279 aDates = sDates.split(this.Locale.DATE_DELIMITER);
4281 for (var d=0;d<aDates.length;++d) {
4282 var sDate = aDates[d];
4284 if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) {
4286 var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER),
4287 dateStart = this._parseDate(aRange[0]),
4288 dateEnd = this._parseDate(aRange[1]),
4289 fullRange = this._parseRange(dateStart, dateEnd);
4291 aReturn = aReturn.concat(fullRange);
4293 // This is not a range
4294 var aDate = this._parseDate(sDate);
4295 aReturn.push(aDate);
4302 * Converts a date range to the full list of included dates
4304 * @param {Number[]} startDate Date field array representing the first date in the range
4305 * @param {Number[]} endDate Date field array representing the last date in the range
4306 * @return An array of date field arrays
4307 * @type Array[](Number[])
4309 _parseRange : function(startDate, endDate) {
4310 var dCurrent = DateMath.add(DateMath.getDate(startDate[0],startDate[1]-1,startDate[2]),DateMath.DAY,1),
4311 dEnd = DateMath.getDate(endDate[0], endDate[1]-1, endDate[2]),
4314 results.push(startDate);
4315 while (dCurrent.getTime() <= dEnd.getTime()) {
4316 results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
4317 dCurrent = DateMath.add(dCurrent,DateMath.DAY,1);
4322 // END DATE PARSE METHODS
4324 // BEGIN RENDERER METHODS
4327 * Resets the render stack of the current calendar to its original pre-render value.
4329 resetRenderers : function() {
4330 this.renderStack = this._renderStack.concat();
4334 * Removes all custom renderers added to the Calendar through the addRenderer, addMonthRenderer and
4335 * addWeekdayRenderer methods. Calendar's render method needs to be called after removing renderers
4336 * to re-render the Calendar without custom renderers applied.
4338 removeRenderers : function() {
4339 this._renderStack = [];
4340 this.renderStack = [];
4344 * Clears the inner HTML, CSS class and style information from the specified cell.
4345 * @method clearElement
4346 * @param {HTMLTableCellElement} cell The cell to clear
4348 clearElement : function(cell) {
4349 cell.innerHTML = " ";
4354 * Adds a renderer to the render stack. The function reference passed to this method will be executed
4355 * when a date cell matches the conditions specified in the date string for this renderer.
4356 * @method addRenderer
4357 * @param {String} sDates A date string to associate with the specified renderer. Valid formats
4358 * include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
4359 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4361 addRenderer : function(sDates, fnRender) {
4362 var aDates = this._parseDates(sDates);
4363 for (var i=0;i<aDates.length;++i) {
4364 var aDate = aDates[i];
4366 if (aDate.length == 2) { // this is either a range or a month/day combo
4367 if (aDate[0] instanceof Array) { // this is a range
4368 this._addRenderer(Calendar.RANGE,aDate,fnRender);
4369 } else { // this is a month/day combo
4370 this._addRenderer(Calendar.MONTH_DAY,aDate,fnRender);
4372 } else if (aDate.length == 3) {
4373 this._addRenderer(Calendar.DATE,aDate,fnRender);
4379 * The private method used for adding cell renderers to the local render stack.
4380 * This method is called by other methods that set the renderer type prior to the method call.
4381 * @method _addRenderer
4383 * @param {String} type The type string that indicates the type of date renderer being added.
4384 * Values are YAHOO.widget.Calendar.DATE, YAHOO.widget.Calendar.MONTH_DAY, YAHOO.widget.Calendar.WEEKDAY,
4385 * YAHOO.widget.Calendar.RANGE, YAHOO.widget.Calendar.MONTH
4386 * @param {Array} aDates An array of dates used to construct the renderer. The format varies based
4387 * on the renderer type
4388 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4390 _addRenderer : function(type, aDates, fnRender) {
4391 var add = [type,aDates,fnRender];
4392 this.renderStack.unshift(add);
4393 this._renderStack = this.renderStack.concat();
4397 * Adds a month to the render stack. The function reference passed to this method will be executed
4398 * when a date cell matches the month passed to this method.
4399 * @method addMonthRenderer
4400 * @param {Number} month The month (1-12) to associate with this renderer
4401 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4403 addMonthRenderer : function(month, fnRender) {
4404 this._addRenderer(Calendar.MONTH,[month],fnRender);
4408 * Adds a weekday to the render stack. The function reference passed to this method will be executed
4409 * when a date cell matches the weekday passed to this method.
4410 * @method addWeekdayRenderer
4411 * @param {Number} weekday The weekday (Sunday = 1, Monday = 2 ... Saturday = 7) to associate with this renderer
4412 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4414 addWeekdayRenderer : function(weekday, fnRender) {
4415 this._addRenderer(Calendar.WEEKDAY,[weekday],fnRender);
4418 // END RENDERER METHODS
4420 // BEGIN CSS METHODS
4423 * Removes all styles from all body cells in the current calendar table.
4424 * @method clearAllBodyCellStyles
4425 * @param {style} style The CSS class name to remove from all calendar body cells
4427 clearAllBodyCellStyles : function(style) {
4428 for (var c=0;c<this.cells.length;++c) {
4429 Dom.removeClass(this.cells[c],style);
4435 // BEGIN GETTER/SETTER METHODS
4437 * Sets the calendar's month explicitly
4439 * @param {Number} month The numeric month, from 0 (January) to 11 (December)
4441 setMonth : function(month) {
4442 var cfgPageDate = DEF_CFG.PAGEDATE.key,
4443 current = this.cfg.getProperty(cfgPageDate);
4444 current.setMonth(parseInt(month, 10));
4445 this.cfg.setProperty(cfgPageDate, current);
4449 * Sets the calendar's year explicitly.
4451 * @param {Number} year The numeric 4-digit year
4453 setYear : function(year) {
4454 var cfgPageDate = DEF_CFG.PAGEDATE.key,
4455 current = this.cfg.getProperty(cfgPageDate);
4457 current.setFullYear(parseInt(year, 10) - this.Locale.YEAR_OFFSET);
4458 this.cfg.setProperty(cfgPageDate, current);
4462 * Gets the list of currently selected dates from the calendar.
4463 * @method getSelectedDates
4464 * @return {Date[]} An array of currently selected JavaScript Date objects.
4466 getSelectedDates : function() {
4467 var returnDates = [],
4468 selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
4470 for (var d=0;d<selected.length;++d) {
4471 var dateArray = selected[d];
4473 var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
4474 returnDates.push(date);
4477 returnDates.sort( function(a,b) { return a-b; } );
4481 /// END GETTER/SETTER METHODS ///
4484 * Hides the Calendar's outer container from view.
4488 if (this.beforeHideEvent.fire()) {
4489 this.oDomContainer.style.display = "none";
4490 this.hideEvent.fire();
4495 * Shows the Calendar's outer container.
4499 if (this.beforeShowEvent.fire()) {
4500 this.oDomContainer.style.display = "block";
4501 this.showEvent.fire();
4506 * Returns a string representing the current browser.
4507 * @deprecated As of 2.3.0, environment information is available in YAHOO.env.ua
4512 browser : (function() {
4513 var ua = navigator.userAgent.toLowerCase();
4514 if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof)
4516 } else if (ua.indexOf('msie 7')!=-1) { // IE7
4518 } else if (ua.indexOf('msie') !=-1) { // IE
4520 } else if (ua.indexOf('safari')!=-1) { // Safari (check before Gecko because it includes "like Gecko")
4522 } else if (ua.indexOf('gecko') != -1) { // Gecko
4529 * Returns a string representation of the object.
4531 * @return {String} A string representation of the Calendar object.
4533 toString : function() {
4534 return "Calendar " + this.id;
4538 * Destroys the Calendar instance. The method will remove references
4539 * to HTML elements, remove any event listeners added by the Calendar,
4540 * and destroy the Config and CalendarNavigator instances it has created.
4544 destroy : function() {
4546 if (this.beforeDestroyEvent.fire()) {
4550 if (cal.navigator) {
4551 cal.navigator.destroy();
4558 // DOM event listeners
4559 Event.purgeElement(cal.oDomContainer, true);
4561 // Generated markup/DOM - Not removing the container DIV since we didn't create it.
4562 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_WITH_TITLE);
4563 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_CONTAINER);
4564 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_SINGLE);
4565 cal.oDomContainer.innerHTML = "";
4567 // JS-to-DOM references
4568 cal.oDomContainer = null;
4571 this.destroyEvent.fire();
4576 YAHOO.widget.Calendar = Calendar;
4579 * @namespace YAHOO.widget
4580 * @class Calendar_Core
4581 * @extends YAHOO.widget.Calendar
4582 * @deprecated The old Calendar_Core class is no longer necessary.
4584 YAHOO.widget.Calendar_Core = YAHOO.widget.Calendar;
4586 YAHOO.widget.Cal_Core = YAHOO.widget.Calendar;
4591 var Dom = YAHOO.util.Dom,
4592 DateMath = YAHOO.widget.DateMath,
4593 Event = YAHOO.util.Event,
4595 Calendar = YAHOO.widget.Calendar;
4598 * YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar. This class facilitates
4599 * the ability to have multi-page calendar views that share a single dataset and are
4600 * dependent on each other.
4602 * The calendar group instance will refer to each of its elements using a 0-based index.
4603 * For example, to construct the placeholder for a calendar group widget with id "cal1" and
4604 * containerId of "cal1Container", the markup would be as follows:
4606 * <div id="cal1Container_0"></div>
4607 * <div id="cal1Container_1"></div>
4609 * The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers.
4612 * <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
4613 * The CalendarGroup can be constructed by simply providing a container ID string,
4614 * or a reference to a container DIV HTMLElement (the element needs to exist
4619 * var c = new YAHOO.widget.CalendarGroup("calContainer", configOptions);
4623 * var containerDiv = YAHOO.util.Dom.get("calContainer");
4624 * var c = new YAHOO.widget.CalendarGroup(containerDiv, configOptions);
4628 * If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
4629 * 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".
4632 * @namespace YAHOO.widget
4633 * @class CalendarGroup
4635 * @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.
4636 * @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.
4637 * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
4639 function CalendarGroup(id, containerId, config) {
4640 if (arguments.length > 0) {
4641 this.init.apply(this, arguments);
4646 * The set of default Config property keys and values for the CalendarGroup.
4649 * NOTE: This property is made public in order to allow users to change
4650 * the default values of configuration properties. Users should not
4651 * modify the key string, unless they are overriding the Calendar implementation
4654 * @property YAHOO.widget.CalendarGroup.DEFAULT_CONFIG
4656 * @type Object An object with key/value pairs, the key being the
4657 * uppercase configuration property name and the value being an objec
4658 * literal with a key string property, and a value property, specifying the
4659 * default value of the property
4663 * The set of default Config property keys and values for the CalendarGroup
4664 * @property YAHOO.widget.CalendarGroup._DEFAULT_CONFIG
4665 * @deprecated Made public. See the public DEFAULT_CONFIG property for details
4670 CalendarGroup.DEFAULT_CONFIG = CalendarGroup._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG;
4671 CalendarGroup.DEFAULT_CONFIG.PAGES = {key:"pages", value:2};
4673 var DEF_CFG = CalendarGroup.DEFAULT_CONFIG;
4675 CalendarGroup.prototype = {
4678 * Initializes the calendar group. All subclasses must call this method in order for the
4679 * group to be initialized properly.
4681 * @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.
4682 * @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.
4683 * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
4685 init : function(id, container, config) {
4687 // Normalize 2.4.0, pre 2.4.0 args
4688 var nArgs = this._parseArgs(arguments);
4691 container = nArgs.container;
4692 config = nArgs.config;
4694 this.oDomContainer = Dom.get(container);
4696 if (!this.oDomContainer.id) {
4697 this.oDomContainer.id = Dom.generateId();
4700 id = this.oDomContainer.id + "_t";
4704 * The unique id associated with the CalendarGroup
4711 * The unique id associated with the CalendarGroup container
4712 * @property containerId
4715 this.containerId = this.oDomContainer.id;
4721 * The collection of Calendar pages contained within the CalendarGroup
4723 * @type YAHOO.widget.Calendar[]
4727 Dom.addClass(this.oDomContainer, CalendarGroup.CSS_CONTAINER);
4728 Dom.addClass(this.oDomContainer, CalendarGroup.CSS_MULTI_UP);
4731 * The Config object used to hold the configuration variables for the CalendarGroup
4733 * @type YAHOO.util.Config
4735 this.cfg = new YAHOO.util.Config(this);
4738 * The local object which contains the CalendarGroup's options
4745 * The local object which contains the CalendarGroup's locale settings
4754 this.cfg.applyConfig(config, true);
4757 this.cfg.fireQueue();
4759 // OPERA HACK FOR MISWRAPPED FLOATS
4760 if (YAHOO.env.ua.opera){
4761 this.renderEvent.subscribe(this._fixWidth, this, true);
4762 this.showEvent.subscribe(this._fixWidth, this, true);
4767 setupConfig : function() {
4772 * The number of pages to include in the CalendarGroup. This value can only be set once, in the CalendarGroup's constructor arguments.
4777 cfg.addProperty(DEF_CFG.PAGES.key, { value:DEF_CFG.PAGES.value, validator:cfg.checkNumber, handler:this.configPages } );
4780 * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to
4781 * be used when displaying or parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by
4782 * methods will always represent the Gregorian year, in order to maintain date/month/week values.
4784 * @config year_offset
4788 cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, handler: this.delegateConfig, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, suppressEvent:true } );
4791 * The date to use to represent "Today".
4795 * @default Today's date
4797 cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler: this.configToday, suppressEvent:false } );
4800 * The month/year representing the current visible Calendar date (mm/yyyy)
4802 * @type String | Date
4803 * @default Today's date
4805 cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } );
4808 * The date or range of dates representing the current Calendar selection
4814 cfg.addProperty(DEF_CFG.SELECTED.key, { value:[], handler:this.configSelected } );
4817 * The title to display above the CalendarGroup's month header
4822 cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
4825 * Whether or not a close button should be displayed for this CalendarGroup
4830 cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
4833 * 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.
4834 * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be
4835 * enabled if required.
4839 * @default true for IE6 and below, false for all other browsers
4841 cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
4844 * The minimum selectable date in the current Calendar (mm/dd/yyyy)
4846 * @type String | Date
4849 cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.delegateConfig } );
4852 * The maximum selectable date in the current Calendar (mm/dd/yyyy)
4854 * @type String | Date
4857 cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.delegateConfig } );
4859 // Options properties
4862 * True if the Calendar should allow multiple selections. False by default.
4863 * @config MULTI_SELECT
4867 cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4870 * The weekday the week begins on. Default is 0 (Sunday).
4871 * @config START_WEEKDAY
4875 cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4878 * True if the Calendar should show weekday labels. True by default.
4879 * @config SHOW_WEEKDAYS
4883 cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4886 * True if the Calendar should show week row headers. False by default.
4887 * @config SHOW_WEEK_HEADER
4891 cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key,{ value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4894 * True if the Calendar should show week row footers. False by default.
4895 * @config SHOW_WEEK_FOOTER
4899 cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4902 * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
4903 * @config HIDE_BLANK_WEEKS
4907 cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key,{ value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4910 * The image that should be used for the left navigation arrow.
4911 * @config NAV_ARROW_LEFT
4913 * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
4916 cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.delegateConfig } );
4919 * The image that should be used for the right navigation arrow.
4920 * @config NAV_ARROW_RIGHT
4922 * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
4925 cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.delegateConfig } );
4927 // Locale properties
4930 * The short month labels for the current locale.
4931 * @config MONTHS_SHORT
4933 * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
4935 cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.delegateConfig } );
4938 * The long month labels for the current locale.
4939 * @config MONTHS_LONG
4941 * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
4943 cfg.addProperty(DEF_CFG.MONTHS_LONG.key, { value:DEF_CFG.MONTHS_LONG.value, handler:this.delegateConfig } );
4946 * The 1-character weekday labels for the current locale.
4947 * @config WEEKDAYS_1CHAR
4949 * @default ["S", "M", "T", "W", "T", "F", "S"]
4951 cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.delegateConfig } );
4954 * The short weekday labels for the current locale.
4955 * @config WEEKDAYS_SHORT
4957 * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
4959 cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.delegateConfig } );
4962 * The medium weekday labels for the current locale.
4963 * @config WEEKDAYS_MEDIUM
4965 * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
4967 cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.delegateConfig } );
4970 * The long weekday labels for the current locale.
4971 * @config WEEKDAYS_LONG
4973 * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
4975 cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.delegateConfig } );
4978 * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
4979 * @config LOCALE_MONTHS
4983 cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.delegateConfig } );
4986 * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
4987 * @config LOCALE_WEEKDAYS
4991 cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.delegateConfig } );
4994 * The value used to delimit individual dates in a date string passed to various Calendar functions.
4995 * @config DATE_DELIMITER
4999 cfg.addProperty(DEF_CFG.DATE_DELIMITER.key, { value:DEF_CFG.DATE_DELIMITER.value, handler:this.delegateConfig } );
5002 * The value used to delimit date fields in a date string passed to various Calendar functions.
5003 * @config DATE_FIELD_DELIMITER
5007 cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key,{ value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.delegateConfig } );
5010 * The value used to delimit date ranges in a date string passed to various Calendar functions.
5011 * @config DATE_RANGE_DELIMITER
5015 cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key,{ value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.delegateConfig } );
5018 * The position of the month in a month/year date string
5019 * @config MY_MONTH_POSITION
5023 cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5026 * The position of the year in a month/year date string
5027 * @config MY_YEAR_POSITION
5031 cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5034 * The position of the month in a month/day date string
5035 * @config MD_MONTH_POSITION
5039 cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5042 * The position of the day in a month/year date string
5043 * @config MD_DAY_POSITION
5047 cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key, { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5050 * The position of the month in a month/day/year date string
5051 * @config MDY_MONTH_POSITION
5055 cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5058 * The position of the day in a month/day/year date string
5059 * @config MDY_DAY_POSITION
5063 cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5066 * The position of the year in a month/day/year date string
5067 * @config MDY_YEAR_POSITION
5071 cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5074 * The position of the month in the month year label string used as the Calendar header
5075 * @config MY_LABEL_MONTH_POSITION
5079 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5082 * The position of the year in the month year label string used as the Calendar header
5083 * @config MY_LABEL_YEAR_POSITION
5087 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5090 * The suffix used after the month when rendering the Calendar header
5091 * @config MY_LABEL_MONTH_SUFFIX
5095 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.delegateConfig } );
5098 * The suffix used after the year when rendering the Calendar header
5099 * @config MY_LABEL_YEAR_SUFFIX
5103 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.delegateConfig } );
5106 * Configuration for the Month Year Navigation UI. By default it is disabled
5111 cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
5114 * The map of UI strings which the CalendarGroup UI uses.
5118 * @default An object with the properties shown below:
5120 * <dt>previousMonth</dt><dd><em>String</em> : The string to use for the "Previous Month" navigation UI. Defaults to "Previous Month".</dd>
5121 * <dt>nextMonth</dt><dd><em>String</em> : The string to use for the "Next Month" navigation UI. Defaults to "Next Month".</dd>
5122 * <dt>close</dt><dd><em>String</em> : The string to use for the close button label. Defaults to "Close".</dd>
5125 cfg.addProperty(DEF_CFG.STRINGS.key, {
5126 value:DEF_CFG.STRINGS.value,
5127 handler:this.configStrings,
5128 validator: function(val) {
5129 return Lang.isObject(val);
5131 supercedes: DEF_CFG.STRINGS.supercedes
5136 * Initializes CalendarGroup's built-in CustomEvents
5137 * @method initEvents
5139 initEvents : function() {
5143 CE = YAHOO.util.CustomEvent;
5146 * Proxy subscriber to subscribe to the CalendarGroup's child Calendars' CustomEvents
5149 * @param {Function} fn The function to subscribe to this CustomEvent
5150 * @param {Object} obj The CustomEvent's scope object
5151 * @param {Boolean} bOverride Whether or not to apply scope correction
5153 var sub = function(fn, obj, bOverride) {
5154 for (var p=0;p<me.pages.length;++p) {
5155 var cal = me.pages[p];
5156 cal[this.type + strEvent].subscribe(fn, obj, bOverride);
5161 * Proxy unsubscriber to unsubscribe from the CalendarGroup's child Calendars' CustomEvents
5164 * @param {Function} fn The function to subscribe to this CustomEvent
5165 * @param {Object} obj The CustomEvent's scope object
5167 var unsub = function(fn, obj) {
5168 for (var p=0;p<me.pages.length;++p) {
5169 var cal = me.pages[p];
5170 cal[this.type + strEvent].unsubscribe(fn, obj);
5174 var defEvents = Calendar._EVENT_TYPES;
5177 * Fired before a date selection is made
5178 * @event beforeSelectEvent
5180 me.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
5181 me.beforeSelectEvent.subscribe = sub; me.beforeSelectEvent.unsubscribe = unsub;
5184 * Fired when a date selection is made
5185 * @event selectEvent
5186 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
5188 me.selectEvent = new CE(defEvents.SELECT);
5189 me.selectEvent.subscribe = sub; me.selectEvent.unsubscribe = unsub;
5192 * Fired before a date or set of dates is deselected
5193 * @event beforeDeselectEvent
5195 me.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
5196 me.beforeDeselectEvent.subscribe = sub; me.beforeDeselectEvent.unsubscribe = unsub;
5199 * Fired when a date or set of dates has been deselected
5200 * @event deselectEvent
5201 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
5203 me.deselectEvent = new CE(defEvents.DESELECT);
5204 me.deselectEvent.subscribe = sub; me.deselectEvent.unsubscribe = unsub;
5207 * Fired when the Calendar page is changed
5208 * @event changePageEvent
5210 me.changePageEvent = new CE(defEvents.CHANGE_PAGE);
5211 me.changePageEvent.subscribe = sub; me.changePageEvent.unsubscribe = unsub;
5214 * Fired before the Calendar is rendered
5215 * @event beforeRenderEvent
5217 me.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
5218 me.beforeRenderEvent.subscribe = sub; me.beforeRenderEvent.unsubscribe = unsub;
5221 * Fired when the Calendar is rendered
5222 * @event renderEvent
5224 me.renderEvent = new CE(defEvents.RENDER);
5225 me.renderEvent.subscribe = sub; me.renderEvent.unsubscribe = unsub;
5228 * Fired when the Calendar is reset
5231 me.resetEvent = new CE(defEvents.RESET);
5232 me.resetEvent.subscribe = sub; me.resetEvent.unsubscribe = unsub;
5235 * Fired when the Calendar is cleared
5238 me.clearEvent = new CE(defEvents.CLEAR);
5239 me.clearEvent.subscribe = sub; me.clearEvent.unsubscribe = unsub;
5242 * Fired just before the CalendarGroup is to be shown
5243 * @event beforeShowEvent
5245 me.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
5248 * Fired after the CalendarGroup is shown
5251 me.showEvent = new CE(defEvents.SHOW);
5254 * Fired just before the CalendarGroup is to be hidden
5255 * @event beforeHideEvent
5257 me.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
5260 * Fired after the CalendarGroup is hidden
5263 me.hideEvent = new CE(defEvents.HIDE);
5266 * Fired just before the CalendarNavigator is to be shown
5267 * @event beforeShowNavEvent
5269 me.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
5272 * Fired after the CalendarNavigator is shown
5273 * @event showNavEvent
5275 me.showNavEvent = new CE(defEvents.SHOW_NAV);
5278 * Fired just before the CalendarNavigator is to be hidden
5279 * @event beforeHideNavEvent
5281 me.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
5284 * Fired after the CalendarNavigator is hidden
5285 * @event hideNavEvent
5287 me.hideNavEvent = new CE(defEvents.HIDE_NAV);
5290 * Fired just before the CalendarNavigator is to be rendered
5291 * @event beforeRenderNavEvent
5293 me.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
5296 * Fired after the CalendarNavigator is rendered
5297 * @event renderNavEvent
5299 me.renderNavEvent = new CE(defEvents.RENDER_NAV);
5302 * Fired just before the CalendarGroup is to be destroyed
5303 * @event beforeDestroyEvent
5305 me.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
5308 * Fired after the CalendarGroup is destroyed. This event should be used
5309 * for notification only. When this event is fired, important CalendarGroup instance
5310 * properties, dom references and event listeners have already been
5311 * removed/dereferenced, and hence the CalendarGroup instance is not in a usable
5314 * @event destroyEvent
5316 me.destroyEvent = new CE(defEvents.DESTROY);
5320 * The default Config handler for the "pages" property
5321 * @method configPages
5322 * @param {String} type The CustomEvent type (usually the property name)
5323 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5324 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5326 configPages : function(type, args, obj) {
5327 var pageCount = args[0],
5328 cfgPageDate = DEF_CFG.PAGEDATE.key,
5331 firstPageDate = null,
5332 groupCalClass = "groupcal",
5333 firstClass = "first-of-type",
5334 lastClass = "last-of-type";
5336 for (var p=0;p<pageCount;++p) {
5337 var calId = this.id + sep + p,
5338 calContainerId = this.containerId + sep + p,
5339 childConfig = this.cfg.getConfig();
5341 childConfig.close = false;
5342 childConfig.title = false;
5343 childConfig.navigator = null;
5346 caldate = new Date(firstPageDate);
5347 this._setMonthOnDate(caldate, caldate.getMonth() + p);
5348 childConfig.pageDate = caldate;
5351 var cal = this.constructChild(calId, calContainerId, childConfig);
5353 Dom.removeClass(cal.oDomContainer, this.Style.CSS_SINGLE);
5354 Dom.addClass(cal.oDomContainer, groupCalClass);
5357 firstPageDate = cal.cfg.getProperty(cfgPageDate);
5358 Dom.addClass(cal.oDomContainer, firstClass);
5361 if (p==(pageCount-1)) {
5362 Dom.addClass(cal.oDomContainer, lastClass);
5368 this.pages[this.pages.length] = cal;
5373 * The default Config handler for the "pagedate" property
5374 * @method configPageDate
5375 * @param {String} type The CustomEvent type (usually the property name)
5376 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5377 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5379 configPageDate : function(type, args, obj) {
5383 var cfgPageDate = DEF_CFG.PAGEDATE.key;
5385 for (var p=0;p<this.pages.length;++p) {
5386 var cal = this.pages[p];
5388 firstPageDate = cal._parsePageDate(val);
5389 cal.cfg.setProperty(cfgPageDate, firstPageDate);
5391 var pageDate = new Date(firstPageDate);
5392 this._setMonthOnDate(pageDate, pageDate.getMonth() + p);
5393 cal.cfg.setProperty(cfgPageDate, pageDate);
5399 * The default Config handler for the CalendarGroup "selected" property
5400 * @method configSelected
5401 * @param {String} type The CustomEvent type (usually the property name)
5402 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5403 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5405 configSelected : function(type, args, obj) {
5406 var cfgSelected = DEF_CFG.SELECTED.key;
5407 this.delegateConfig(type, args, obj);
5408 var selected = (this.pages.length > 0) ? this.pages[0].cfg.getProperty(cfgSelected) : [];
5409 this.cfg.setProperty(cfgSelected, selected, true);
5414 * Delegates a configuration property to the CustomEvents associated with the CalendarGroup's children
5415 * @method delegateConfig
5416 * @param {String} type The CustomEvent type (usually the property name)
5417 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5418 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5420 delegateConfig : function(type, args, obj) {
5424 for (var p=0;p<this.pages.length;p++) {
5425 cal = this.pages[p];
5426 cal.cfg.setProperty(type, val);
5431 * Adds a function to all child Calendars within this CalendarGroup.
5432 * @method setChildFunction
5433 * @param {String} fnName The name of the function
5434 * @param {Function} fn The function to apply to each Calendar page object
5436 setChildFunction : function(fnName, fn) {
5437 var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
5439 for (var p=0;p<pageCount;++p) {
5440 this.pages[p][fnName] = fn;
5445 * Calls a function within all child Calendars within this CalendarGroup.
5446 * @method callChildFunction
5447 * @param {String} fnName The name of the function
5448 * @param {Array} args The arguments to pass to the function
5450 callChildFunction : function(fnName, args) {
5451 var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
5453 for (var p=0;p<pageCount;++p) {
5454 var page = this.pages[p];
5456 var fn = page[fnName];
5457 fn.call(page, args);
5463 * Constructs a child calendar. This method can be overridden if a subclassed version of the default
5464 * calendar is to be used.
5465 * @method constructChild
5466 * @param {String} id The id of the table element that will represent the calendar widget
5467 * @param {String} containerId The id of the container div element that will wrap the calendar table
5468 * @param {Object} config The configuration object containing the Calendar's arguments
5469 * @return {YAHOO.widget.Calendar} The YAHOO.widget.Calendar instance that is constructed
5471 constructChild : function(id,containerId,config) {
5472 var container = document.getElementById(containerId);
5474 container = document.createElement("div");
5475 container.id = containerId;
5476 this.oDomContainer.appendChild(container);
5478 return new Calendar(id,containerId,config);
5482 * Sets the calendar group's month explicitly. This month will be set into the first
5483 * page of the multi-page calendar, and all other months will be iterated appropriately.
5485 * @param {Number} month The numeric month, from 0 (January) to 11 (December)
5487 setMonth : function(month) {
5488 month = parseInt(month, 10);
5491 var cfgPageDate = DEF_CFG.PAGEDATE.key;
5493 for (var p=0; p<this.pages.length; ++p) {
5494 var cal = this.pages[p];
5495 var pageDate = cal.cfg.getProperty(cfgPageDate);
5497 currYear = pageDate.getFullYear();
5499 pageDate.setFullYear(currYear);
5501 this._setMonthOnDate(pageDate, month+p);
5502 cal.cfg.setProperty(cfgPageDate, pageDate);
5507 * Sets the calendar group's year explicitly. This year will be set into the first
5508 * page of the multi-page calendar, and all other months will be iterated appropriately.
5510 * @param {Number} year The numeric 4-digit year
5512 setYear : function(year) {
5514 var cfgPageDate = DEF_CFG.PAGEDATE.key;
5516 year = parseInt(year, 10);
5517 for (var p=0;p<this.pages.length;++p) {
5518 var cal = this.pages[p];
5519 var pageDate = cal.cfg.getProperty(cfgPageDate);
5521 if ((pageDate.getMonth()+1) == 1 && p>0) {
5529 * Calls the render function of all child calendars within the group.
5532 render : function() {
5533 this.renderHeader();
5534 for (var p=0;p<this.pages.length;++p) {
5535 var cal = this.pages[p];
5538 this.renderFooter();
5542 * Selects a date or a collection of dates on the current calendar. This method, by default,
5543 * does not call the render method explicitly. Once selection has completed, render must be
5544 * called for the changes to be reflected visually.
5546 * @param {String/Date/Date[]} date The date string of dates to select in the current calendar. Valid formats are
5547 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
5548 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
5549 * This method can also take a JavaScript Date object or an array of Date objects.
5550 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5552 select : function(date) {
5553 for (var p=0;p<this.pages.length;++p) {
5554 var cal = this.pages[p];
5557 return this.getSelectedDates();
5561 * 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.
5562 * The value of the MULTI_SELECT Configuration attribute will determine the set of dates which get selected.
5564 * <li>If MULTI_SELECT is false, selectCell will select the cell at the specified index for only the last displayed Calendar page.</li>
5565 * <li>If MULTI_SELECT is true, selectCell will select the cell at the specified index, on each displayed Calendar page.</li>
5567 * @method selectCell
5568 * @param {Number} cellIndex The index of the cell to be selected.
5569 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5571 selectCell : function(cellIndex) {
5572 for (var p=0;p<this.pages.length;++p) {
5573 var cal = this.pages[p];
5574 cal.selectCell(cellIndex);
5576 return this.getSelectedDates();
5580 * Deselects a date or a collection of dates on the current calendar. This method, by default,
5581 * does not call the render method explicitly. Once deselection has completed, render must be
5582 * called for the changes to be reflected visually.
5584 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
5585 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
5586 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
5587 * This method can also take a JavaScript Date object or an array of Date objects.
5588 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5590 deselect : function(date) {
5591 for (var p=0;p<this.pages.length;++p) {
5592 var cal = this.pages[p];
5595 return this.getSelectedDates();
5599 * Deselects all dates on the current calendar.
5600 * @method deselectAll
5601 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5602 * Assuming that this function executes properly, the return value should be an empty array.
5603 * However, the empty array is returned for the sake of being able to check the selection status
5606 deselectAll : function() {
5607 for (var p=0;p<this.pages.length;++p) {
5608 var cal = this.pages[p];
5611 return this.getSelectedDates();
5615 * 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.
5616 * deselectCell will deselect the cell at the specified index on each displayed Calendar page.
5618 * @method deselectCell
5619 * @param {Number} cellIndex The index of the cell to deselect.
5620 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5622 deselectCell : function(cellIndex) {
5623 for (var p=0;p<this.pages.length;++p) {
5624 var cal = this.pages[p];
5625 cal.deselectCell(cellIndex);
5627 return this.getSelectedDates();
5631 * Resets the calendar widget to the originally selected month and year, and
5632 * sets the calendar to the initial selection(s).
5635 reset : function() {
5636 for (var p=0;p<this.pages.length;++p) {
5637 var cal = this.pages[p];
5643 * Clears the selected dates in the current calendar widget and sets the calendar
5644 * to the current month and year.
5647 clear : function() {
5648 for (var p=0;p<this.pages.length;++p) {
5649 var cal = this.pages[p];
5653 this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
5654 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.pages[0].today.getTime()));
5659 * Navigates to the next month page in the calendar widget.
5662 nextMonth : function() {
5663 for (var p=0;p<this.pages.length;++p) {
5664 var cal = this.pages[p];
5670 * Navigates to the previous month page in the calendar widget.
5671 * @method previousMonth
5673 previousMonth : function() {
5674 for (var p=this.pages.length-1;p>=0;--p) {
5675 var cal = this.pages[p];
5676 cal.previousMonth();
5681 * Navigates to the next year in the currently selected month in the calendar widget.
5684 nextYear : function() {
5685 for (var p=0;p<this.pages.length;++p) {
5686 var cal = this.pages[p];
5692 * Navigates to the previous year in the currently selected month in the calendar widget.
5693 * @method previousYear
5695 previousYear : function() {
5696 for (var p=0;p<this.pages.length;++p) {
5697 var cal = this.pages[p];
5703 * Gets the list of currently selected dates from the calendar.
5704 * @return An array of currently selected JavaScript Date objects.
5707 getSelectedDates : function() {
5708 var returnDates = [];
5709 var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
5710 for (var d=0;d<selected.length;++d) {
5711 var dateArray = selected[d];
5713 var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
5714 returnDates.push(date);
5717 returnDates.sort( function(a,b) { return a-b; } );
5722 * Adds a renderer to the render stack. The function reference passed to this method will be executed
5723 * when a date cell matches the conditions specified in the date string for this renderer.
5724 * @method addRenderer
5725 * @param {String} sDates A date string to associate with the specified renderer. Valid formats
5726 * include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
5727 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5729 addRenderer : function(sDates, fnRender) {
5730 for (var p=0;p<this.pages.length;++p) {
5731 var cal = this.pages[p];
5732 cal.addRenderer(sDates, fnRender);
5737 * Adds a month to the render stack. The function reference passed to this method will be executed
5738 * when a date cell matches the month passed to this method.
5739 * @method addMonthRenderer
5740 * @param {Number} month The month (1-12) to associate with this renderer
5741 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5743 addMonthRenderer : function(month, fnRender) {
5744 for (var p=0;p<this.pages.length;++p) {
5745 var cal = this.pages[p];
5746 cal.addMonthRenderer(month, fnRender);
5751 * Adds a weekday to the render stack. The function reference passed to this method will be executed
5752 * when a date cell matches the weekday passed to this method.
5753 * @method addWeekdayRenderer
5754 * @param {Number} weekday The weekday (1-7) to associate with this renderer. 1=Sunday, 2=Monday etc.
5755 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5757 addWeekdayRenderer : function(weekday, fnRender) {
5758 for (var p=0;p<this.pages.length;++p) {
5759 var cal = this.pages[p];
5760 cal.addWeekdayRenderer(weekday, fnRender);
5765 * Removes all custom renderers added to the CalendarGroup through the addRenderer, addMonthRenderer and
5766 * addWeekRenderer methods. CalendarGroup's render method needs to be called to after removing renderers
5767 * to see the changes applied.
5769 * @method removeRenderers
5771 removeRenderers : function() {
5772 this.callChildFunction("removeRenderers");
5776 * Renders the header for the CalendarGroup.
5777 * @method renderHeader
5779 renderHeader : function() {
5780 // EMPTY DEFAULT IMPL
5784 * Renders a footer for the 2-up calendar container. By default, this method is
5786 * @method renderFooter
5788 renderFooter : function() {
5789 // EMPTY DEFAULT IMPL
5793 * Adds the designated number of months to the current calendar month, and sets the current
5794 * calendar page date to the new month.
5796 * @param {Number} count The number of months to add to the current calendar
5798 addMonths : function(count) {
5799 this.callChildFunction("addMonths", count);
5803 * Subtracts the designated number of months from the current calendar month, and sets the current
5804 * calendar page date to the new month.
5805 * @method subtractMonths
5806 * @param {Number} count The number of months to subtract from the current calendar
5808 subtractMonths : function(count) {
5809 this.callChildFunction("subtractMonths", count);
5813 * Adds the designated number of years to the current calendar, and sets the current
5814 * calendar page date to the new month.
5816 * @param {Number} count The number of years to add to the current calendar
5818 addYears : function(count) {
5819 this.callChildFunction("addYears", count);
5823 * Subtcats the designated number of years from the current calendar, and sets the current
5824 * calendar page date to the new month.
5825 * @method subtractYears
5826 * @param {Number} count The number of years to subtract from the current calendar
5828 subtractYears : function(count) {
5829 this.callChildFunction("subtractYears", count);
5833 * Returns the Calendar page instance which has a pagedate (month/year) matching the given date.
5834 * Returns null if no match is found.
5836 * @method getCalendarPage
5837 * @param {Date} date The JavaScript Date object for which a Calendar page is to be found.
5838 * @return {Calendar} The Calendar page instance representing the month to which the date
5841 getCalendarPage : function(date) {
5844 var y = date.getFullYear(),
5845 m = date.getMonth();
5847 var pages = this.pages;
5848 for (var i = 0; i < pages.length; ++i) {
5849 var pageDate = pages[i].cfg.getProperty("pagedate");
5850 if (pageDate.getFullYear() === y && pageDate.getMonth() === m) {
5860 * Sets the month on a Date object, taking into account year rollover if the month is less than 0 or greater than 11.
5861 * 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
5862 * @method _setMonthOnDate
5864 * @param {Date} date The Date object on which to set the month index
5865 * @param {Number} iMonth The month index to set
5867 _setMonthOnDate : function(date, iMonth) {
5868 // Bug in Safari 1.3, 2.0 (WebKit build < 420), Date.setMonth does not work consistently if iMonth is not 0-11
5869 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420 && (iMonth < 0 || iMonth > 11)) {
5870 var newDate = DateMath.add(date, DateMath.MONTH, iMonth-date.getMonth());
5871 date.setTime(newDate.getTime());
5873 date.setMonth(iMonth);
5878 * Fixes the width of the CalendarGroup container element, to account for miswrapped floats
5882 _fixWidth : function() {
5884 for (var p=0;p<this.pages.length;++p) {
5885 var cal = this.pages[p];
5886 w += cal.oDomContainer.offsetWidth;
5889 this.oDomContainer.style.width = w + "px";
5894 * Returns a string representation of the object.
5896 * @return {String} A string representation of the CalendarGroup object.
5898 toString : function() {
5899 return "CalendarGroup " + this.id;
5903 * Destroys the CalendarGroup instance. The method will remove references
5904 * to HTML elements, remove any event listeners added by the CalendarGroup.
5906 * It will also destroy the Config and CalendarNavigator instances created by the
5907 * CalendarGroup and the individual Calendar instances created for each page.
5911 destroy : function() {
5913 if (this.beforeDestroyEvent.fire()) {
5918 if (cal.navigator) {
5919 cal.navigator.destroy();
5926 // DOM event listeners
5927 Event.purgeElement(cal.oDomContainer, true);
5929 // Generated markup/DOM - Not removing the container DIV since we didn't create it.
5930 Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_CONTAINER);
5931 Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_MULTI_UP);
5933 for (var i = 0, l = cal.pages.length; i < l; i++) {
5934 cal.pages[i].destroy();
5935 cal.pages[i] = null;
5938 cal.oDomContainer.innerHTML = "";
5940 // JS-to-DOM references
5941 cal.oDomContainer = null;
5943 this.destroyEvent.fire();
5949 * CSS class representing the container for the calendar
5950 * @property YAHOO.widget.CalendarGroup.CSS_CONTAINER
5955 CalendarGroup.CSS_CONTAINER = "yui-calcontainer";
5958 * CSS class representing the container for the calendar
5959 * @property YAHOO.widget.CalendarGroup.CSS_MULTI_UP
5964 CalendarGroup.CSS_MULTI_UP = "multi";
5967 * CSS class representing the title for the 2-up calendar
5968 * @property YAHOO.widget.CalendarGroup.CSS_2UPTITLE
5973 CalendarGroup.CSS_2UPTITLE = "title";
5976 * CSS class representing the close icon for the 2-up calendar
5977 * @property YAHOO.widget.CalendarGroup.CSS_2UPCLOSE
5980 * @deprecated Along with Calendar.IMG_ROOT and NAV_ARROW_LEFT, NAV_ARROW_RIGHT configuration properties.
5981 * 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
5984 CalendarGroup.CSS_2UPCLOSE = "close-icon";
5986 YAHOO.lang.augmentProto(CalendarGroup, Calendar, "buildDayLabel",
5988 "renderOutOfBoundsDate",
5991 "renderCellDefault",
5993 "renderCellStyleHighlight1",
5994 "renderCellStyleHighlight2",
5995 "renderCellStyleHighlight3",
5996 "renderCellStyleHighlight4",
5997 "renderCellStyleToday",
5998 "renderCellStyleSelected",
5999 "renderCellNotThisMonth",
6000 "renderBodyCellRestricted",
6009 "createCloseButton",
6011 "removeCloseButton",
6019 YAHOO.widget.CalGrp = CalendarGroup;
6020 YAHOO.widget.CalendarGroup = CalendarGroup;
6023 * @class YAHOO.widget.Calendar2up
6024 * @extends YAHOO.widget.CalendarGroup
6025 * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
6027 YAHOO.widget.Calendar2up = function(id, containerId, config) {
6028 this.init(id, containerId, config);
6031 YAHOO.extend(YAHOO.widget.Calendar2up, CalendarGroup);
6034 * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
6036 YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up;
6040 * The CalendarNavigator is used along with a Calendar/CalendarGroup to
6041 * provide a Month/Year popup navigation control, allowing the user to navigate
6042 * to a specific month/year in the Calendar/CalendarGroup without having to
6043 * scroll through months sequentially
6045 * @namespace YAHOO.widget
6046 * @class CalendarNavigator
6048 * @param {Calendar|CalendarGroup} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached.
6050 YAHOO.widget.CalendarNavigator = function(cal) {
6055 // Setup static properties (inside anon fn, so that we can use shortcuts)
6056 var CN = YAHOO.widget.CalendarNavigator;
6059 * YAHOO.widget.CalendarNavigator.CLASSES contains constants
6060 * for the class values applied to the CalendarNaviatgator's
6062 * @property YAHOO.widget.CalendarNavigator.CLASSES
6068 * Class applied to the Calendar Navigator's bounding box
6069 * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV
6075 * Class applied to the Calendar/CalendarGroup's bounding box to indicate
6076 * the Navigator is currently visible
6077 * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV_VISIBLE
6081 NAV_VISIBLE: "yui-cal-nav-visible",
6083 * Class applied to the Navigator mask's bounding box
6084 * @property YAHOO.widget.CalendarNavigator.CLASSES.MASK
6088 MASK : "yui-cal-nav-mask",
6090 * Class applied to the year label/control bounding box
6091 * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR
6095 YEAR : "yui-cal-nav-y",
6097 * Class applied to the month label/control bounding box
6098 * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH
6102 MONTH : "yui-cal-nav-m",
6104 * Class applied to the submit/cancel button's bounding box
6105 * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTONS
6109 BUTTONS : "yui-cal-nav-b",
6111 * Class applied to buttons wrapping element
6112 * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTON
6116 BUTTON : "yui-cal-nav-btn",
6118 * Class applied to the validation error area's bounding box
6119 * @property YAHOO.widget.CalendarNavigator.CLASSES.ERROR
6123 ERROR : "yui-cal-nav-e",
6125 * Class applied to the year input control
6126 * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR_CTRL
6130 YEAR_CTRL : "yui-cal-nav-yc",
6132 * Class applied to the month input control
6133 * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH_CTRL
6137 MONTH_CTRL : "yui-cal-nav-mc",
6139 * Class applied to controls with invalid data (e.g. a year input field with invalid an year)
6140 * @property YAHOO.widget.CalendarNavigator.CLASSES.INVALID
6144 INVALID : "yui-invalid",
6146 * Class applied to default controls
6147 * @property YAHOO.widget.CalendarNavigator.CLASSES.DEFAULT
6151 DEFAULT : "yui-default"
6155 * Object literal containing the default configuration values for the CalendarNavigator
6156 * The configuration object is expected to follow the format below, with the properties being
6160 * <dd><em>Object</em> : An object with the properties shown below, defining the string labels to use in the Navigator's UI
6162 * <dt>month</dt><dd><em>String</em> : The string to use for the month label. Defaults to "Month".</dd>
6163 * <dt>year</dt><dd><em>String</em> : The string to use for the year label. Defaults to "Year".</dd>
6164 * <dt>submit</dt><dd><em>String</em> : The string to use for the submit button label. Defaults to "Okay".</dd>
6165 * <dt>cancel</dt><dd><em>String</em> : The string to use for the cancel button label. Defaults to "Cancel".</dd>
6166 * <dt>invalidYear</dt><dd><em>String</em> : The string to use for invalid year values. Defaults to "Year needs to be a number".</dd>
6169 * <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>
6170 * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
6172 * @property DEFAULT_CONFIG
6176 CN.DEFAULT_CONFIG = {
6182 invalidYear : "Year needs to be a number"
6184 monthFormat: YAHOO.widget.Calendar.LONG,
6185 initialFocus: "year"
6189 * Object literal containing the default configuration values for the CalendarNavigator
6190 * @property _DEFAULT_CFG
6192 * @deprecated Made public. See the public DEFAULT_CONFIG property
6196 CN._DEFAULT_CFG = CN.DEFAULT_CONFIG;
6200 * The suffix added to the Calendar/CalendarGroup's ID, to generate
6201 * a unique ID for the Navigator and it's bounding box.
6202 * @property YAHOO.widget.CalendarNavigator.ID_SUFFIX
6207 CN.ID_SUFFIX = "_nav";
6209 * The suffix added to the Navigator's ID, to generate
6210 * a unique ID for the month control.
6211 * @property YAHOO.widget.CalendarNavigator.MONTH_SUFFIX
6216 CN.MONTH_SUFFIX = "_month";
6218 * The suffix added to the Navigator's ID, to generate
6219 * a unique ID for the year control.
6220 * @property YAHOO.widget.CalendarNavigator.YEAR_SUFFIX
6225 CN.YEAR_SUFFIX = "_year";
6227 * The suffix added to the Navigator's ID, to generate
6228 * a unique ID for the error bounding box.
6229 * @property YAHOO.widget.CalendarNavigator.ERROR_SUFFIX
6234 CN.ERROR_SUFFIX = "_error";
6236 * The suffix added to the Navigator's ID, to generate
6237 * a unique ID for the "Cancel" button.
6238 * @property YAHOO.widget.CalendarNavigator.CANCEL_SUFFIX
6243 CN.CANCEL_SUFFIX = "_cancel";
6245 * The suffix added to the Navigator's ID, to generate
6246 * a unique ID for the "Submit" button.
6247 * @property YAHOO.widget.CalendarNavigator.SUBMIT_SUFFIX
6252 CN.SUBMIT_SUFFIX = "_submit";
6255 * The number of digits to which the year input control is to be limited.
6256 * @property YAHOO.widget.CalendarNavigator.YR_MAX_DIGITS
6260 CN.YR_MAX_DIGITS = 4;
6263 * The amount by which to increment the current year value,
6264 * when the arrow up/down key is pressed on the year control
6265 * @property YAHOO.widget.CalendarNavigator.YR_MINOR_INC
6269 CN.YR_MINOR_INC = 1;
6272 * The amount by which to increment the current year value,
6273 * when the page up/down key is pressed on the year control
6274 * @property YAHOO.widget.CalendarNavigator.YR_MAJOR_INC
6278 CN.YR_MAJOR_INC = 10;
6281 * Artificial delay (in ms) between the time the Navigator is hidden
6282 * and the Calendar/CalendarGroup state is updated. Allows the user
6283 * the see the Calendar/CalendarGroup page changing. If set to 0
6284 * the Calendar/CalendarGroup page will be updated instantly
6285 * @property YAHOO.widget.CalendarNavigator.UPDATE_DELAY
6289 CN.UPDATE_DELAY = 50;
6292 * Regular expression used to validate the year input
6293 * @property YAHOO.widget.CalendarNavigator.YR_PATTERN
6297 CN.YR_PATTERN = /^\d+$/;
6299 * Regular expression used to trim strings
6300 * @property YAHOO.widget.CalendarNavigator.TRIM
6304 CN.TRIM = /^\s*(.*?)\s*$/;
6307 YAHOO.widget.CalendarNavigator.prototype = {
6310 * The unique ID for this CalendarNavigator instance
6317 * The Calendar/CalendarGroup instance to which the navigator belongs
6319 * @type {Calendar|CalendarGroup}
6324 * Reference to the HTMLElement used to render the navigator's bounding box
6331 * Reference to the HTMLElement used to render the navigator's mask
6338 * Reference to the HTMLElement used to input the year
6345 * Reference to the HTMLElement used to input the month
6352 * Reference to the HTMLElement used to display validation errors
6359 * Reference to the HTMLElement used to update the Calendar/Calendar group
6360 * with the month/year values
6361 * @property submitEl
6367 * Reference to the HTMLElement used to hide the navigator without updating the
6368 * Calendar/Calendar group
6369 * @property cancelEl
6375 * Reference to the first focusable control in the navigator (by default monthEl)
6376 * @property firstCtrl
6382 * Reference to the last focusable control in the navigator (by default cancelEl)
6383 * @property lastCtrl
6389 * The document containing the Calendar/Calendar group instance
6392 * @type HTMLDocument
6397 * Internal state property for the current year displayed in the navigator
6405 * Internal state property for the current month index displayed in the navigator
6413 * Private internal state property which indicates whether or not the
6414 * Navigator has been rendered.
6416 * @property __rendered
6422 * Init lifecycle method called as part of construction
6425 * @param {Calendar} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached
6427 init : function(cal) {
6428 var calBox = cal.oDomContainer;
6431 this.id = calBox.id + YAHOO.widget.CalendarNavigator.ID_SUFFIX;
6432 this._doc = calBox.ownerDocument;
6435 * Private flag, to identify IE Quirks
6437 * @property __isIEQuirks
6439 var ie = YAHOO.env.ua.ie;
6440 this.__isIEQuirks = (ie && ((ie <= 6) || (this._doc.compatMode == "BackCompat")));
6444 * Displays the navigator and mask, updating the input controls to reflect the
6445 * currently set month and year. The show method will invoke the render method
6446 * if the navigator has not been renderered already, allowing for lazy rendering
6449 * The show method will fire the Calendar/CalendarGroup's beforeShowNav and showNav events
6454 var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
6456 if (this.cal.beforeShowNavEvent.fire()) {
6457 if (!this.__rendered) {
6462 this._updateMonthUI();
6463 this._updateYearUI();
6464 this._show(this.navEl, true);
6466 this.setInitialFocus();
6469 YAHOO.util.Dom.addClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
6470 this.cal.showNavEvent.fire();
6475 * Hides the navigator and mask
6477 * The show method will fire the Calendar/CalendarGroup's beforeHideNav event and hideNav events
6481 var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
6483 if (this.cal.beforeHideNavEvent.fire()) {
6484 this._show(this.navEl, false);
6486 YAHOO.util.Dom.removeClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
6487 this.cal.hideNavEvent.fire();
6493 * Displays the navigator's mask element
6497 showMask : function() {
6498 this._show(this.maskEl, true);
6499 if (this.__isIEQuirks) {
6505 * Hides the navigator's mask element
6509 hideMask : function() {
6510 this._show(this.maskEl, false);
6514 * Returns the current month set on the navigator
6516 * Note: This may not be the month set in the UI, if
6517 * the UI contains an invalid value.
6520 * @return {Number} The Navigator's current month index
6522 getMonth: function() {
6527 * Returns the current year set on the navigator
6529 * Note: This may not be the year set in the UI, if
6530 * the UI contains an invalid value.
6533 * @return {Number} The Navigator's current year value
6535 getYear: function() {
6540 * Sets the current month on the Navigator, and updates the UI
6543 * @param {Number} nMonth The month index, from 0 (Jan) through 11 (Dec).
6545 setMonth : function(nMonth) {
6546 if (nMonth >= 0 && nMonth < 12) {
6547 this._month = nMonth;
6549 this._updateMonthUI();
6553 * Sets the current year on the Navigator, and updates the UI. If the
6554 * provided year is invalid, it will not be set.
6557 * @param {Number} nYear The full year value to set the Navigator to.
6559 setYear : function(nYear) {
6560 var yrPattern = YAHOO.widget.CalendarNavigator.YR_PATTERN;
6561 if (YAHOO.lang.isNumber(nYear) && yrPattern.test(nYear+"")) {
6564 this._updateYearUI();
6568 * Renders the HTML for the navigator, adding it to the
6569 * document and attaches event listeners if it has not
6570 * already been rendered.
6574 render: function() {
6575 this.cal.beforeRenderNavEvent.fire();
6576 if (!this.__rendered) {
6579 this.applyListeners();
6580 this.__rendered = true;
6582 this.cal.renderNavEvent.fire();
6586 * Creates the navigator's containing HTMLElement, it's contents, and appends
6587 * the containg element to the Calendar/CalendarGroup's container.
6591 createNav : function() {
6592 var NAV = YAHOO.widget.CalendarNavigator;
6593 var doc = this._doc;
6595 var d = doc.createElement("div");
6596 d.className = NAV.CLASSES.NAV;
6598 var htmlBuf = this.renderNavContents([]);
6600 d.innerHTML = htmlBuf.join('');
6601 this.cal.oDomContainer.appendChild(d);
6605 this.yearEl = doc.getElementById(this.id + NAV.YEAR_SUFFIX);
6606 this.monthEl = doc.getElementById(this.id + NAV.MONTH_SUFFIX);
6607 this.errorEl = doc.getElementById(this.id + NAV.ERROR_SUFFIX);
6608 this.submitEl = doc.getElementById(this.id + NAV.SUBMIT_SUFFIX);
6609 this.cancelEl = doc.getElementById(this.id + NAV.CANCEL_SUFFIX);
6611 if (YAHOO.env.ua.gecko && this.yearEl && this.yearEl.type == "text") {
6612 // Avoid XUL error on focus, select [ https://bugzilla.mozilla.org/show_bug.cgi?id=236791,
6613 // supposedly fixed in 1.8.1, but there are reports of it still being around for methods other than blur ]
6614 this.yearEl.setAttribute("autocomplete", "off");
6617 this._setFirstLastElements();
6621 * Creates the Mask HTMLElement and appends it to the Calendar/CalendarGroups
6624 * @method createMask
6626 createMask : function() {
6627 var C = YAHOO.widget.CalendarNavigator.CLASSES;
6629 var d = this._doc.createElement("div");
6630 d.className = C.MASK;
6632 this.cal.oDomContainer.appendChild(d);
6637 * Used to set the width/height of the mask in pixels to match the Calendar Container.
6638 * Currently only used for IE6 or IE in quirks mode. The other A-Grade browser are handled using CSS (width/height 100%).
6640 * The method is also registered as an HTMLElement resize listener on the Calendars container element.
6645 _syncMask : function() {
6646 var c = this.cal.oDomContainer;
6647 if (c && this.maskEl) {
6648 var r = YAHOO.util.Dom.getRegion(c);
6649 YAHOO.util.Dom.setStyle(this.maskEl, "width", r.right - r.left + "px");
6650 YAHOO.util.Dom.setStyle(this.maskEl, "height", r.bottom - r.top + "px");
6655 * Renders the contents of the navigator
6657 * @method renderNavContents
6659 * @param {Array} html The HTML buffer to append the HTML to.
6660 * @return {Array} A reference to the buffer passed in.
6662 renderNavContents : function(html) {
6663 var NAV = YAHOO.widget.CalendarNavigator,
6665 h = html; // just to use a shorter name
6667 h[h.length] = '<div class="' + C.MONTH + '">';
6668 this.renderMonth(h);
6669 h[h.length] = '</div>';
6670 h[h.length] = '<div class="' + C.YEAR + '">';
6672 h[h.length] = '</div>';
6673 h[h.length] = '<div class="' + C.BUTTONS + '">';
6674 this.renderButtons(h);
6675 h[h.length] = '</div>';
6676 h[h.length] = '<div class="' + C.ERROR + '" id="' + this.id + NAV.ERROR_SUFFIX + '"></div>';
6682 * Renders the month label and control for the navigator
6684 * @method renderNavContents
6685 * @param {Array} html The HTML buffer to append the HTML to.
6686 * @return {Array} A reference to the buffer passed in.
6688 renderMonth : function(html) {
6689 var NAV = YAHOO.widget.CalendarNavigator,
6692 var id = this.id + NAV.MONTH_SUFFIX,
6693 mf = this.__getCfg("monthFormat"),
6694 months = this.cal.cfg.getProperty((mf == YAHOO.widget.Calendar.SHORT) ? "MONTHS_SHORT" : "MONTHS_LONG"),
6697 if (months && months.length > 0) {
6698 h[h.length] = '<label for="' + id + '">';
6699 h[h.length] = this.__getCfg("month", true);
6700 h[h.length] = '</label>';
6701 h[h.length] = '<select name="' + id + '" id="' + id + '" class="' + C.MONTH_CTRL + '">';
6702 for (var i = 0; i < months.length; i++) {
6703 h[h.length] = '<option value="' + i + '">';
6704 h[h.length] = months[i];
6705 h[h.length] = '</option>';
6707 h[h.length] = '</select>';
6713 * Renders the year label and control for the navigator
6715 * @method renderYear
6716 * @param {Array} html The HTML buffer to append the HTML to.
6717 * @return {Array} A reference to the buffer passed in.
6719 renderYear : function(html) {
6720 var NAV = YAHOO.widget.CalendarNavigator,
6723 var id = this.id + NAV.YEAR_SUFFIX,
6724 size = NAV.YR_MAX_DIGITS,
6727 h[h.length] = '<label for="' + id + '">';
6728 h[h.length] = this.__getCfg("year", true);
6729 h[h.length] = '</label>';
6730 h[h.length] = '<input type="text" name="' + id + '" id="' + id + '" class="' + C.YEAR_CTRL + '" maxlength="' + size + '"/>';
6735 * Renders the submit/cancel buttons for the navigator
6737 * @method renderButton
6738 * @return {String} The HTML created for the Button UI
6740 renderButtons : function(html) {
6741 var C = YAHOO.widget.CalendarNavigator.CLASSES;
6744 h[h.length] = '<span class="' + C.BUTTON + ' ' + C.DEFAULT + '">';
6745 h[h.length] = '<button type="button" id="' + this.id + '_submit' + '">';
6746 h[h.length] = this.__getCfg("submit", true);
6747 h[h.length] = '</button>';
6748 h[h.length] = '</span>';
6749 h[h.length] = '<span class="' + C.BUTTON +'">';
6750 h[h.length] = '<button type="button" id="' + this.id + '_cancel' + '">';
6751 h[h.length] = this.__getCfg("cancel", true);
6752 h[h.length] = '</button>';
6753 h[h.length] = '</span>';
6759 * Attaches DOM event listeners to the rendered elements
6761 * The method will call applyKeyListeners, to setup keyboard specific
6764 * @method applyListeners
6766 applyListeners : function() {
6767 var E = YAHOO.util.Event;
6769 function yearUpdateHandler() {
6770 if (this.validate()) {
6771 this.setYear(this._getYearFromUI());
6775 function monthUpdateHandler() {
6776 this.setMonth(this._getMonthFromUI());
6779 E.on(this.submitEl, "click", this.submit, this, true);
6780 E.on(this.cancelEl, "click", this.cancel, this, true);
6781 E.on(this.yearEl, "blur", yearUpdateHandler, this, true);
6782 E.on(this.monthEl, "change", monthUpdateHandler, this, true);
6784 if (this.__isIEQuirks) {
6785 YAHOO.util.Event.on(this.cal.oDomContainer, "resize", this._syncMask, this, true);
6788 this.applyKeyListeners();
6792 * Removes/purges DOM event listeners from the rendered elements
6794 * @method purgeListeners
6796 purgeListeners : function() {
6797 var E = YAHOO.util.Event;
6798 E.removeListener(this.submitEl, "click", this.submit);
6799 E.removeListener(this.cancelEl, "click", this.cancel);
6800 E.removeListener(this.yearEl, "blur");
6801 E.removeListener(this.monthEl, "change");
6802 if (this.__isIEQuirks) {
6803 E.removeListener(this.cal.oDomContainer, "resize", this._syncMask);
6806 this.purgeKeyListeners();
6810 * Attaches DOM listeners for keyboard support.
6811 * Tab/Shift-Tab looping, Enter Key Submit on Year element,
6812 * Up/Down/PgUp/PgDown year increment on Year element
6814 * NOTE: MacOSX Safari 2.x doesn't let you tab to buttons and
6815 * MacOSX Gecko does not let you tab to buttons or select controls,
6816 * so for these browsers, Tab/Shift-Tab looping is limited to the
6817 * elements which can be reached using the tab key.
6819 * @method applyKeyListeners
6821 applyKeyListeners : function() {
6822 var E = YAHOO.util.Event,
6825 // IE/Safari 3.1 doesn't fire keypress for arrow/pg keys (non-char keys)
6826 var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
6828 // - IE/Safari 3.1 doesn't fire keypress for non-char keys
6829 // - Opera doesn't allow us to cancel keydown or keypress for tab, but
6830 // changes focus successfully on keydown (keypress is too late to change focus - opera's already moved on).
6831 var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
6833 // Everyone likes keypress for Enter (char keys) - whoo hoo!
6834 E.on(this.yearEl, "keypress", this._handleEnterKey, this, true);
6836 E.on(this.yearEl, arrowEvt, this._handleDirectionKeys, this, true);
6837 E.on(this.lastCtrl, tabEvt, this._handleTabKey, this, true);
6838 E.on(this.firstCtrl, tabEvt, this._handleShiftTabKey, this, true);
6842 * Removes/purges DOM listeners for keyboard support
6844 * @method purgeKeyListeners
6846 purgeKeyListeners : function() {
6847 var E = YAHOO.util.Event,
6850 var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
6851 var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
6853 E.removeListener(this.yearEl, "keypress", this._handleEnterKey);
6854 E.removeListener(this.yearEl, arrowEvt, this._handleDirectionKeys);
6855 E.removeListener(this.lastCtrl, tabEvt, this._handleTabKey);
6856 E.removeListener(this.firstCtrl, tabEvt, this._handleShiftTabKey);
6860 * Updates the Calendar/CalendarGroup's pagedate with the currently set month and year if valid.
6862 * If the currently set month/year is invalid, a validation error will be displayed and the
6863 * Calendar/CalendarGroup's pagedate will not be updated.
6867 submit : function() {
6868 if (this.validate()) {
6871 this.setMonth(this._getMonthFromUI());
6872 this.setYear(this._getYearFromUI());
6876 // Artificial delay, just to help the user see something changed
6877 var delay = YAHOO.widget.CalendarNavigator.UPDATE_DELAY;
6880 window.setTimeout(function(){ nav._update(cal); }, delay);
6888 * Updates the Calendar rendered state, based on the state of the CalendarNavigator
6890 * @param cal The Calendar instance to update
6893 _update : function(cal) {
6894 var date = YAHOO.widget.DateMath.getDate(this.getYear() - cal.cfg.getProperty("YEAR_OFFSET"), this.getMonth(), 1);
6895 cal.cfg.setProperty("pagedate", date);
6900 * Hides the navigator and mask, without updating the Calendar/CalendarGroup's state
6904 cancel : function() {
6909 * Validates the current state of the UI controls
6912 * @return {Boolean} true, if the current UI state contains valid values, false if not
6914 validate : function() {
6915 if (this._getYearFromUI() !== null) {
6919 this.setYearError();
6920 this.setError(this.__getCfg("invalidYear", true));
6926 * Displays an error message in the Navigator's error panel
6928 * @param {String} msg The error message to display
6930 setError : function(msg) {
6932 this.errorEl.innerHTML = msg;
6933 this._show(this.errorEl, true);
6938 * Clears the navigator's error message and hides the error panel
6939 * @method clearError
6941 clearError : function() {
6943 this.errorEl.innerHTML = "";
6944 this._show(this.errorEl, false);
6949 * Displays the validation error UI for the year control
6950 * @method setYearError
6952 setYearError : function() {
6953 YAHOO.util.Dom.addClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
6957 * Removes the validation error UI for the year control
6958 * @method clearYearError
6960 clearYearError : function() {
6961 YAHOO.util.Dom.removeClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
6965 * Clears all validation and error messages in the UI
6966 * @method clearErrors
6968 clearErrors : function() {
6970 this.clearYearError();
6974 * Sets the initial focus, based on the configured value
6975 * @method setInitialFocus
6977 setInitialFocus : function() {
6978 var el = this.submitEl,
6979 f = this.__getCfg("initialFocus");
6981 if (f && f.toLowerCase) {
6982 f = f.toLowerCase();
6986 this.yearEl.select();
6990 } else if (f == "month") {
6995 if (el && YAHOO.lang.isFunction(el.focus)) {
6998 } catch (focusErr) {
6999 // TODO: Fall back if focus fails?
7005 * Removes all renderered HTML elements for the Navigator from
7006 * the DOM, purges event listeners and clears (nulls) any property
7007 * references to HTML references
7010 erase : function() {
7011 if (this.__rendered) {
7012 this.purgeListeners();
7014 // Clear out innerHTML references
7016 this.monthEl = null;
7017 this.errorEl = null;
7018 this.submitEl = null;
7019 this.cancelEl = null;
7020 this.firstCtrl = null;
7021 this.lastCtrl = null;
7023 this.navEl.innerHTML = "";
7026 var p = this.navEl.parentNode;
7028 p.removeChild(this.navEl);
7032 var pm = this.maskEl.parentNode;
7034 pm.removeChild(this.maskEl);
7037 this.__rendered = false;
7042 * Destroys the Navigator object and any HTML references
7045 destroy : function() {
7053 * Protected implementation to handle how UI elements are
7059 _show : function(el, bShow) {
7061 YAHOO.util.Dom.setStyle(el, "display", (bShow) ? "block" : "none");
7066 * Returns the month value (index), from the month UI element
7068 * @method _getMonthFromUI
7069 * @return {Number} The month index, or 0 if a UI element for the month
7072 _getMonthFromUI : function() {
7074 return this.monthEl.selectedIndex;
7076 return 0; // Default to Jan
7081 * Returns the year value, from the Navitator's year UI element
7083 * @method _getYearFromUI
7084 * @return {Number} The year value set in the UI, if valid. null is returned if
7085 * the UI does not contain a valid year value.
7087 _getYearFromUI : function() {
7088 var NAV = YAHOO.widget.CalendarNavigator;
7092 var value = this.yearEl.value;
7093 value = value.replace(NAV.TRIM, "$1");
7095 if (NAV.YR_PATTERN.test(value)) {
7096 yr = parseInt(value, 10);
7103 * Updates the Navigator's year UI, based on the year value set on the Navigator object
7105 * @method _updateYearUI
7107 _updateYearUI : function() {
7108 if (this.yearEl && this._year !== null) {
7109 this.yearEl.value = this._year;
7114 * Updates the Navigator's month UI, based on the month value set on the Navigator object
7116 * @method _updateMonthUI
7118 _updateMonthUI : function() {
7120 this.monthEl.selectedIndex = this._month;
7125 * Sets up references to the first and last focusable element in the Navigator's UI
7126 * in terms of tab order (Naviagator's firstEl and lastEl properties). The references
7127 * are used to control modality by looping around from the first to the last control
7128 * and visa versa for tab/shift-tab navigation.
7130 * See <a href="#applyKeyListeners">applyKeyListeners</a>
7133 * @method _setFirstLastElements
7135 _setFirstLastElements : function() {
7136 this.firstCtrl = this.monthEl;
7137 this.lastCtrl = this.cancelEl;
7139 // Special handling for MacOSX.
7140 // - Safari 2.x can't focus on buttons
7141 // - Gecko can't focus on select boxes or buttons
7143 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420){
7144 this.firstCtrl = this.monthEl;
7145 this.lastCtrl = this.yearEl;
7147 if (YAHOO.env.ua.gecko) {
7148 this.firstCtrl = this.yearEl;
7149 this.lastCtrl = this.yearEl;
7155 * Default Keyboard event handler to capture Enter
7156 * on the Navigator's year control (yearEl)
7158 * @method _handleEnterKey
7160 * @param {Event} e The DOM event being handled
7162 _handleEnterKey : function(e) {
7163 var KEYS = YAHOO.util.KeyListener.KEY;
7165 if (YAHOO.util.Event.getCharCode(e) == KEYS.ENTER) {
7166 YAHOO.util.Event.preventDefault(e);
7172 * Default Keyboard event handler to capture up/down/pgup/pgdown
7173 * on the Navigator's year control (yearEl).
7175 * @method _handleDirectionKeys
7177 * @param {Event} e The DOM event being handled
7179 _handleDirectionKeys : function(e) {
7180 var E = YAHOO.util.Event,
7181 KEYS = YAHOO.util.KeyListener.KEY,
7182 NAV = YAHOO.widget.CalendarNavigator;
7184 var value = (this.yearEl.value) ? parseInt(this.yearEl.value, 10) : null;
7185 if (isFinite(value)) {
7187 switch(E.getCharCode(e)) {
7189 this.yearEl.value = value + NAV.YR_MINOR_INC;
7193 this.yearEl.value = Math.max(value - NAV.YR_MINOR_INC, 0);
7197 this.yearEl.value = value + NAV.YR_MAJOR_INC;
7200 case KEYS.PAGE_DOWN:
7201 this.yearEl.value = Math.max(value - NAV.YR_MAJOR_INC, 0);
7208 E.preventDefault(e);
7210 this.yearEl.select();
7219 * Default Keyboard event handler to capture Tab
7220 * on the last control (lastCtrl) in the Navigator.
7222 * @method _handleTabKey
7224 * @param {Event} e The DOM event being handled
7226 _handleTabKey : function(e) {
7227 var E = YAHOO.util.Event,
7228 KEYS = YAHOO.util.KeyListener.KEY;
7230 if (E.getCharCode(e) == KEYS.TAB && !e.shiftKey) {
7232 E.preventDefault(e);
7233 this.firstCtrl.focus();
7235 // Ignore - mainly for focus edge cases
7241 * Default Keyboard event handler to capture Shift-Tab
7242 * on the first control (firstCtrl) in the Navigator.
7244 * @method _handleShiftTabKey
7246 * @param {Event} e The DOM event being handled
7248 _handleShiftTabKey : function(e) {
7249 var E = YAHOO.util.Event,
7250 KEYS = YAHOO.util.KeyListener.KEY;
7252 if (e.shiftKey && E.getCharCode(e) == KEYS.TAB) {
7254 E.preventDefault(e);
7255 this.lastCtrl.focus();
7257 // Ignore - mainly for focus edge cases
7263 * Retrieve Navigator configuration values from
7264 * the parent Calendar/CalendarGroup's config value.
7266 * If it has not been set in the user provided configuration, the method will
7267 * return the default value of the configuration property, as set in DEFAULT_CONFIG
7271 * @param {String} Case sensitive property name.
7272 * @param {Boolean} true, if the property is a string property, false if not.
7273 * @return The value of the configuration property
7275 __getCfg : function(prop, bIsStr) {
7276 var DEF_CFG = YAHOO.widget.CalendarNavigator.DEFAULT_CONFIG;
7277 var cfg = this.cal.cfg.getProperty("navigator");
7280 return (cfg !== true && cfg.strings && cfg.strings[prop]) ? cfg.strings[prop] : DEF_CFG.strings[prop];
7282 return (cfg !== true && cfg[prop]) ? cfg[prop] : DEF_CFG[prop];
7287 * Private flag, to identify MacOS
7291 __isMac : (navigator.userAgent.toLowerCase().indexOf("macintosh") != -1)
7294 YAHOO.register("calendar", YAHOO.widget.Calendar, {version: "2.8.0r4", build: "2449"});