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} override Optional. If true, will override "this"
541 * within the handler to map to the scope Object passed into the method.
542 * @return {Boolean} True, if the subscription was successful,
545 subscribeToConfigEvent: function (key, handler, obj, override) {
547 var property = this.config[key.toLowerCase()];
549 if (property && property.event) {
550 if (!Config.alreadySubscribed(property.event, handler, obj)) {
551 property.event.subscribe(handler, obj, override);
561 * Unsubscribes an external handler from the change event for any
563 * @method unsubscribeFromConfigEvent
564 * @param {String} key The property name
565 * @param {Function} handler The handler function to use subscribe to
566 * the property's event
567 * @param {Object} obj The Object to use for scoping the event
568 * handler (see CustomEvent documentation)
569 * @return {Boolean} True, if the unsubscription was successful,
572 unsubscribeFromConfigEvent: function (key, handler, obj) {
573 var property = this.config[key.toLowerCase()];
574 if (property && property.event) {
575 return property.event.unsubscribe(handler, obj);
582 * Returns a string representation of the Config object
584 * @return {String} The Config object in string format.
586 toString: function () {
587 var output = "Config";
589 output += " [" + this.owner.toString() + "]";
595 * Returns a string representation of the Config object's current
597 * @method outputEventQueue
598 * @return {String} The string list of CustomEvents currently queued
601 outputEventQueue: function () {
606 nQueue = this.eventQueue.length;
608 for (q = 0; q < nQueue; q++) {
609 queueItem = this.eventQueue[q];
611 output += queueItem[0] + "=" + queueItem[1] + ", ";
618 * Sets all properties to null, unsubscribes all listeners from each
619 * property's change event and all listeners from the configChangedEvent.
622 destroy: function () {
624 var oConfig = this.config,
629 for (sProperty in oConfig) {
631 if (Lang.hasOwnProperty(oConfig, sProperty)) {
633 oProperty = oConfig[sProperty];
635 oProperty.event.unsubscribeAll();
636 oProperty.event = null;
642 this.configChangedEvent.unsubscribeAll();
644 this.configChangedEvent = null;
647 this.initialConfig = null;
648 this.eventQueue = null;
657 * Checks to determine if a particular function/Object pair are already
658 * subscribed to the specified CustomEvent
659 * @method YAHOO.util.Config.alreadySubscribed
661 * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check
663 * @param {Function} fn The function to look for in the subscribers list
664 * @param {Object} obj The execution scope Object for the subscription
665 * @return {Boolean} true, if the function/Object pair is already subscribed
666 * to the CustomEvent passed in
668 Config.alreadySubscribed = function (evt, fn, obj) {
670 var nSubscribers = evt.subscribers.length,
674 if (nSubscribers > 0) {
675 i = nSubscribers - 1;
677 subsc = evt.subscribers[i];
678 if (subsc && subsc.obj == obj && subsc.fn == fn) {
689 YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider);
693 * YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility
694 * used for adding, subtracting, and comparing dates.
695 * @namespace YAHOO.widget
698 YAHOO.widget.DateMath = {
700 * Constant field representing Day
709 * Constant field representing Week
718 * Constant field representing Year
727 * Constant field representing Month
736 * Constant field representing one day, in milliseconds
737 * @property ONE_DAY_MS
742 ONE_DAY_MS : 1000*60*60*24,
745 * Constant field representing the date in first week of January
746 * which identifies the first week of the year.
748 * In the U.S, Jan 1st is normally used based on a Sunday start of week.
749 * ISO 8601, used widely throughout Europe, uses Jan 4th, based on a Monday start of week.
751 * @property WEEK_ONE_JAN_DATE
755 WEEK_ONE_JAN_DATE : 1,
758 * Adds the specified amount of time to the this instance.
760 * @param {Date} date The JavaScript Date object to perform addition on
761 * @param {String} field The field constant to be used for performing addition.
762 * @param {Number} amount The number of units (measured in the field constant) to add to the date.
763 * @return {Date} The resulting Date object
765 add : function(date, field, amount) {
766 var d = new Date(date.getTime());
769 var newMonth = date.getMonth() + amount;
773 while (newMonth < 0) {
777 } else if (newMonth > 11) {
778 while (newMonth > 11) {
784 d.setMonth(newMonth);
785 d.setFullYear(date.getFullYear() + years);
788 this._addDays(d, amount);
789 // d.setDate(date.getDate() + amount);
792 d.setFullYear(date.getFullYear() + amount);
795 this._addDays(d, (amount * 7));
796 // d.setDate(date.getDate() + (amount * 7));
803 * Private helper method to account for bug in Safari 2 (webkit < 420)
804 * when Date.setDate(n) is called with n less than -128 or greater than 127.
806 * Fix approach and original findings are available here:
807 * http://brianary.blogspot.com/2006/03/safari-date-bug.html
810 * @param {Date} d JavaScript date object
811 * @param {Number} nDays The number of days to add to the date object (can be negative)
814 _addDays : function(d, nDays) {
815 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420) {
817 // Ensure we don't go below -128 (getDate() is always 1 to 31, so we won't go above 127)
818 for(var min = -128; nDays < min; nDays -= min) {
819 d.setDate(d.getDate() + min);
822 // Ensure we don't go above 96 + 31 = 127
823 for(var max = 96; nDays > max; nDays -= max) {
824 d.setDate(d.getDate() + max);
827 // nDays should be remainder between -128 and 96
829 d.setDate(d.getDate() + nDays);
833 * Subtracts the specified amount of time from the this instance.
835 * @param {Date} date The JavaScript Date object to perform subtraction on
836 * @param {Number} field The this field constant to be used for performing subtraction.
837 * @param {Number} amount The number of units (measured in the field constant) to subtract from the date.
838 * @return {Date} The resulting Date object
840 subtract : function(date, field, amount) {
841 return this.add(date, field, (amount*-1));
845 * Determines whether a given date is before another date on the calendar.
847 * @param {Date} date The Date object to compare with the compare argument
848 * @param {Date} compareTo The Date object to use for the comparison
849 * @return {Boolean} true if the date occurs before the compared date; false if not.
851 before : function(date, compareTo) {
852 var ms = compareTo.getTime();
853 if (date.getTime() < ms) {
861 * Determines whether a given date is after another date on the calendar.
863 * @param {Date} date The Date object to compare with the compare argument
864 * @param {Date} compareTo The Date object to use for the comparison
865 * @return {Boolean} true if the date occurs after the compared date; false if not.
867 after : function(date, compareTo) {
868 var ms = compareTo.getTime();
869 if (date.getTime() > ms) {
877 * Determines whether a given date is between two other dates on the calendar.
879 * @param {Date} date The date to check for
880 * @param {Date} dateBegin The start of the range
881 * @param {Date} dateEnd The end of the range
882 * @return {Boolean} true if the date occurs between the compared dates; false if not.
884 between : function(date, dateBegin, dateEnd) {
885 if (this.after(date, dateBegin) && this.before(date, dateEnd)) {
893 * Retrieves a JavaScript Date object representing January 1 of any given year.
895 * @param {Number} calendarYear The calendar year for which to retrieve January 1
896 * @return {Date} January 1 of the calendar year specified.
898 getJan1 : function(calendarYear) {
899 return this.getDate(calendarYear,0,1);
903 * Calculates the number of days the specified date is from January 1 of the specified calendar year.
904 * Passing January 1 to this function would return an offset value of zero.
905 * @method getDayOffset
906 * @param {Date} date The JavaScript date for which to find the offset
907 * @param {Number} calendarYear The calendar year to use for determining the offset
908 * @return {Number} The number of days since January 1 of the given year
910 getDayOffset : function(date, calendarYear) {
911 var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1.
913 // Find the number of days the passed in date is away from the calendar year start
914 var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS);
919 * Calculates the week number for the given date. Can currently support standard
920 * U.S. week numbers, based on Jan 1st defining the 1st week of the year, and
921 * ISO8601 week numbers, based on Jan 4th defining the 1st week of the year.
923 * @method getWeekNumber
924 * @param {Date} date The JavaScript date for which to find the week number
925 * @param {Number} firstDayOfWeek The index of the first day of the week (0 = Sun, 1 = Mon ... 6 = Sat).
927 * @param {Number} janDate The date in the first week of January which defines week one for the year
928 * Defaults to the value of YAHOO.widget.DateMath.WEEK_ONE_JAN_DATE, which is 1 (Jan 1st).
929 * For the U.S, this is normally Jan 1st. ISO8601 uses Jan 4th to define the first week of the year.
931 * @return {Number} The number of the week containing the given date.
933 getWeekNumber : function(date, firstDayOfWeek, janDate) {
936 firstDayOfWeek = firstDayOfWeek || 0;
937 janDate = janDate || this.WEEK_ONE_JAN_DATE;
939 var targetDate = this.clearTime(date),
943 if (targetDate.getDay() === firstDayOfWeek) {
944 startOfWeek = targetDate;
946 startOfWeek = this.getFirstDayOfWeek(targetDate, firstDayOfWeek);
949 var startYear = startOfWeek.getFullYear(),
950 startTime = startOfWeek.getTime();
952 // DST shouldn't be a problem here, math is quicker than setDate();
953 endOfWeek = new Date(startOfWeek.getTime() + 6*this.ONE_DAY_MS);
956 if (startYear !== endOfWeek.getFullYear() && endOfWeek.getDate() >= janDate) {
957 // If years don't match, endOfWeek is in Jan. and if the
958 // week has WEEK_ONE_JAN_DATE in it, it's week one by definition.
961 // Get the 1st day of the 1st week, and
962 // find how many days away we are from it.
963 var weekOne = this.clearTime(this.getDate(startYear, 0, janDate)),
964 weekOneDayOne = this.getFirstDayOfWeek(weekOne, firstDayOfWeek);
966 // Round days to smoothen out 1 hr DST diff
967 var daysDiff = Math.round((targetDate.getTime() - weekOneDayOne.getTime())/this.ONE_DAY_MS);
970 var rem = daysDiff % 7;
971 var weeksDiff = (daysDiff - rem)/7;
972 weekNum = weeksDiff + 1;
978 * Get the first day of the week, for the give date.
979 * @param {Date} dt The date in the week for which the first day is required.
980 * @param {Number} startOfWeek The index for the first day of the week, 0 = Sun, 1 = Mon ... 6 = Sat (defaults to 0)
981 * @return {Date} The first day of the week
983 getFirstDayOfWeek : function (dt, startOfWeek) {
984 startOfWeek = startOfWeek || 0;
985 var dayOfWeekIndex = dt.getDay(),
986 dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7;
988 return this.subtract(dt, this.DAY, dayOfWeek);
992 * Determines if a given week overlaps two different years.
993 * @method isYearOverlapWeek
994 * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week.
995 * @return {Boolean} true if the date overlaps two different years.
997 isYearOverlapWeek : function(weekBeginDate) {
998 var overlaps = false;
999 var nextWeek = this.add(weekBeginDate, this.DAY, 6);
1000 if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) {
1007 * Determines if a given week overlaps two different months.
1008 * @method isMonthOverlapWeek
1009 * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week.
1010 * @return {Boolean} true if the date overlaps two different months.
1012 isMonthOverlapWeek : function(weekBeginDate) {
1013 var overlaps = false;
1014 var nextWeek = this.add(weekBeginDate, this.DAY, 6);
1015 if (nextWeek.getMonth() != weekBeginDate.getMonth()) {
1022 * Gets the first day of a month containing a given date.
1023 * @method findMonthStart
1024 * @param {Date} date The JavaScript Date used to calculate the month start
1025 * @return {Date} The JavaScript Date representing the first day of the month
1027 findMonthStart : function(date) {
1028 var start = this.getDate(date.getFullYear(), date.getMonth(), 1);
1033 * Gets the last day of a month containing a given date.
1034 * @method findMonthEnd
1035 * @param {Date} date The JavaScript Date used to calculate the month end
1036 * @return {Date} The JavaScript Date representing the last day of the month
1038 findMonthEnd : function(date) {
1039 var start = this.findMonthStart(date);
1040 var nextMonth = this.add(start, this.MONTH, 1);
1041 var end = this.subtract(nextMonth, this.DAY, 1);
1046 * Clears the time fields from a given date, effectively setting the time to 12 noon.
1048 * @param {Date} date The JavaScript Date for which the time fields will be cleared
1049 * @return {Date} The JavaScript Date cleared of all time fields
1051 clearTime : function(date) {
1052 date.setHours(12,0,0,0);
1057 * Returns a new JavaScript Date object, representing the given year, month and date. Time fields (hr, min, sec, ms) on the new Date object
1058 * 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
1059 * set the year to 19xx if a year (xx) which is less than 100 is provided.
1061 * <em>NOTE:</em>Validation on argument values is not performed. It is the caller's responsibility to ensure
1062 * arguments are valid as per the ECMAScript-262 Date object specification for the new Date(year, month[, date]) constructor.
1065 * @param {Number} y Year.
1066 * @param {Number} m Month index from 0 (Jan) to 11 (Dec).
1067 * @param {Number} d (optional) Date from 1 to 31. If not provided, defaults to 1.
1068 * @return {Date} The JavaScript date object with year, month, date set as provided.
1070 getDate : function(y, m, d) {
1072 if (YAHOO.lang.isUndefined(d)) {
1076 dt = new Date(y, m, d);
1082 dt.setHours(0,0,0,0);
1089 * 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
1090 * multi-month interface. Calendars are generated entirely via script and can be navigated without any page refreshes.
1093 * @namespace YAHOO.widget
1094 * @requires yahoo,dom,event
1098 var Dom = YAHOO.util.Dom,
1099 Event = YAHOO.util.Event,
1101 DateMath = YAHOO.widget.DateMath;
1104 * Calendar is the base class for the Calendar widget. In its most basic
1105 * implementation, it has the ability to render a calendar widget on the page
1106 * that can be manipulated to select a single date, move back and forth between
1108 * <p>To construct the placeholder for the calendar widget, the code is as
1111 * <div id="calContainer"></div>
1115 * <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
1116 * The Calendar can be constructed by simply providing a container ID string,
1117 * or a reference to a container DIV HTMLElement (the element needs to exist
1122 * var c = new YAHOO.widget.Calendar("calContainer", configOptions);
1126 * var containerDiv = YAHOO.util.Dom.get("calContainer");
1127 * var c = new YAHOO.widget.Calendar(containerDiv, configOptions);
1131 * If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
1132 * 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".
1135 * @namespace YAHOO.widget
1138 * @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.
1139 * @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.
1140 * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
1142 function Calendar(id, containerId, config) {
1143 this.init.apply(this, arguments);
1147 * The path to be used for images loaded for the Calendar
1148 * @property YAHOO.widget.Calendar.IMG_ROOT
1150 * @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
1153 Calendar.IMG_ROOT = null;
1156 * Type constant used for renderers to represent an individual date (M/D/Y)
1157 * @property YAHOO.widget.Calendar.DATE
1162 Calendar.DATE = "D";
1165 * Type constant used for renderers to represent an individual date across any year (M/D)
1166 * @property YAHOO.widget.Calendar.MONTH_DAY
1171 Calendar.MONTH_DAY = "MD";
1174 * Type constant used for renderers to represent a weekday
1175 * @property YAHOO.widget.Calendar.WEEKDAY
1180 Calendar.WEEKDAY = "WD";
1183 * Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
1184 * @property YAHOO.widget.Calendar.RANGE
1189 Calendar.RANGE = "R";
1192 * Type constant used for renderers to represent a month across any year
1193 * @property YAHOO.widget.Calendar.MONTH
1198 Calendar.MONTH = "M";
1201 * Constant that represents the total number of date cells that are displayed in a given month
1202 * @property YAHOO.widget.Calendar.DISPLAY_DAYS
1207 Calendar.DISPLAY_DAYS = 42;
1210 * Constant used for halting the execution of the remainder of the render stack
1211 * @property YAHOO.widget.Calendar.STOP_RENDER
1216 Calendar.STOP_RENDER = "S";
1219 * Constant used to represent short date field string formats (e.g. Tu or Feb)
1220 * @property YAHOO.widget.Calendar.SHORT
1225 Calendar.SHORT = "short";
1228 * Constant used to represent long date field string formats (e.g. Monday or February)
1229 * @property YAHOO.widget.Calendar.LONG
1234 Calendar.LONG = "long";
1237 * Constant used to represent medium date field string formats (e.g. Mon)
1238 * @property YAHOO.widget.Calendar.MEDIUM
1243 Calendar.MEDIUM = "medium";
1246 * Constant used to represent single character date field string formats (e.g. M, T, W)
1247 * @property YAHOO.widget.Calendar.ONE_CHAR
1252 Calendar.ONE_CHAR = "1char";
1255 * The set of default Config property keys and values for the Calendar
1256 * @property YAHOO.widget.Calendar._DEFAULT_CONFIG
1262 Calendar._DEFAULT_CONFIG = {
1263 // Default values for pagedate and selected are not class level constants - they are set during instance creation
1264 PAGEDATE : {key:"pagedate", value:null},
1265 SELECTED : {key:"selected", value:null},
1266 TITLE : {key:"title", value:""},
1267 CLOSE : {key:"close", value:false},
1268 IFRAME : {key:"iframe", value:(YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) ? true : false},
1269 MINDATE : {key:"mindate", value:null},
1270 MAXDATE : {key:"maxdate", value:null},
1271 MULTI_SELECT : {key:"multi_select", value:false},
1272 START_WEEKDAY : {key:"start_weekday", value:0},
1273 SHOW_WEEKDAYS : {key:"show_weekdays", value:true},
1274 SHOW_WEEK_HEADER : {key:"show_week_header", value:false},
1275 SHOW_WEEK_FOOTER : {key:"show_week_footer", value:false},
1276 HIDE_BLANK_WEEKS : {key:"hide_blank_weeks", value:false},
1277 NAV_ARROW_LEFT: {key:"nav_arrow_left", value:null} ,
1278 NAV_ARROW_RIGHT : {key:"nav_arrow_right", value:null} ,
1279 MONTHS_SHORT : {key:"months_short", value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]},
1280 MONTHS_LONG: {key:"months_long", value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]},
1281 WEEKDAYS_1CHAR: {key:"weekdays_1char", value:["S", "M", "T", "W", "T", "F", "S"]},
1282 WEEKDAYS_SHORT: {key:"weekdays_short", value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]},
1283 WEEKDAYS_MEDIUM: {key:"weekdays_medium", value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]},
1284 WEEKDAYS_LONG: {key:"weekdays_long", value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]},
1285 LOCALE_MONTHS:{key:"locale_months", value:"long"},
1286 LOCALE_WEEKDAYS:{key:"locale_weekdays", value:"short"},
1287 DATE_DELIMITER:{key:"date_delimiter", value:","},
1288 DATE_FIELD_DELIMITER:{key:"date_field_delimiter", value:"/"},
1289 DATE_RANGE_DELIMITER:{key:"date_range_delimiter", value:"-"},
1290 MY_MONTH_POSITION:{key:"my_month_position", value:1},
1291 MY_YEAR_POSITION:{key:"my_year_position", value:2},
1292 MD_MONTH_POSITION:{key:"md_month_position", value:1},
1293 MD_DAY_POSITION:{key:"md_day_position", value:2},
1294 MDY_MONTH_POSITION:{key:"mdy_month_position", value:1},
1295 MDY_DAY_POSITION:{key:"mdy_day_position", value:2},
1296 MDY_YEAR_POSITION:{key:"mdy_year_position", value:3},
1297 MY_LABEL_MONTH_POSITION:{key:"my_label_month_position", value:1},
1298 MY_LABEL_YEAR_POSITION:{key:"my_label_year_position", value:2},
1299 MY_LABEL_MONTH_SUFFIX:{key:"my_label_month_suffix", value:" "},
1300 MY_LABEL_YEAR_SUFFIX:{key:"my_label_year_suffix", value:""},
1301 NAV: {key:"navigator", value: null},
1305 previousMonth : "Previous Month",
1306 nextMonth : "Next Month",
1309 supercedes : ["close", "title"]
1313 var DEF_CFG = Calendar._DEFAULT_CONFIG;
1316 * The set of Custom Event types supported by the Calendar
1317 * @property YAHOO.widget.Calendar._EVENT_TYPES
1323 Calendar._EVENT_TYPES = {
1324 BEFORE_SELECT : "beforeSelect",
1326 BEFORE_DESELECT : "beforeDeselect",
1327 DESELECT : "deselect",
1328 CHANGE_PAGE : "changePage",
1329 BEFORE_RENDER : "beforeRender",
1331 BEFORE_DESTROY : "beforeDestroy",
1332 DESTROY : "destroy",
1335 BEFORE_HIDE : "beforeHide",
1337 BEFORE_SHOW : "beforeShow",
1339 BEFORE_HIDE_NAV : "beforeHideNav",
1340 HIDE_NAV : "hideNav",
1341 BEFORE_SHOW_NAV : "beforeShowNav",
1342 SHOW_NAV : "showNav",
1343 BEFORE_RENDER_NAV : "beforeRenderNav",
1344 RENDER_NAV : "renderNav"
1348 * The set of default style constants for the Calendar
1349 * @property YAHOO.widget.Calendar._STYLES
1355 Calendar._STYLES = {
1356 CSS_ROW_HEADER: "calrowhead",
1357 CSS_ROW_FOOTER: "calrowfoot",
1358 CSS_CELL : "calcell",
1359 CSS_CELL_SELECTOR : "selector",
1360 CSS_CELL_SELECTED : "selected",
1361 CSS_CELL_SELECTABLE : "selectable",
1362 CSS_CELL_RESTRICTED : "restricted",
1363 CSS_CELL_TODAY : "today",
1364 CSS_CELL_OOM : "oom",
1365 CSS_CELL_OOB : "previous",
1366 CSS_HEADER : "calheader",
1367 CSS_HEADER_TEXT : "calhead",
1368 CSS_BODY : "calbody",
1369 CSS_WEEKDAY_CELL : "calweekdaycell",
1370 CSS_WEEKDAY_ROW : "calweekdayrow",
1371 CSS_FOOTER : "calfoot",
1372 CSS_CALENDAR : "yui-calendar",
1373 CSS_SINGLE : "single",
1374 CSS_CONTAINER : "yui-calcontainer",
1375 CSS_NAV_LEFT : "calnavleft",
1376 CSS_NAV_RIGHT : "calnavright",
1378 CSS_CLOSE : "calclose",
1379 CSS_CELL_TOP : "calcelltop",
1380 CSS_CELL_LEFT : "calcellleft",
1381 CSS_CELL_RIGHT : "calcellright",
1382 CSS_CELL_BOTTOM : "calcellbottom",
1383 CSS_CELL_HOVER : "calcellhover",
1384 CSS_CELL_HIGHLIGHT1 : "highlight1",
1385 CSS_CELL_HIGHLIGHT2 : "highlight2",
1386 CSS_CELL_HIGHLIGHT3 : "highlight3",
1387 CSS_CELL_HIGHLIGHT4 : "highlight4"
1390 Calendar.prototype = {
1393 * The configuration object used to set up the calendars various locale and style options.
1396 * @deprecated Configuration properties should be set by calling Calendar.cfg.setProperty.
1402 * The parent CalendarGroup, only to be set explicitly by the parent group
1404 * @type CalendarGroup
1409 * The index of this item in the parent group
1416 * The collection of calendar table cells
1418 * @type HTMLTableCellElement[]
1423 * 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].
1424 * @property cellDates
1425 * @type Array[](Number[])
1430 * The id that uniquely identifies this Calendar.
1437 * The unique id associated with the Calendar's container
1438 * @property containerId
1444 * 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.
1445 * @property oDomContainer
1448 oDomContainer : null,
1451 * A Date object representing today's date.
1458 * The list of render functions, along with required parameters, used to render cells.
1459 * @property renderStack
1465 * A copy of the initial render functions created before rendering.
1466 * @property _renderStack
1470 _renderStack : null,
1473 * A reference to the CalendarNavigator instance created for this Calendar.
1474 * Will be null if the "navigator" configuration property has not been set
1475 * @property oNavigator
1476 * @type CalendarNavigator
1481 * The private list of initially selected dates.
1482 * @property _selectedDates
1486 _selectedDates : null,
1489 * A map of DOM event handlers to attach to cells associated with specific CSS class names
1490 * @property domEventMap
1496 * Protected helper used to parse Calendar constructor/init arguments.
1498 * As of 2.4.0, Calendar supports a simpler constructor
1499 * signature. This method reconciles arguments
1500 * received in the pre 2.4.0 and 2.4.0 formats.
1503 * @method _parseArgs
1504 * @param {Array} Function "arguments" array
1505 * @return {Object} Object with id, container, config properties containing
1506 * the reconciled argument values.
1508 _parseArgs : function(args) {
1510 2.4.0 Constructors signatures
1512 new Calendar(String)
1513 new Calendar(HTMLElement)
1514 new Calendar(String, ConfigObject)
1515 new Calendar(HTMLElement, ConfigObject)
1517 Pre 2.4.0 Constructor signatures
1519 new Calendar(String, String)
1520 new Calendar(String, HTMLElement)
1521 new Calendar(String, String, ConfigObject)
1522 new Calendar(String, HTMLElement, ConfigObject)
1524 var nArgs = {id:null, container:null, config:null};
1526 if (args && args.length && args.length > 0) {
1527 switch (args.length) {
1530 nArgs.container = args[0];
1531 nArgs.config = null;
1534 if (Lang.isObject(args[1]) && !args[1].tagName && !(args[1] instanceof String)) {
1536 nArgs.container = args[0];
1537 nArgs.config = args[1];
1540 nArgs.container = args[1];
1541 nArgs.config = null;
1546 nArgs.container = args[1];
1547 nArgs.config = args[2];
1556 * Initializes the Calendar widget.
1559 * @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.
1560 * @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.
1561 * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
1563 init : function(id, container, config) {
1564 // Normalize 2.4.0, pre 2.4.0 args
1565 var nArgs = this._parseArgs(arguments);
1568 container = nArgs.container;
1569 config = nArgs.config;
1571 this.oDomContainer = Dom.get(container);
1573 if (!this.oDomContainer.id) {
1574 this.oDomContainer.id = Dom.generateId();
1577 id = this.oDomContainer.id + "_t";
1581 this.containerId = this.oDomContainer.id;
1585 this.today = new Date();
1586 DateMath.clearTime(this.today);
1589 * The Config object used to hold the configuration variables for the Calendar
1591 * @type YAHOO.util.Config
1593 this.cfg = new YAHOO.util.Config(this);
1596 * The local object which contains the Calendar's options
1603 * The local object which contains the Calendar's locale settings
1611 Dom.addClass(this.oDomContainer, this.Style.CSS_CONTAINER);
1612 Dom.addClass(this.oDomContainer, this.Style.CSS_SINGLE);
1614 this.cellDates = [];
1616 this.renderStack = [];
1617 this._renderStack = [];
1622 this.cfg.applyConfig(config, true);
1625 this.cfg.fireQueue();
1629 * Default Config listener for the iframe property. If the iframe config property is set to true,
1630 * renders the built-in IFRAME shim if the container is relatively or absolutely positioned.
1632 * @method configIframe
1634 configIframe : function(type, args, obj) {
1635 var useIframe = args[0];
1638 if (Dom.inDocument(this.oDomContainer)) {
1640 var pos = Dom.getStyle(this.oDomContainer, "position");
1642 if (pos == "absolute" || pos == "relative") {
1644 if (!Dom.inDocument(this.iframe)) {
1645 this.iframe = document.createElement("iframe");
1646 this.iframe.src = "javascript:false;";
1648 Dom.setStyle(this.iframe, "opacity", "0");
1650 if (YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) {
1651 Dom.addClass(this.iframe, "fixedsize");
1654 this.oDomContainer.insertBefore(this.iframe, this.oDomContainer.firstChild);
1659 if (this.iframe.parentNode) {
1660 this.iframe.parentNode.removeChild(this.iframe);
1670 * Default handler for the "title" property
1671 * @method configTitle
1673 configTitle : function(type, args, obj) {
1674 var title = args[0];
1676 // "" disables title bar
1678 this.createTitleBar(title);
1680 var close = this.cfg.getProperty(DEF_CFG.CLOSE.key);
1682 this.removeTitleBar();
1684 this.createTitleBar(" ");
1690 * Default handler for the "close" property
1691 * @method configClose
1693 configClose : function(type, args, obj) {
1694 var close = args[0],
1695 title = this.cfg.getProperty(DEF_CFG.TITLE.key);
1699 this.createTitleBar(" ");
1701 this.createCloseButton();
1703 this.removeCloseButton();
1705 this.removeTitleBar();
1711 * Initializes Calendar's built-in CustomEvents
1712 * @method initEvents
1714 initEvents : function() {
1716 var defEvents = Calendar._EVENT_TYPES,
1717 CE = YAHOO.util.CustomEvent,
1718 cal = this; // To help with minification
1721 * Fired before a date selection is made
1722 * @event beforeSelectEvent
1724 cal.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
1727 * Fired when a date selection is made
1728 * @event selectEvent
1729 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
1731 cal.selectEvent = new CE(defEvents.SELECT);
1734 * Fired before a date or set of dates is deselected
1735 * @event beforeDeselectEvent
1737 cal.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
1740 * Fired when a date or set of dates is deselected
1741 * @event deselectEvent
1742 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
1744 cal.deselectEvent = new CE(defEvents.DESELECT);
1747 * Fired when the Calendar page is changed
1748 * @event changePageEvent
1750 cal.changePageEvent = new CE(defEvents.CHANGE_PAGE);
1753 * Fired before the Calendar is rendered
1754 * @event beforeRenderEvent
1756 cal.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
1759 * Fired when the Calendar is rendered
1760 * @event renderEvent
1762 cal.renderEvent = new CE(defEvents.RENDER);
1765 * Fired just before the Calendar is to be destroyed
1766 * @event beforeDestroyEvent
1768 cal.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
1771 * Fired after the Calendar is destroyed. This event should be used
1772 * for notification only. When this event is fired, important Calendar instance
1773 * properties, dom references and event listeners have already been
1774 * removed/dereferenced, and hence the Calendar instance is not in a usable
1777 * @event destroyEvent
1779 cal.destroyEvent = new CE(defEvents.DESTROY);
1782 * Fired when the Calendar is reset
1785 cal.resetEvent = new CE(defEvents.RESET);
1788 * Fired when the Calendar is cleared
1791 cal.clearEvent = new CE(defEvents.CLEAR);
1794 * Fired just before the Calendar is to be shown
1795 * @event beforeShowEvent
1797 cal.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
1800 * Fired after the Calendar is shown
1803 cal.showEvent = new CE(defEvents.SHOW);
1806 * Fired just before the Calendar is to be hidden
1807 * @event beforeHideEvent
1809 cal.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
1812 * Fired after the Calendar is hidden
1815 cal.hideEvent = new CE(defEvents.HIDE);
1818 * Fired just before the CalendarNavigator is to be shown
1819 * @event beforeShowNavEvent
1821 cal.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
1824 * Fired after the CalendarNavigator is shown
1825 * @event showNavEvent
1827 cal.showNavEvent = new CE(defEvents.SHOW_NAV);
1830 * Fired just before the CalendarNavigator is to be hidden
1831 * @event beforeHideNavEvent
1833 cal.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
1836 * Fired after the CalendarNavigator is hidden
1837 * @event hideNavEvent
1839 cal.hideNavEvent = new CE(defEvents.HIDE_NAV);
1842 * Fired just before the CalendarNavigator is to be rendered
1843 * @event beforeRenderNavEvent
1845 cal.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
1848 * Fired after the CalendarNavigator is rendered
1849 * @event renderNavEvent
1851 cal.renderNavEvent = new CE(defEvents.RENDER_NAV);
1853 cal.beforeSelectEvent.subscribe(cal.onBeforeSelect, this, true);
1854 cal.selectEvent.subscribe(cal.onSelect, this, true);
1855 cal.beforeDeselectEvent.subscribe(cal.onBeforeDeselect, this, true);
1856 cal.deselectEvent.subscribe(cal.onDeselect, this, true);
1857 cal.changePageEvent.subscribe(cal.onChangePage, this, true);
1858 cal.renderEvent.subscribe(cal.onRender, this, true);
1859 cal.resetEvent.subscribe(cal.onReset, this, true);
1860 cal.clearEvent.subscribe(cal.onClear, this, true);
1864 * The default event handler for clicks on the "Previous Month" navigation UI
1866 * @method doPreviousMonthNav
1867 * @param {DOMEvent} e The DOM event
1868 * @param {Calendar} cal A reference to the calendar
1870 doPreviousMonthNav : function(e, cal) {
1871 Event.preventDefault(e);
1872 // previousMonth invoked in a timeout, to allow
1873 // event to bubble up, with correct target. Calling
1874 // previousMonth, will call render which will remove
1875 // HTML which generated the event, resulting in an
1876 // invalid event target in certain browsers.
1877 setTimeout(function() {
1878 cal.previousMonth();
1879 var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_LEFT, "a", cal.oDomContainer);
1880 if (navs && navs[0]) {
1891 * The default event handler for clicks on the "Next Month" navigation UI
1893 * @method doNextMonthNav
1894 * @param {DOMEvent} e The DOM event
1895 * @param {Calendar} cal A reference to the calendar
1897 doNextMonthNav : function(e, cal) {
1898 Event.preventDefault(e);
1899 setTimeout(function() {
1901 var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_RIGHT, "a", cal.oDomContainer);
1902 if (navs && navs[0]) {
1913 * The default event handler for date cell selection. Currently attached to
1914 * the Calendar's bounding box, referenced by it's <a href="#property_oDomContainer">oDomContainer</a> property.
1916 * @method doSelectCell
1917 * @param {DOMEvent} e The DOM event
1918 * @param {Calendar} cal A reference to the calendar
1920 doSelectCell : function(e, cal) {
1921 var cell, d, date, index;
1923 var target = Event.getTarget(e),
1924 tagName = target.tagName.toLowerCase(),
1925 defSelector = false;
1927 while (tagName != "td" && !Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1929 if (!defSelector && tagName == "a" && Dom.hasClass(target, cal.Style.CSS_CELL_SELECTOR)) {
1933 target = target.parentNode;
1934 tagName = target.tagName.toLowerCase();
1936 if (target == this.oDomContainer || tagName == "html") {
1942 // Stop link href navigation for default renderer
1943 Event.preventDefault(e);
1948 if (Dom.hasClass(cell, cal.Style.CSS_CELL_SELECTABLE)) {
1949 index = cal.getIndexFromId(cell.id);
1951 d = cal.cellDates[index];
1953 date = DateMath.getDate(d[0],d[1]-1,d[2]);
1957 if (cal.Options.MULTI_SELECT) {
1958 link = cell.getElementsByTagName("a")[0];
1963 var cellDate = cal.cellDates[index];
1964 var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
1966 if (cellDateIndex > -1) {
1967 cal.deselectCell(index);
1969 cal.selectCell(index);
1973 link = cell.getElementsByTagName("a")[0];
1977 cal.selectCell(index);
1985 * The event that is executed when the user hovers over a cell
1986 * @method doCellMouseOver
1987 * @param {DOMEvent} e The event
1988 * @param {Calendar} cal A reference to the calendar passed by the Event utility
1990 doCellMouseOver : function(e, cal) {
1993 target = Event.getTarget(e);
1998 while (target.tagName && target.tagName.toLowerCase() != "td") {
1999 target = target.parentNode;
2000 if (!target.tagName || target.tagName.toLowerCase() == "html") {
2005 if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
2006 Dom.addClass(target, cal.Style.CSS_CELL_HOVER);
2011 * The event that is executed when the user moves the mouse out of a cell
2012 * @method doCellMouseOut
2013 * @param {DOMEvent} e The event
2014 * @param {Calendar} cal A reference to the calendar passed by the Event utility
2016 doCellMouseOut : function(e, cal) {
2019 target = Event.getTarget(e);
2024 while (target.tagName && target.tagName.toLowerCase() != "td") {
2025 target = target.parentNode;
2026 if (!target.tagName || target.tagName.toLowerCase() == "html") {
2031 if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
2032 Dom.removeClass(target, cal.Style.CSS_CELL_HOVER);
2036 setupConfig : function() {
2040 * The month/year representing the current visible Calendar date (mm/yyyy)
2042 * @type String | Date
2043 * @default today's date
2045 cfg.addProperty(DEF_CFG.PAGEDATE.key, { value:new Date(), handler:this.configPageDate } );
2048 * The date or range of dates representing the current Calendar selection
2053 cfg.addProperty(DEF_CFG.SELECTED.key, { value:[], handler:this.configSelected } );
2056 * The title to display above the Calendar's month header
2061 cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
2064 * Whether or not a close button should be displayed for this Calendar
2069 cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
2072 * 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.
2073 * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be
2074 * enabled if required.
2078 * @default true for IE6 and below, false for all other browsers
2080 cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
2083 * The minimum selectable date in the current Calendar (mm/dd/yyyy)
2085 * @type String | Date
2088 cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.configMinDate } );
2091 * The maximum selectable date in the current Calendar (mm/dd/yyyy)
2093 * @type String | Date
2096 cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.configMaxDate } );
2099 // Options properties
2102 * True if the Calendar should allow multiple selections. False by default.
2103 * @config MULTI_SELECT
2107 cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2110 * The weekday the week begins on. Default is 0 (Sunday = 0, Monday = 1 ... Saturday = 6).
2111 * @config START_WEEKDAY
2115 cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.configOptions, validator:cfg.checkNumber } );
2118 * True if the Calendar should show weekday labels. True by default.
2119 * @config SHOW_WEEKDAYS
2123 cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2126 * True if the Calendar should show week row headers. False by default.
2127 * @config SHOW_WEEK_HEADER
2131 cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key, { value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2134 * True if the Calendar should show week row footers. False by default.
2135 * @config SHOW_WEEK_FOOTER
2139 cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2142 * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
2143 * @config HIDE_BLANK_WEEKS
2147 cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key, { value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2150 * The image that should be used for the left navigation arrow.
2151 * @config NAV_ARROW_LEFT
2153 * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
2156 cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.configOptions } );
2159 * The image that should be used for the right navigation arrow.
2160 * @config NAV_ARROW_RIGHT
2162 * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
2165 cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.configOptions } );
2167 // Locale properties
2170 * The short month labels for the current locale.
2171 * @config MONTHS_SHORT
2173 * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
2175 cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.configLocale } );
2178 * The long month labels for the current locale.
2179 * @config MONTHS_LONG
2181 * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
2183 cfg.addProperty(DEF_CFG.MONTHS_LONG.key, { value:DEF_CFG.MONTHS_LONG.value, handler:this.configLocale } );
2186 * The 1-character weekday labels for the current locale.
2187 * @config WEEKDAYS_1CHAR
2189 * @default ["S", "M", "T", "W", "T", "F", "S"]
2191 cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.configLocale } );
2194 * The short weekday labels for the current locale.
2195 * @config WEEKDAYS_SHORT
2197 * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
2199 cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.configLocale } );
2202 * The medium weekday labels for the current locale.
2203 * @config WEEKDAYS_MEDIUM
2205 * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
2207 cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.configLocale } );
2210 * The long weekday labels for the current locale.
2211 * @config WEEKDAYS_LONG
2213 * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
2215 cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.configLocale } );
2218 * Refreshes the locale values used to build the Calendar.
2219 * @method refreshLocale
2222 var refreshLocale = function() {
2223 cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
2224 cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
2227 cfg.subscribeToConfigEvent(DEF_CFG.START_WEEKDAY.key, refreshLocale, this, true);
2228 cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_SHORT.key, refreshLocale, this, true);
2229 cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_LONG.key, refreshLocale, this, true);
2230 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_1CHAR.key, refreshLocale, this, true);
2231 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_SHORT.key, refreshLocale, this, true);
2232 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_MEDIUM.key, refreshLocale, this, true);
2233 cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_LONG.key, refreshLocale, this, true);
2236 * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
2237 * @config LOCALE_MONTHS
2241 cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.configLocaleValues } );
2244 * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
2245 * @config LOCALE_WEEKDAYS
2249 cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.configLocaleValues } );
2252 * The value used to delimit individual dates in a date string passed to various Calendar functions.
2253 * @config DATE_DELIMITER
2257 cfg.addProperty(DEF_CFG.DATE_DELIMITER.key, { value:DEF_CFG.DATE_DELIMITER.value, handler:this.configLocale } );
2260 * The value used to delimit date fields in a date string passed to various Calendar functions.
2261 * @config DATE_FIELD_DELIMITER
2265 cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key, { value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.configLocale } );
2268 * The value used to delimit date ranges in a date string passed to various Calendar functions.
2269 * @config DATE_RANGE_DELIMITER
2273 cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key, { value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.configLocale } );
2276 * The position of the month in a month/year date string
2277 * @config MY_MONTH_POSITION
2281 cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2284 * The position of the year in a month/year date string
2285 * @config MY_YEAR_POSITION
2289 cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2292 * The position of the month in a month/day date string
2293 * @config MD_MONTH_POSITION
2297 cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2300 * The position of the day in a month/year date string
2301 * @config MD_DAY_POSITION
2305 cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key, { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2308 * The position of the month in a month/day/year date string
2309 * @config MDY_MONTH_POSITION
2313 cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2316 * The position of the day in a month/day/year date string
2317 * @config MDY_DAY_POSITION
2321 cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2324 * The position of the year in a month/day/year date string
2325 * @config MDY_YEAR_POSITION
2329 cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2332 * The position of the month in the month year label string used as the Calendar header
2333 * @config MY_LABEL_MONTH_POSITION
2337 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2340 * The position of the year in the month year label string used as the Calendar header
2341 * @config MY_LABEL_YEAR_POSITION
2345 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2348 * The suffix used after the month when rendering the Calendar header
2349 * @config MY_LABEL_MONTH_SUFFIX
2353 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.configLocale } );
2356 * The suffix used after the year when rendering the Calendar header
2357 * @config MY_LABEL_YEAR_SUFFIX
2361 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.configLocale } );
2364 * Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a
2365 * specific Month/Year without having to scroll sequentially through months.
2367 * Setting this property to null (default value) or false, will disable the CalendarNavigator UI.
2370 * Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values.
2373 * This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI.
2374 * The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object.
2375 * Any properties which are not provided will use the default values (defined in the CalendarNavigator class).
2379 * <dd><em>Object</em> : An object with the properties shown below, defining the string labels to use in the Navigator's UI
2381 * <dt>month</dt><dd><em>String</em> : The string to use for the month label. Defaults to "Month".</dd>
2382 * <dt>year</dt><dd><em>String</em> : The string to use for the year label. Defaults to "Year".</dd>
2383 * <dt>submit</dt><dd><em>String</em> : The string to use for the submit button label. Defaults to "Okay".</dd>
2384 * <dt>cancel</dt><dd><em>String</em> : The string to use for the cancel button label. Defaults to "Cancel".</dd>
2385 * <dt>invalidYear</dt><dd><em>String</em> : The string to use for invalid year values. Defaults to "Year needs to be a number".</dd>
2388 * <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>
2389 * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
2395 * month:"Calendar Month",
2396 * year:"Calendar Year",
2399 * invalidYear: "Please enter a valid year"
2401 * monthFormat: YAHOO.widget.Calendar.SHORT,
2402 * initialFocus: "month"
2406 * @type {Object|Boolean}
2409 cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
2412 * The map of UI strings which the Calendar UI uses.
2416 * @default An object with the properties shown below:
2418 * <dt>previousMonth</dt><dd><em>String</em> : The string to use for the "Previous Month" navigation UI. Defaults to "Previous Month".</dd>
2419 * <dt>nextMonth</dt><dd><em>String</em> : The string to use for the "Next Month" navigation UI. Defaults to "Next Month".</dd>
2420 * <dt>close</dt><dd><em>String</em> : The string to use for the close button label. Defaults to "Close".</dd>
2423 cfg.addProperty(DEF_CFG.STRINGS.key, {
2424 value:DEF_CFG.STRINGS.value,
2425 handler:this.configStrings,
2426 validator: function(val) {
2427 return Lang.isObject(val);
2429 supercedes:DEF_CFG.STRINGS.supercedes
2434 * The default handler for the "strings" property
2435 * @method configStrings
2437 configStrings : function(type, args, obj) {
2438 var val = Lang.merge(DEF_CFG.STRINGS.value, args[0]);
2439 this.cfg.setProperty(DEF_CFG.STRINGS.key, val, true);
2443 * The default handler for the "pagedate" property
2444 * @method configPageDate
2446 configPageDate : function(type, args, obj) {
2447 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, this._parsePageDate(args[0]), true);
2451 * The default handler for the "mindate" property
2452 * @method configMinDate
2454 configMinDate : function(type, args, obj) {
2456 if (Lang.isString(val)) {
2457 val = this._parseDate(val);
2458 this.cfg.setProperty(DEF_CFG.MINDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
2463 * The default handler for the "maxdate" property
2464 * @method configMaxDate
2466 configMaxDate : function(type, args, obj) {
2468 if (Lang.isString(val)) {
2469 val = this._parseDate(val);
2470 this.cfg.setProperty(DEF_CFG.MAXDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
2475 * The default handler for the "selected" property
2476 * @method configSelected
2478 configSelected : function(type, args, obj) {
2479 var selected = args[0],
2480 cfgSelected = DEF_CFG.SELECTED.key;
2483 if (Lang.isString(selected)) {
2484 this.cfg.setProperty(cfgSelected, this._parseDates(selected), true);
2487 if (! this._selectedDates) {
2488 this._selectedDates = this.cfg.getProperty(cfgSelected);
2493 * The default handler for all configuration options properties
2494 * @method configOptions
2496 configOptions : function(type, args, obj) {
2497 this.Options[type.toUpperCase()] = args[0];
2501 * The default handler for all configuration locale properties
2502 * @method configLocale
2504 configLocale : function(type, args, obj) {
2505 this.Locale[type.toUpperCase()] = args[0];
2507 this.cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
2508 this.cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
2512 * The default handler for all configuration locale field length properties
2513 * @method configLocaleValues
2515 configLocaleValues : function(type, args, obj) {
2517 type = type.toLowerCase();
2521 Locale = this.Locale;
2524 case DEF_CFG.LOCALE_MONTHS.key:
2526 case Calendar.SHORT:
2527 Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_SHORT.key).concat();
2530 Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_LONG.key).concat();
2534 case DEF_CFG.LOCALE_WEEKDAYS.key:
2536 case Calendar.ONE_CHAR:
2537 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_1CHAR.key).concat();
2539 case Calendar.SHORT:
2540 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_SHORT.key).concat();
2542 case Calendar.MEDIUM:
2543 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_MEDIUM.key).concat();
2546 Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_LONG.key).concat();
2550 var START_WEEKDAY = cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
2552 if (START_WEEKDAY > 0) {
2553 for (var w=0; w < START_WEEKDAY; ++w) {
2554 Locale.LOCALE_WEEKDAYS.push(Locale.LOCALE_WEEKDAYS.shift());
2562 * The default handler for the "navigator" property
2563 * @method configNavigator
2565 configNavigator : function(type, args, obj) {
2567 if (YAHOO.widget.CalendarNavigator && (val === true || Lang.isObject(val))) {
2568 if (!this.oNavigator) {
2569 this.oNavigator = new YAHOO.widget.CalendarNavigator(this);
2570 // Cleanup DOM Refs/Events before innerHTML is removed.
2571 this.beforeRenderEvent.subscribe(function () {
2573 this.oNavigator.erase();
2578 if (this.oNavigator) {
2579 this.oNavigator.destroy();
2580 this.oNavigator = null;
2586 * Defines the style constants for the Calendar
2587 * @method initStyles
2589 initStyles : function() {
2591 var defStyle = Calendar._STYLES;
2595 * @property Style.CSS_ROW_HEADER
2597 CSS_ROW_HEADER: defStyle.CSS_ROW_HEADER,
2599 * @property Style.CSS_ROW_FOOTER
2601 CSS_ROW_FOOTER: defStyle.CSS_ROW_FOOTER,
2603 * @property Style.CSS_CELL
2605 CSS_CELL : defStyle.CSS_CELL,
2607 * @property Style.CSS_CELL_SELECTOR
2609 CSS_CELL_SELECTOR : defStyle.CSS_CELL_SELECTOR,
2611 * @property Style.CSS_CELL_SELECTED
2613 CSS_CELL_SELECTED : defStyle.CSS_CELL_SELECTED,
2615 * @property Style.CSS_CELL_SELECTABLE
2617 CSS_CELL_SELECTABLE : defStyle.CSS_CELL_SELECTABLE,
2619 * @property Style.CSS_CELL_RESTRICTED
2621 CSS_CELL_RESTRICTED : defStyle.CSS_CELL_RESTRICTED,
2623 * @property Style.CSS_CELL_TODAY
2625 CSS_CELL_TODAY : defStyle.CSS_CELL_TODAY,
2627 * @property Style.CSS_CELL_OOM
2629 CSS_CELL_OOM : defStyle.CSS_CELL_OOM,
2631 * @property Style.CSS_CELL_OOB
2633 CSS_CELL_OOB : defStyle.CSS_CELL_OOB,
2635 * @property Style.CSS_HEADER
2637 CSS_HEADER : defStyle.CSS_HEADER,
2639 * @property Style.CSS_HEADER_TEXT
2641 CSS_HEADER_TEXT : defStyle.CSS_HEADER_TEXT,
2643 * @property Style.CSS_BODY
2645 CSS_BODY : defStyle.CSS_BODY,
2647 * @property Style.CSS_WEEKDAY_CELL
2649 CSS_WEEKDAY_CELL : defStyle.CSS_WEEKDAY_CELL,
2651 * @property Style.CSS_WEEKDAY_ROW
2653 CSS_WEEKDAY_ROW : defStyle.CSS_WEEKDAY_ROW,
2655 * @property Style.CSS_FOOTER
2657 CSS_FOOTER : defStyle.CSS_FOOTER,
2659 * @property Style.CSS_CALENDAR
2661 CSS_CALENDAR : defStyle.CSS_CALENDAR,
2663 * @property Style.CSS_SINGLE
2665 CSS_SINGLE : defStyle.CSS_SINGLE,
2667 * @property Style.CSS_CONTAINER
2669 CSS_CONTAINER : defStyle.CSS_CONTAINER,
2671 * @property Style.CSS_NAV_LEFT
2673 CSS_NAV_LEFT : defStyle.CSS_NAV_LEFT,
2675 * @property Style.CSS_NAV_RIGHT
2677 CSS_NAV_RIGHT : defStyle.CSS_NAV_RIGHT,
2679 * @property Style.CSS_NAV
2681 CSS_NAV : defStyle.CSS_NAV,
2683 * @property Style.CSS_CLOSE
2685 CSS_CLOSE : defStyle.CSS_CLOSE,
2687 * @property Style.CSS_CELL_TOP
2689 CSS_CELL_TOP : defStyle.CSS_CELL_TOP,
2691 * @property Style.CSS_CELL_LEFT
2693 CSS_CELL_LEFT : defStyle.CSS_CELL_LEFT,
2695 * @property Style.CSS_CELL_RIGHT
2697 CSS_CELL_RIGHT : defStyle.CSS_CELL_RIGHT,
2699 * @property Style.CSS_CELL_BOTTOM
2701 CSS_CELL_BOTTOM : defStyle.CSS_CELL_BOTTOM,
2703 * @property Style.CSS_CELL_HOVER
2705 CSS_CELL_HOVER : defStyle.CSS_CELL_HOVER,
2707 * @property Style.CSS_CELL_HIGHLIGHT1
2709 CSS_CELL_HIGHLIGHT1 : defStyle.CSS_CELL_HIGHLIGHT1,
2711 * @property Style.CSS_CELL_HIGHLIGHT2
2713 CSS_CELL_HIGHLIGHT2 : defStyle.CSS_CELL_HIGHLIGHT2,
2715 * @property Style.CSS_CELL_HIGHLIGHT3
2717 CSS_CELL_HIGHLIGHT3 : defStyle.CSS_CELL_HIGHLIGHT3,
2719 * @property Style.CSS_CELL_HIGHLIGHT4
2721 CSS_CELL_HIGHLIGHT4 : defStyle.CSS_CELL_HIGHLIGHT4
2726 * Builds the date label that will be displayed in the calendar header or
2727 * footer, depending on configuration.
2728 * @method buildMonthLabel
2729 * @return {String} The formatted calendar month label
2731 buildMonthLabel : function() {
2732 return this._buildMonthLabel(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
2736 * Helper method, to format a Month Year string, given a JavaScript Date, based on the
2737 * Calendar localization settings
2739 * @method _buildMonthLabel
2741 * @param {Date} date
2742 * @return {String} Formated month, year string
2744 _buildMonthLabel : function(date) {
2745 var monthLabel = this.Locale.LOCALE_MONTHS[date.getMonth()] + this.Locale.MY_LABEL_MONTH_SUFFIX,
2746 yearLabel = date.getFullYear() + this.Locale.MY_LABEL_YEAR_SUFFIX;
2748 if (this.Locale.MY_LABEL_MONTH_POSITION == 2 || this.Locale.MY_LABEL_YEAR_POSITION == 1) {
2749 return yearLabel + monthLabel;
2751 return monthLabel + yearLabel;
2756 * Builds the date digit that will be displayed in calendar cells
2757 * @method buildDayLabel
2758 * @param {Date} workingDate The current working date
2759 * @return {String} The formatted day label
2761 buildDayLabel : function(workingDate) {
2762 return workingDate.getDate();
2766 * Creates the title bar element and adds it to Calendar container DIV
2768 * @method createTitleBar
2769 * @param {String} strTitle The title to display in the title bar
2770 * @return The title bar element
2772 createTitleBar : function(strTitle) {
2773 var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || document.createElement("div");
2774 tDiv.className = YAHOO.widget.CalendarGroup.CSS_2UPTITLE;
2775 tDiv.innerHTML = strTitle;
2776 this.oDomContainer.insertBefore(tDiv, this.oDomContainer.firstChild);
2778 Dom.addClass(this.oDomContainer, "withtitle");
2784 * Removes the title bar element from the DOM
2786 * @method removeTitleBar
2788 removeTitleBar : function() {
2789 var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || null;
2791 Event.purgeElement(tDiv);
2792 this.oDomContainer.removeChild(tDiv);
2794 Dom.removeClass(this.oDomContainer, "withtitle");
2798 * Creates the close button HTML element and adds it to Calendar container DIV
2800 * @method createCloseButton
2801 * @return The close HTML element created
2803 createCloseButton : function() {
2804 var cssClose = YAHOO.widget.CalendarGroup.CSS_2UPCLOSE,
2805 DEPR_CLOSE_PATH = "us/my/bn/x_d.gif",
2806 lnk = Dom.getElementsByClassName("link-close", "a", this.oDomContainer)[0],
2807 strings = this.cfg.getProperty(DEF_CFG.STRINGS.key),
2808 closeStr = (strings && strings.close) ? strings.close : "";
2811 lnk = document.createElement("a");
2812 Event.addListener(lnk, "click", function(e, cal) {
2814 Event.preventDefault(e);
2819 lnk.className = "link-close";
2821 if (Calendar.IMG_ROOT !== null) {
2822 var img = Dom.getElementsByClassName(cssClose, "img", lnk)[0] || document.createElement("img");
2823 img.src = Calendar.IMG_ROOT + DEPR_CLOSE_PATH;
2824 img.className = cssClose;
2825 lnk.appendChild(img);
2827 lnk.innerHTML = '<span class="' + cssClose + ' ' + this.Style.CSS_CLOSE + '">' + closeStr + '</span>';
2829 this.oDomContainer.appendChild(lnk);
2835 * Removes the close button HTML element from the DOM
2837 * @method removeCloseButton
2839 removeCloseButton : function() {
2840 var btn = Dom.getElementsByClassName("link-close", "a", this.oDomContainer)[0] || null;
2842 Event.purgeElement(btn);
2843 this.oDomContainer.removeChild(btn);
2848 * Renders the calendar header.
2849 * @method renderHeader
2850 * @param {Array} html The current working HTML array
2851 * @return {Array} The current working HTML array
2853 renderHeader : function(html) {
2857 DEPR_NAV_LEFT = "us/tr/callt.gif",
2858 DEPR_NAV_RIGHT = "us/tr/calrt.gif",
2860 pageDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
2861 strings= cfg.getProperty(DEF_CFG.STRINGS.key),
2862 prevStr = (strings && strings.previousMonth) ? strings.previousMonth : "",
2863 nextStr = (strings && strings.nextMonth) ? strings.nextMonth : "",
2866 if (cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
2870 if (cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
2874 html[html.length] = "<thead>";
2875 html[html.length] = "<tr>";
2876 html[html.length] = '<th colspan="' + colSpan + '" class="' + this.Style.CSS_HEADER_TEXT + '">';
2877 html[html.length] = '<div class="' + this.Style.CSS_HEADER + '">';
2879 var renderLeft, renderRight = false;
2882 if (this.index === 0) {
2885 if (this.index == (this.parent.cfg.getProperty("pages") -1)) {
2894 monthLabel = this._buildMonthLabel(DateMath.subtract(pageDate, DateMath.MONTH, 1));
2896 var leftArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_LEFT.key);
2897 // 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
2898 if (leftArrow === null && Calendar.IMG_ROOT !== null) {
2899 leftArrow = Calendar.IMG_ROOT + DEPR_NAV_LEFT;
2901 var leftStyle = (leftArrow === null) ? "" : ' style="background-image:url(' + leftArrow + ')"';
2902 html[html.length] = '<a class="' + this.Style.CSS_NAV_LEFT + '"' + leftStyle + ' href="#">' + prevStr + ' (' + monthLabel + ')' + '</a>';
2905 var lbl = this.buildMonthLabel();
2906 var cal = this.parent || this;
2907 if (cal.cfg.getProperty("navigator")) {
2908 lbl = "<a class=\"" + this.Style.CSS_NAV + "\" href=\"#\">" + lbl + "</a>";
2910 html[html.length] = lbl;
2913 monthLabel = this._buildMonthLabel(DateMath.add(pageDate, DateMath.MONTH, 1));
2915 var rightArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_RIGHT.key);
2916 if (rightArrow === null && Calendar.IMG_ROOT !== null) {
2917 rightArrow = Calendar.IMG_ROOT + DEPR_NAV_RIGHT;
2919 var rightStyle = (rightArrow === null) ? "" : ' style="background-image:url(' + rightArrow + ')"';
2920 html[html.length] = '<a class="' + this.Style.CSS_NAV_RIGHT + '"' + rightStyle + ' href="#">' + nextStr + ' (' + monthLabel + ')' + '</a>';
2923 html[html.length] = '</div>\n</th>\n</tr>';
2925 if (cfg.getProperty(DEF_CFG.SHOW_WEEKDAYS.key)) {
2926 html = this.buildWeekdays(html);
2929 html[html.length] = '</thead>';
2935 * Renders the Calendar's weekday headers.
2936 * @method buildWeekdays
2937 * @param {Array} html The current working HTML array
2938 * @return {Array} The current working HTML array
2940 buildWeekdays : function(html) {
2942 html[html.length] = '<tr class="' + this.Style.CSS_WEEKDAY_ROW + '">';
2944 if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
2945 html[html.length] = '<th> </th>';
2948 for(var i=0;i < this.Locale.LOCALE_WEEKDAYS.length; ++i) {
2949 html[html.length] = '<th class="calweekdaycell">' + this.Locale.LOCALE_WEEKDAYS[i] + '</th>';
2952 if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
2953 html[html.length] = '<th> </th>';
2956 html[html.length] = '</tr>';
2962 * Renders the calendar body.
2963 * @method renderBody
2964 * @param {Date} workingDate The current working Date being used for the render process
2965 * @param {Array} html The current working HTML array
2966 * @return {Array} The current working HTML array
2968 renderBody : function(workingDate, html) {
2970 var startDay = this.cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
2972 this.preMonthDays = workingDate.getDay();
2974 this.preMonthDays -= startDay;
2976 if (this.preMonthDays < 0) {
2977 this.preMonthDays += 7;
2980 this.monthDays = DateMath.findMonthEnd(workingDate).getDate();
2981 this.postMonthDays = Calendar.DISPLAY_DAYS-this.preMonthDays-this.monthDays;
2984 workingDate = DateMath.subtract(workingDate, DateMath.DAY, this.preMonthDays);
2989 cellPrefix = "_cell",
2990 workingDayPrefix = "wd",
2996 todayYear = t.getFullYear(),
2997 todayMonth = t.getMonth(),
2998 todayDate = t.getDate(),
2999 useDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
3000 hideBlankWeeks = cfg.getProperty(DEF_CFG.HIDE_BLANK_WEEKS.key),
3001 showWeekFooter = cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key),
3002 showWeekHeader = cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key),
3003 mindate = cfg.getProperty(DEF_CFG.MINDATE.key),
3004 maxdate = cfg.getProperty(DEF_CFG.MAXDATE.key);
3007 mindate = DateMath.clearTime(mindate);
3010 maxdate = DateMath.clearTime(maxdate);
3013 html[html.length] = '<tbody class="m' + (useDate.getMonth()+1) + ' ' + this.Style.CSS_BODY + '">';
3016 tempDiv = document.createElement("div"),
3017 cell = document.createElement("td");
3019 tempDiv.appendChild(cell);
3021 var cal = this.parent || this;
3023 for (var r=0;r<6;r++) {
3024 weekNum = DateMath.getWeekNumber(workingDate, startDay);
3025 weekClass = weekPrefix + weekNum;
3027 // Local OOM check for performance, since we already have pagedate
3028 if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth()) {
3031 html[html.length] = '<tr class="' + weekClass + '">';
3033 if (showWeekHeader) { html = this.renderRowHeader(weekNum, html); }
3035 for (var d=0; d < 7; d++){ // Render actual days
3039 this.clearElement(cell);
3040 cell.className = this.Style.CSS_CELL;
3041 cell.id = this.id + cellPrefix + i;
3043 if (workingDate.getDate() == todayDate &&
3044 workingDate.getMonth() == todayMonth &&
3045 workingDate.getFullYear() == todayYear) {
3046 cellRenderers[cellRenderers.length]=cal.renderCellStyleToday;
3049 var workingArray = [workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()];
3050 this.cellDates[this.cellDates.length] = workingArray; // Add this date to cellDates
3052 // Local OOM check for performance, since we already have pagedate
3053 if (workingDate.getMonth() != useDate.getMonth()) {
3054 cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth;
3056 Dom.addClass(cell, workingDayPrefix + workingDate.getDay());
3057 Dom.addClass(cell, dayPrefix + workingDate.getDate());
3059 for (var s=0;s<this.renderStack.length;++s) {
3063 var rArray = this.renderStack[s],
3071 month = rArray[1][1];
3073 year = rArray[1][0];
3075 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == year) {
3076 renderer = rArray[2];
3077 this.renderStack.splice(s,1);
3080 case Calendar.MONTH_DAY:
3081 month = rArray[1][0];
3084 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day) {
3085 renderer = rArray[2];
3086 this.renderStack.splice(s,1);
3089 case Calendar.RANGE:
3090 var date1 = rArray[1][0],
3091 date2 = rArray[1][1],
3095 d1 = DateMath.getDate(d1year, d1month-1, d1day),
3099 d2 = DateMath.getDate(d2year, d2month-1, d2day);
3101 if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime()) {
3102 renderer = rArray[2];
3104 if (workingDate.getTime()==d2.getTime()) {
3105 this.renderStack.splice(s,1);
3109 case Calendar.WEEKDAY:
3110 var weekday = rArray[1][0];
3111 if (workingDate.getDay()+1 == weekday) {
3112 renderer = rArray[2];
3115 case Calendar.MONTH:
3116 month = rArray[1][0];
3117 if (workingDate.getMonth()+1 == month) {
3118 renderer = rArray[2];
3124 cellRenderers[cellRenderers.length]=renderer;
3130 if (this._indexOfSelectedFieldArray(workingArray) > -1) {
3131 cellRenderers[cellRenderers.length]=cal.renderCellStyleSelected;
3134 if ((mindate && (workingDate.getTime() < mindate.getTime())) ||
3135 (maxdate && (workingDate.getTime() > maxdate.getTime()))
3137 cellRenderers[cellRenderers.length]=cal.renderOutOfBoundsDate;
3139 cellRenderers[cellRenderers.length]=cal.styleCellDefault;
3140 cellRenderers[cellRenderers.length]=cal.renderCellDefault;
3143 for (var x=0; x < cellRenderers.length; ++x) {
3144 if (cellRenderers[x].call(cal, workingDate, cell) == Calendar.STOP_RENDER) {
3149 workingDate.setTime(workingDate.getTime() + DateMath.ONE_DAY_MS);
3150 // Just in case we crossed DST/Summertime boundaries
3151 workingDate = DateMath.clearTime(workingDate);
3153 if (i >= 0 && i <= 6) {
3154 Dom.addClass(cell, this.Style.CSS_CELL_TOP);
3156 if ((i % 7) === 0) {
3157 Dom.addClass(cell, this.Style.CSS_CELL_LEFT);
3159 if (((i+1) % 7) === 0) {
3160 Dom.addClass(cell, this.Style.CSS_CELL_RIGHT);
3163 var postDays = this.postMonthDays;
3164 if (hideBlankWeeks && postDays >= 7) {
3165 var blankWeeks = Math.floor(postDays/7);
3166 for (var p=0;p<blankWeeks;++p) {
3171 if (i >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
3172 Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM);
3175 html[html.length] = tempDiv.innerHTML;
3179 if (showWeekFooter) { html = this.renderRowFooter(weekNum, html); }
3181 html[html.length] = '</tr>';
3185 html[html.length] = '</tbody>';
3191 * Renders the calendar footer. In the default implementation, there is
3193 * @method renderFooter
3194 * @param {Array} html The current working HTML array
3195 * @return {Array} The current working HTML array
3197 renderFooter : function(html) { return html; },
3200 * Renders the calendar after it has been configured. The render() method has a specific call chain that will execute
3201 * when the method is called: renderHeader, renderBody, renderFooter.
3202 * Refer to the documentation for those methods for information on
3203 * individual render tasks.
3206 render : function() {
3207 this.beforeRenderEvent.fire();
3209 // Find starting day of the current month
3210 var workingDate = DateMath.findMonthStart(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
3212 this.resetRenderers();
3213 this.cellDates.length = 0;
3215 Event.purgeElement(this.oDomContainer, true);
3219 html[html.length] = '<table cellSpacing="0" class="' + this.Style.CSS_CALENDAR + ' y' + workingDate.getFullYear() + '" id="' + this.id + '">';
3220 html = this.renderHeader(html);
3221 html = this.renderBody(workingDate, html);
3222 html = this.renderFooter(html);
3223 html[html.length] = '</table>';
3225 this.oDomContainer.innerHTML = html.join("\n");
3227 this.applyListeners();
3228 this.cells = this.oDomContainer.getElementsByTagName("td");
3230 this.cfg.refireEvent(DEF_CFG.TITLE.key);
3231 this.cfg.refireEvent(DEF_CFG.CLOSE.key);
3232 this.cfg.refireEvent(DEF_CFG.IFRAME.key);
3234 this.renderEvent.fire();
3238 * Applies the Calendar's DOM listeners to applicable elements.
3239 * @method applyListeners
3241 applyListeners : function() {
3242 var root = this.oDomContainer,
3243 cal = this.parent || this,
3247 var linkLeft = Dom.getElementsByClassName(this.Style.CSS_NAV_LEFT, anchor, root),
3248 linkRight = Dom.getElementsByClassName(this.Style.CSS_NAV_RIGHT, anchor, root);
3250 if (linkLeft && linkLeft.length > 0) {
3251 this.linkLeft = linkLeft[0];
3252 Event.addListener(this.linkLeft, click, this.doPreviousMonthNav, cal, true);
3255 if (linkRight && linkRight.length > 0) {
3256 this.linkRight = linkRight[0];
3257 Event.addListener(this.linkRight, click, this.doNextMonthNav, cal, true);
3260 if (cal.cfg.getProperty("navigator") !== null) {
3261 this.applyNavListeners();
3264 if (this.domEventMap) {
3266 for (var cls in this.domEventMap) {
3267 if (Lang.hasOwnProperty(this.domEventMap, cls)) {
3268 var items = this.domEventMap[cls];
3270 if (! (items instanceof Array)) {
3274 for (var i=0;i<items.length;i++) {
3275 var item = items[i];
3276 elements = Dom.getElementsByClassName(cls, item.tag, this.oDomContainer);
3278 for (var c=0;c<elements.length;c++) {
3280 Event.addListener(el, item.event, item.handler, item.scope, item.correct );
3287 Event.addListener(this.oDomContainer, "click", this.doSelectCell, this);
3288 Event.addListener(this.oDomContainer, "mouseover", this.doCellMouseOver, this);
3289 Event.addListener(this.oDomContainer, "mouseout", this.doCellMouseOut, this);
3292 applyNavListeners : function() {
3293 var calParent = this.parent || this,
3295 navBtns = Dom.getElementsByClassName(this.Style.CSS_NAV, "a", this.oDomContainer);
3297 if (navBtns.length > 0) {
3299 Event.addListener(navBtns, "click", function (e, obj) {
3300 var target = Event.getTarget(e);
3302 if (this === target || Dom.isAncestor(this, target)) {
3303 Event.preventDefault(e);
3305 var navigator = calParent.oNavigator;
3307 var pgdate = cal.cfg.getProperty("pagedate");
3308 navigator.setYear(pgdate.getFullYear());
3309 navigator.setMonth(pgdate.getMonth());
3317 * Retrieves the Date object for the specified Calendar cell
3318 * @method getDateByCellId
3319 * @param {String} id The id of the cell
3320 * @return {Date} The Date object for the specified Calendar cell
3322 getDateByCellId : function(id) {
3323 var date = this.getDateFieldsByCellId(id);
3324 return (date) ? DateMath.getDate(date[0],date[1]-1,date[2]) : null;
3328 * Retrieves the Date object for the specified Calendar cell
3329 * @method getDateFieldsByCellId
3330 * @param {String} id The id of the cell
3331 * @return {Array} The array of Date fields for the specified Calendar cell
3333 getDateFieldsByCellId : function(id) {
3334 id = this.getIndexFromId(id);
3335 return (id > -1) ? this.cellDates[id] : null;
3339 * Find the Calendar's cell index for a given date.
3340 * If the date is not found, the method returns -1.
3342 * The returned index can be used to lookup the cell HTMLElement
3343 * using the Calendar's cells array or passed to selectCell to select
3347 * See <a href="#cells">cells</a>, <a href="#selectCell">selectCell</a>.
3349 * @method getCellIndex
3350 * @param {Date} date JavaScript Date object, for which to find a cell index.
3351 * @return {Number} The index of the date in Calendars cellDates/cells arrays, or -1 if the date
3352 * is not on the curently rendered Calendar page.
3354 getCellIndex : function(date) {
3357 var m = date.getMonth(),
3358 y = date.getFullYear(),
3360 dates = this.cellDates;
3362 for (var i = 0; i < dates.length; ++i) {
3363 var cellDate = dates[i];
3364 if (cellDate[0] === y && cellDate[1] === m+1 && cellDate[2] === d) {
3374 * Given the id used to mark each Calendar cell, this method
3375 * extracts the index number from the id.
3377 * @param {String} strId The cell id
3378 * @return {Number} The index of the cell, or -1 if id does not contain an index number
3380 getIndexFromId : function(strId) {
3382 li = strId.lastIndexOf("_cell");
3385 idx = parseInt(strId.substring(li + 5), 10);
3391 // BEGIN BUILT-IN TABLE CELL RENDERERS
3394 * Renders a cell that falls before the minimum date or after the maximum date.
3396 * @method renderOutOfBoundsDate
3397 * @param {Date} workingDate The current working Date object being used to generate the calendar
3398 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3399 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3400 * should not be terminated
3402 renderOutOfBoundsDate : function(workingDate, cell) {
3403 Dom.addClass(cell, this.Style.CSS_CELL_OOB);
3404 cell.innerHTML = workingDate.getDate();
3405 return Calendar.STOP_RENDER;
3409 * Renders the row header for a week.
3410 * @method renderRowHeader
3411 * @param {Number} weekNum The week number of the current row
3412 * @param {Array} cell The current working HTML array
3414 renderRowHeader : function(weekNum, html) {
3415 html[html.length] = '<th class="calrowhead">' + weekNum + '</th>';
3420 * Renders the row footer for a week.
3421 * @method renderRowFooter
3422 * @param {Number} weekNum The week number of the current row
3423 * @param {Array} cell The current working HTML array
3425 renderRowFooter : function(weekNum, html) {
3426 html[html.length] = '<th class="calrowfoot">' + weekNum + '</th>';
3431 * Renders a single standard calendar cell in the calendar widget table.
3432 * All logic for determining how a standard default cell will be rendered is
3433 * encapsulated in this method, and must be accounted for when extending the
3435 * @method renderCellDefault
3436 * @param {Date} workingDate The current working Date object being used to generate the calendar
3437 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3439 renderCellDefault : function(workingDate, cell) {
3440 cell.innerHTML = '<a href="#" class="' + this.Style.CSS_CELL_SELECTOR + '">' + this.buildDayLabel(workingDate) + "</a>";
3444 * Styles a selectable cell.
3445 * @method styleCellDefault
3446 * @param {Date} workingDate The current working Date object being used to generate the calendar
3447 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3449 styleCellDefault : function(workingDate, cell) {
3450 Dom.addClass(cell, this.Style.CSS_CELL_SELECTABLE);
3455 * Renders a single standard calendar cell using the CSS hightlight1 style
3456 * @method renderCellStyleHighlight1
3457 * @param {Date} workingDate The current working Date object being used to generate the calendar
3458 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3460 renderCellStyleHighlight1 : function(workingDate, cell) {
3461 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
3465 * Renders a single standard calendar cell using the CSS hightlight2 style
3466 * @method renderCellStyleHighlight2
3467 * @param {Date} workingDate The current working Date object being used to generate the calendar
3468 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3470 renderCellStyleHighlight2 : function(workingDate, cell) {
3471 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
3475 * Renders a single standard calendar cell using the CSS hightlight3 style
3476 * @method renderCellStyleHighlight3
3477 * @param {Date} workingDate The current working Date object being used to generate the calendar
3478 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3480 renderCellStyleHighlight3 : function(workingDate, cell) {
3481 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
3485 * Renders a single standard calendar cell using the CSS hightlight4 style
3486 * @method renderCellStyleHighlight4
3487 * @param {Date} workingDate The current working Date object being used to generate the calendar
3488 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3490 renderCellStyleHighlight4 : function(workingDate, cell) {
3491 Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
3495 * Applies the default style used for rendering today's date to the current calendar cell
3496 * @method renderCellStyleToday
3497 * @param {Date} workingDate The current working Date object being used to generate the calendar
3498 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3500 renderCellStyleToday : function(workingDate, cell) {
3501 Dom.addClass(cell, this.Style.CSS_CELL_TODAY);
3505 * Applies the default style used for rendering selected dates to the current calendar cell
3506 * @method renderCellStyleSelected
3507 * @param {Date} workingDate The current working Date object being used to generate the calendar
3508 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3509 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3510 * should not be terminated
3512 renderCellStyleSelected : function(workingDate, cell) {
3513 Dom.addClass(cell, this.Style.CSS_CELL_SELECTED);
3517 * Applies the default style used for rendering dates that are not a part of the current
3518 * month (preceding or trailing the cells for the current month)
3519 * @method renderCellNotThisMonth
3520 * @param {Date} workingDate The current working Date object being used to generate the calendar
3521 * @param {HTMLTableCellElement} cell The current working cell in the calendar
3522 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3523 * should not be terminated
3525 renderCellNotThisMonth : function(workingDate, cell) {
3526 Dom.addClass(cell, this.Style.CSS_CELL_OOM);
3527 cell.innerHTML=workingDate.getDate();
3528 return Calendar.STOP_RENDER;
3532 * Renders the current calendar cell as a non-selectable "black-out" date using the default
3534 * @method renderBodyCellRestricted
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
3537 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3538 * should not be terminated
3540 renderBodyCellRestricted : function(workingDate, cell) {
3541 Dom.addClass(cell, this.Style.CSS_CELL);
3542 Dom.addClass(cell, this.Style.CSS_CELL_RESTRICTED);
3543 cell.innerHTML=workingDate.getDate();
3544 return Calendar.STOP_RENDER;
3547 // END BUILT-IN TABLE CELL RENDERERS
3549 // BEGIN MONTH NAVIGATION METHODS
3552 * Adds the designated number of months to the current calendar month, and sets the current
3553 * calendar page date to the new month.
3555 * @param {Number} count The number of months to add to the current calendar
3557 addMonths : function(count) {
3558 var cfgPageDate = DEF_CFG.PAGEDATE.key;
3559 this.cfg.setProperty(cfgPageDate, DateMath.add(this.cfg.getProperty(cfgPageDate), DateMath.MONTH, count));
3560 this.resetRenderers();
3561 this.changePageEvent.fire();
3565 * Subtracts the designated number of months from the current calendar month, and sets the current
3566 * calendar page date to the new month.
3567 * @method subtractMonths
3568 * @param {Number} count The number of months to subtract from the current calendar
3570 subtractMonths : function(count) {
3571 var cfgPageDate = DEF_CFG.PAGEDATE.key;
3572 this.cfg.setProperty(cfgPageDate, DateMath.subtract(this.cfg.getProperty(cfgPageDate), DateMath.MONTH, count));
3573 this.resetRenderers();
3574 this.changePageEvent.fire();
3578 * Adds the designated number of years to the current calendar, and sets the current
3579 * calendar page date to the new month.
3581 * @param {Number} count The number of years to add to the current calendar
3583 addYears : function(count) {
3584 var cfgPageDate = DEF_CFG.PAGEDATE.key;
3585 this.cfg.setProperty(cfgPageDate, DateMath.add(this.cfg.getProperty(cfgPageDate), DateMath.YEAR, count));
3586 this.resetRenderers();
3587 this.changePageEvent.fire();
3591 * Subtcats the designated number of years from the current calendar, and sets the current
3592 * calendar page date to the new month.
3593 * @method subtractYears
3594 * @param {Number} count The number of years to subtract from the current calendar
3596 subtractYears : function(count) {
3597 var cfgPageDate = DEF_CFG.PAGEDATE.key;
3598 this.cfg.setProperty(cfgPageDate, DateMath.subtract(this.cfg.getProperty(cfgPageDate), DateMath.YEAR, count));
3599 this.resetRenderers();
3600 this.changePageEvent.fire();
3604 * Navigates to the next month page in the calendar widget.
3607 nextMonth : function() {
3612 * Navigates to the previous month page in the calendar widget.
3613 * @method previousMonth
3615 previousMonth : function() {
3616 this.subtractMonths(1);
3620 * Navigates to the next year in the currently selected month in the calendar widget.
3623 nextYear : function() {
3628 * Navigates to the previous year in the currently selected month in the calendar widget.
3629 * @method previousYear
3631 previousYear : function() {
3632 this.subtractYears(1);
3635 // END MONTH NAVIGATION METHODS
3637 // BEGIN SELECTION METHODS
3640 * Resets the calendar widget to the originally selected month and year, and
3641 * sets the calendar to the initial selection(s).
3644 reset : function() {
3645 this.cfg.resetProperty(DEF_CFG.SELECTED.key);
3646 this.cfg.resetProperty(DEF_CFG.PAGEDATE.key);
3647 this.resetEvent.fire();
3651 * Clears the selected dates in the current calendar widget and sets the calendar
3652 * to the current month and year.
3655 clear : function() {
3656 this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
3657 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.today.getTime()));
3658 this.clearEvent.fire();
3662 * Selects a date or a collection of dates on the current calendar. This method, by default,
3663 * does not call the render method explicitly. Once selection has completed, render must be
3664 * called for the changes to be reflected visually.
3666 * Any dates which are OOB (out of bounds, not selectable) will not be selected and the array of
3667 * selected dates passed to the selectEvent will not contain OOB dates.
3669 * If all dates are OOB, the no state change will occur; beforeSelect and select events will not be fired.
3672 * @param {String/Date/Date[]} date The date string of dates to select in the current calendar. Valid formats are
3673 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3674 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3675 * This method can also take a JavaScript Date object or an array of Date objects.
3676 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3678 select : function(date) {
3680 var aToBeSelected = this._toFieldArray(date),
3683 cfgSelected = DEF_CFG.SELECTED.key;
3686 for (var a=0; a < aToBeSelected.length; ++a) {
3687 var toSelect = aToBeSelected[a];
3689 if (!this.isDateOOB(this._toDate(toSelect))) {
3691 if (validDates.length === 0) {
3692 this.beforeSelectEvent.fire();
3693 selected = this.cfg.getProperty(cfgSelected);
3695 validDates.push(toSelect);
3697 if (this._indexOfSelectedFieldArray(toSelect) == -1) {
3698 selected[selected.length] = toSelect;
3704 if (validDates.length > 0) {
3706 this.parent.cfg.setProperty(cfgSelected, selected);
3708 this.cfg.setProperty(cfgSelected, selected);
3710 this.selectEvent.fire(validDates);
3713 return this.getSelectedDates();
3717 * Selects a date on the current calendar by referencing the index of the cell that should be selected.
3718 * This method is used to easily select a single cell (usually with a mouse click) without having to do
3719 * a full render. The selected style is applied to the cell directly.
3721 * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month
3722 * or out of bounds cells), it will not be selected and in such a case beforeSelect and select events will not be fired.
3724 * @method selectCell
3725 * @param {Number} cellIndex The index of the cell to select in the current calendar.
3726 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3728 selectCell : function(cellIndex) {
3730 var cell = this.cells[cellIndex],
3731 cellDate = this.cellDates[cellIndex],
3732 dCellDate = this._toDate(cellDate),
3733 selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3738 this.beforeSelectEvent.fire();
3740 var cfgSelected = DEF_CFG.SELECTED.key;
3741 var selected = this.cfg.getProperty(cfgSelected);
3743 var selectDate = cellDate.concat();
3745 if (this._indexOfSelectedFieldArray(selectDate) == -1) {
3746 selected[selected.length] = selectDate;
3749 this.parent.cfg.setProperty(cfgSelected, selected);
3751 this.cfg.setProperty(cfgSelected, selected);
3753 this.renderCellStyleSelected(dCellDate,cell);
3754 this.selectEvent.fire([selectDate]);
3756 this.doCellMouseOut.call(cell, null, this);
3759 return this.getSelectedDates();
3763 * Deselects a date or a collection of dates on the current calendar. This method, by default,
3764 * does not call the render method explicitly. Once deselection has completed, render must be
3765 * called for the changes to be reflected visually.
3767 * The method will not attempt to deselect any dates which are OOB (out of bounds, and hence not selectable)
3768 * and the array of deselected dates passed to the deselectEvent will not contain any OOB dates.
3770 * If all dates are OOB, beforeDeselect and deselect events will not be fired.
3773 * @param {String/Date/Date[]} date The date string of dates to deselect 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 deselect : function(date) {
3781 var aToBeDeselected = this._toFieldArray(date),
3784 cfgSelected = DEF_CFG.SELECTED.key;
3787 for (var a=0; a < aToBeDeselected.length; ++a) {
3788 var toDeselect = aToBeDeselected[a];
3790 if (!this.isDateOOB(this._toDate(toDeselect))) {
3792 if (validDates.length === 0) {
3793 this.beforeDeselectEvent.fire();
3794 selected = this.cfg.getProperty(cfgSelected);
3797 validDates.push(toDeselect);
3799 var index = this._indexOfSelectedFieldArray(toDeselect);
3801 selected.splice(index,1);
3807 if (validDates.length > 0) {
3809 this.parent.cfg.setProperty(cfgSelected, selected);
3811 this.cfg.setProperty(cfgSelected, selected);
3813 this.deselectEvent.fire(validDates);
3816 return this.getSelectedDates();
3820 * Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
3821 * This method is used to easily deselect a single cell (usually with a mouse click) without having to do
3822 * a full render. The selected style is removed from the cell directly.
3824 * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month
3825 * or out of bounds cells), the method will not attempt to deselect it and in such a case, beforeDeselect and
3826 * deselect events will not be fired.
3828 * @method deselectCell
3829 * @param {Number} cellIndex The index of the cell to deselect in the current calendar.
3830 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3832 deselectCell : function(cellIndex) {
3833 var cell = this.cells[cellIndex],
3834 cellDate = this.cellDates[cellIndex],
3835 cellDateIndex = this._indexOfSelectedFieldArray(cellDate);
3837 var selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3841 this.beforeDeselectEvent.fire();
3843 var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key),
3844 dCellDate = this._toDate(cellDate),
3845 selectDate = cellDate.concat();
3847 if (cellDateIndex > -1) {
3848 if (this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth() == dCellDate.getMonth() &&
3849 this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getFullYear() == dCellDate.getFullYear()) {
3850 Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
3852 selected.splice(cellDateIndex, 1);
3856 this.parent.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
3858 this.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
3861 this.deselectEvent.fire([selectDate]);
3864 return this.getSelectedDates();
3868 * Deselects all dates on the current calendar.
3869 * @method deselectAll
3870 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3871 * Assuming that this function executes properly, the return value should be an empty array.
3872 * However, the empty array is returned for the sake of being able to check the selection status
3875 deselectAll : function() {
3876 this.beforeDeselectEvent.fire();
3878 var cfgSelected = DEF_CFG.SELECTED.key,
3879 selected = this.cfg.getProperty(cfgSelected),
3880 count = selected.length,
3881 sel = selected.concat();
3884 this.parent.cfg.setProperty(cfgSelected, []);
3886 this.cfg.setProperty(cfgSelected, []);
3890 this.deselectEvent.fire(sel);
3893 return this.getSelectedDates();
3896 // END SELECTION METHODS
3898 // BEGIN TYPE CONVERSION METHODS
3901 * Converts a date (either a JavaScript Date object, or a date string) to the internal data structure
3902 * used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]].
3903 * @method _toFieldArray
3905 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
3906 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3907 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3908 * This method can also take a JavaScript Date object or an array of Date objects.
3909 * @return {Array[](Number[])} Array of date field arrays
3911 _toFieldArray : function(date) {
3912 var returnDate = [];
3914 if (date instanceof Date) {
3915 returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]];
3916 } else if (Lang.isString(date)) {
3917 returnDate = this._parseDates(date);
3918 } else if (Lang.isArray(date)) {
3919 for (var i=0;i<date.length;++i) {
3921 returnDate[returnDate.length] = [d.getFullYear(),d.getMonth()+1,d.getDate()];
3929 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object. The date field array
3930 * is the format in which dates are as provided as arguments to selectEvent and deselectEvent listeners.
3933 * @param {Number[]} dateFieldArray The date field array to convert to a JavaScript Date.
3934 * @return {Date} JavaScript Date object representing the date field array.
3936 toDate : function(dateFieldArray) {
3937 return this._toDate(dateFieldArray);
3941 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
3944 * @deprecated Made public, toDate
3945 * @param {Number[]} dateFieldArray The date field array to convert to a JavaScript Date.
3946 * @return {Date} JavaScript Date object representing the date field array
3948 _toDate : function(dateFieldArray) {
3949 if (dateFieldArray instanceof Date) {
3950 return dateFieldArray;
3952 return DateMath.getDate(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]);
3956 // END TYPE CONVERSION METHODS
3958 // BEGIN UTILITY METHODS
3961 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
3962 * @method _fieldArraysAreEqual
3964 * @param {Number[]} array1 The first date field array to compare
3965 * @param {Number[]} array2 The first date field array to compare
3966 * @return {Boolean} The boolean that represents the equality of the two arrays
3968 _fieldArraysAreEqual : function(array1, array2) {
3971 if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2]) {
3979 * Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates.
3980 * @method _indexOfSelectedFieldArray
3982 * @param {Number[]} find The date field array to search for
3983 * @return {Number} The index of the date field array within the collection of selected dates.
3984 * -1 will be returned if the date is not found.
3986 _indexOfSelectedFieldArray : function(find) {
3988 seldates = this.cfg.getProperty(DEF_CFG.SELECTED.key);
3990 for (var s=0;s<seldates.length;++s) {
3991 var sArray = seldates[s];
3992 if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2]) {
4002 * Determines whether a given date is OOM (out of month).
4004 * @param {Date} date The JavaScript Date object for which to check the OOM status
4005 * @return {Boolean} true if the date is OOM
4007 isDateOOM : function(date) {
4008 return (date.getMonth() != this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth());
4012 * Determines whether a given date is OOB (out of bounds - less than the mindate or more than the maxdate).
4015 * @param {Date} date The JavaScript Date object for which to check the OOB status
4016 * @return {Boolean} true if the date is OOB
4018 isDateOOB : function(date) {
4019 var minDate = this.cfg.getProperty(DEF_CFG.MINDATE.key),
4020 maxDate = this.cfg.getProperty(DEF_CFG.MAXDATE.key),
4024 minDate = dm.clearTime(minDate);
4027 maxDate = dm.clearTime(maxDate);
4030 var clearedDate = new Date(date.getTime());
4031 clearedDate = dm.clearTime(clearedDate);
4033 return ((minDate && clearedDate.getTime() < minDate.getTime()) || (maxDate && clearedDate.getTime() > maxDate.getTime()));
4037 * Parses a pagedate configuration property value. The value can either be specified as a string of form "mm/yyyy" or a Date object
4038 * 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
4039 * @method _parsePageDate
4041 * @param {Date|String} date Pagedate value which needs to be parsed
4042 * @return {Date} The Date object representing the pagedate
4044 _parsePageDate : function(date) {
4048 if (date instanceof Date) {
4049 parsedDate = DateMath.findMonthStart(date);
4051 var month, year, aMonthYear;
4052 aMonthYear = date.split(this.cfg.getProperty(DEF_CFG.DATE_FIELD_DELIMITER.key));
4053 month = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_MONTH_POSITION.key)-1], 10)-1;
4054 year = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_YEAR_POSITION.key)-1], 10);
4056 parsedDate = DateMath.getDate(year, month, 1);
4059 parsedDate = DateMath.getDate(this.today.getFullYear(), this.today.getMonth(), 1);
4064 // END UTILITY METHODS
4066 // BEGIN EVENT HANDLERS
4069 * Event executed before a date is selected in the calendar widget.
4070 * @deprecated Event handlers for this event should be susbcribed to beforeSelectEvent.
4072 onBeforeSelect : function() {
4073 if (this.cfg.getProperty(DEF_CFG.MULTI_SELECT.key) === false) {
4075 this.parent.callChildFunction("clearAllBodyCellStyles", this.Style.CSS_CELL_SELECTED);
4076 this.parent.deselectAll();
4078 this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED);
4085 * Event executed when a date is selected in the calendar widget.
4086 * @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] ]
4087 * @deprecated Event handlers for this event should be susbcribed to selectEvent.
4089 onSelect : function(selected) { },
4092 * Event executed before a date is deselected in the calendar widget.
4093 * @deprecated Event handlers for this event should be susbcribed to beforeDeselectEvent.
4095 onBeforeDeselect : function() { },
4098 * Event executed when a date is deselected in the calendar widget.
4099 * @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] ]
4100 * @deprecated Event handlers for this event should be susbcribed to deselectEvent.
4102 onDeselect : function(deselected) { },
4105 * Event executed when the user navigates to a different calendar page.
4106 * @deprecated Event handlers for this event should be susbcribed to changePageEvent.
4108 onChangePage : function() {
4113 * Event executed when the calendar widget is rendered.
4114 * @deprecated Event handlers for this event should be susbcribed to renderEvent.
4116 onRender : function() { },
4119 * Event executed when the calendar widget is reset to its original state.
4120 * @deprecated Event handlers for this event should be susbcribed to resetEvemt.
4122 onReset : function() { this.render(); },
4125 * Event executed when the calendar widget is completely cleared to the current month with no selections.
4126 * @deprecated Event handlers for this event should be susbcribed to clearEvent.
4128 onClear : function() { this.render(); },
4131 * Validates the calendar widget. This method has no default implementation
4132 * and must be extended by subclassing the widget.
4133 * @return Should return true if the widget validates, and false if
4137 validate : function() { return true; },
4139 // END EVENT HANDLERS
4141 // BEGIN DATE PARSE METHODS
4144 * Converts a date string to a date field array
4146 * @param {String} sDate Date string. Valid formats are mm/dd and mm/dd/yyyy.
4147 * @return A date field array representing the string passed to the method
4148 * @type Array[](Number[])
4150 _parseDate : function(sDate) {
4151 var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER),
4154 if (aDate.length == 2) {
4155 rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
4156 rArray.type = Calendar.MONTH_DAY;
4158 rArray = [aDate[this.Locale.MDY_YEAR_POSITION-1],aDate[this.Locale.MDY_MONTH_POSITION-1],aDate[this.Locale.MDY_DAY_POSITION-1]];
4159 rArray.type = Calendar.DATE;
4162 for (var i=0;i<rArray.length;i++) {
4163 rArray[i] = parseInt(rArray[i], 10);
4170 * Converts a multi or single-date string to an array of date field arrays
4172 * @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
4173 * @return An array of date field arrays
4174 * @type Array[](Number[])
4176 _parseDates : function(sDates) {
4178 aDates = sDates.split(this.Locale.DATE_DELIMITER);
4180 for (var d=0;d<aDates.length;++d) {
4181 var sDate = aDates[d];
4183 if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) {
4185 var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER),
4186 dateStart = this._parseDate(aRange[0]),
4187 dateEnd = this._parseDate(aRange[1]),
4188 fullRange = this._parseRange(dateStart, dateEnd);
4190 aReturn = aReturn.concat(fullRange);
4192 // This is not a range
4193 var aDate = this._parseDate(sDate);
4194 aReturn.push(aDate);
4201 * Converts a date range to the full list of included dates
4203 * @param {Number[]} startDate Date field array representing the first date in the range
4204 * @param {Number[]} endDate Date field array representing the last date in the range
4205 * @return An array of date field arrays
4206 * @type Array[](Number[])
4208 _parseRange : function(startDate, endDate) {
4209 var dCurrent = DateMath.add(DateMath.getDate(startDate[0],startDate[1]-1,startDate[2]),DateMath.DAY,1),
4210 dEnd = DateMath.getDate(endDate[0], endDate[1]-1, endDate[2]),
4213 results.push(startDate);
4214 while (dCurrent.getTime() <= dEnd.getTime()) {
4215 results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
4216 dCurrent = DateMath.add(dCurrent,DateMath.DAY,1);
4221 // END DATE PARSE METHODS
4223 // BEGIN RENDERER METHODS
4226 * Resets the render stack of the current calendar to its original pre-render value.
4228 resetRenderers : function() {
4229 this.renderStack = this._renderStack.concat();
4233 * Removes all custom renderers added to the Calendar through the addRenderer, addMonthRenderer and
4234 * addWeekdayRenderer methods. Calendar's render method needs to be called after removing renderers
4235 * to re-render the Calendar without custom renderers applied.
4237 removeRenderers : function() {
4238 this._renderStack = [];
4239 this.renderStack = [];
4243 * Clears the inner HTML, CSS class and style information from the specified cell.
4244 * @method clearElement
4245 * @param {HTMLTableCellElement} cell The cell to clear
4247 clearElement : function(cell) {
4248 cell.innerHTML = " ";
4253 * Adds a renderer to the render stack. The function reference passed to this method will be executed
4254 * when a date cell matches the conditions specified in the date string for this renderer.
4255 * @method addRenderer
4256 * @param {String} sDates A date string to associate with the specified renderer. Valid formats
4257 * include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
4258 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4260 addRenderer : function(sDates, fnRender) {
4261 var aDates = this._parseDates(sDates);
4262 for (var i=0;i<aDates.length;++i) {
4263 var aDate = aDates[i];
4265 if (aDate.length == 2) { // this is either a range or a month/day combo
4266 if (aDate[0] instanceof Array) { // this is a range
4267 this._addRenderer(Calendar.RANGE,aDate,fnRender);
4268 } else { // this is a month/day combo
4269 this._addRenderer(Calendar.MONTH_DAY,aDate,fnRender);
4271 } else if (aDate.length == 3) {
4272 this._addRenderer(Calendar.DATE,aDate,fnRender);
4278 * The private method used for adding cell renderers to the local render stack.
4279 * This method is called by other methods that set the renderer type prior to the method call.
4280 * @method _addRenderer
4282 * @param {String} type The type string that indicates the type of date renderer being added.
4283 * Values are YAHOO.widget.Calendar.DATE, YAHOO.widget.Calendar.MONTH_DAY, YAHOO.widget.Calendar.WEEKDAY,
4284 * YAHOO.widget.Calendar.RANGE, YAHOO.widget.Calendar.MONTH
4285 * @param {Array} aDates An array of dates used to construct the renderer. The format varies based
4286 * on the renderer type
4287 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4289 _addRenderer : function(type, aDates, fnRender) {
4290 var add = [type,aDates,fnRender];
4291 this.renderStack.unshift(add);
4292 this._renderStack = this.renderStack.concat();
4296 * Adds a month to the render stack. The function reference passed to this method will be executed
4297 * when a date cell matches the month passed to this method.
4298 * @method addMonthRenderer
4299 * @param {Number} month The month (1-12) to associate with this renderer
4300 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4302 addMonthRenderer : function(month, fnRender) {
4303 this._addRenderer(Calendar.MONTH,[month],fnRender);
4307 * Adds a weekday to the render stack. The function reference passed to this method will be executed
4308 * when a date cell matches the weekday passed to this method.
4309 * @method addWeekdayRenderer
4310 * @param {Number} weekday The weekday (Sunday = 1, Monday = 2 ... Saturday = 7) to associate with this renderer
4311 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4313 addWeekdayRenderer : function(weekday, fnRender) {
4314 this._addRenderer(Calendar.WEEKDAY,[weekday],fnRender);
4317 // END RENDERER METHODS
4319 // BEGIN CSS METHODS
4322 * Removes all styles from all body cells in the current calendar table.
4323 * @method clearAllBodyCellStyles
4324 * @param {style} style The CSS class name to remove from all calendar body cells
4326 clearAllBodyCellStyles : function(style) {
4327 for (var c=0;c<this.cells.length;++c) {
4328 Dom.removeClass(this.cells[c],style);
4334 // BEGIN GETTER/SETTER METHODS
4336 * Sets the calendar's month explicitly
4338 * @param {Number} month The numeric month, from 0 (January) to 11 (December)
4340 setMonth : function(month) {
4341 var cfgPageDate = DEF_CFG.PAGEDATE.key,
4342 current = this.cfg.getProperty(cfgPageDate);
4343 current.setMonth(parseInt(month, 10));
4344 this.cfg.setProperty(cfgPageDate, current);
4348 * Sets the calendar's year explicitly.
4350 * @param {Number} year The numeric 4-digit year
4352 setYear : function(year) {
4353 var cfgPageDate = DEF_CFG.PAGEDATE.key,
4354 current = this.cfg.getProperty(cfgPageDate);
4356 current.setFullYear(parseInt(year, 10));
4357 this.cfg.setProperty(cfgPageDate, current);
4361 * Gets the list of currently selected dates from the calendar.
4362 * @method getSelectedDates
4363 * @return {Date[]} An array of currently selected JavaScript Date objects.
4365 getSelectedDates : function() {
4366 var returnDates = [],
4367 selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
4369 for (var d=0;d<selected.length;++d) {
4370 var dateArray = selected[d];
4372 var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
4373 returnDates.push(date);
4376 returnDates.sort( function(a,b) { return a-b; } );
4380 /// END GETTER/SETTER METHODS ///
4383 * Hides the Calendar's outer container from view.
4387 if (this.beforeHideEvent.fire()) {
4388 this.oDomContainer.style.display = "none";
4389 this.hideEvent.fire();
4394 * Shows the Calendar's outer container.
4398 if (this.beforeShowEvent.fire()) {
4399 this.oDomContainer.style.display = "block";
4400 this.showEvent.fire();
4405 * Returns a string representing the current browser.
4406 * @deprecated As of 2.3.0, environment information is available in YAHOO.env.ua
4411 browser : (function() {
4412 var ua = navigator.userAgent.toLowerCase();
4413 if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof)
4415 } else if (ua.indexOf('msie 7')!=-1) { // IE7
4417 } else if (ua.indexOf('msie') !=-1) { // IE
4419 } else if (ua.indexOf('safari')!=-1) { // Safari (check before Gecko because it includes "like Gecko")
4421 } else if (ua.indexOf('gecko') != -1) { // Gecko
4428 * Returns a string representation of the object.
4430 * @return {String} A string representation of the Calendar object.
4432 toString : function() {
4433 return "Calendar " + this.id;
4437 * Destroys the Calendar instance. The method will remove references
4438 * to HTML elements, remove any event listeners added by the Calendar,
4439 * and destroy the Config and CalendarNavigator instances it has created.
4443 destroy : function() {
4445 if (this.beforeDestroyEvent.fire()) {
4449 if (cal.navigator) {
4450 cal.navigator.destroy();
4457 // DOM event listeners
4458 Event.purgeElement(cal.oDomContainer, true);
4460 // Generated markup/DOM - Not removing the container DIV since we didn't create it.
4461 Dom.removeClass(cal.oDomContainer, "withtitle");
4462 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_CONTAINER);
4463 Dom.removeClass(cal.oDomContainer, cal.Style.CSS_SINGLE);
4464 cal.oDomContainer.innerHTML = "";
4466 // JS-to-DOM references
4467 cal.oDomContainer = null;
4470 this.destroyEvent.fire();
4475 YAHOO.widget.Calendar = Calendar;
4478 * @namespace YAHOO.widget
4479 * @class Calendar_Core
4480 * @extends YAHOO.widget.Calendar
4481 * @deprecated The old Calendar_Core class is no longer necessary.
4483 YAHOO.widget.Calendar_Core = YAHOO.widget.Calendar;
4485 YAHOO.widget.Cal_Core = YAHOO.widget.Calendar;
4491 var Dom = YAHOO.util.Dom,
4492 DateMath = YAHOO.widget.DateMath,
4493 Event = YAHOO.util.Event,
4495 Calendar = YAHOO.widget.Calendar;
4498 * YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar. This class facilitates
4499 * the ability to have multi-page calendar views that share a single dataset and are
4500 * dependent on each other.
4502 * The calendar group instance will refer to each of its elements using a 0-based index.
4503 * For example, to construct the placeholder for a calendar group widget with id "cal1" and
4504 * containerId of "cal1Container", the markup would be as follows:
4506 * <div id="cal1Container_0"></div>
4507 * <div id="cal1Container_1"></div>
4509 * The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers.
4512 * <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
4513 * The CalendarGroup can be constructed by simply providing a container ID string,
4514 * or a reference to a container DIV HTMLElement (the element needs to exist
4519 * var c = new YAHOO.widget.CalendarGroup("calContainer", configOptions);
4523 * var containerDiv = YAHOO.util.Dom.get("calContainer");
4524 * var c = new YAHOO.widget.CalendarGroup(containerDiv, configOptions);
4528 * If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
4529 * 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".
4532 * @namespace YAHOO.widget
4533 * @class CalendarGroup
4535 * @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.
4536 * @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.
4537 * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
4539 function CalendarGroup(id, containerId, config) {
4540 if (arguments.length > 0) {
4541 this.init.apply(this, arguments);
4546 * The set of default Config property keys and values for the CalendarGroup
4547 * @property YAHOO.widget.CalendarGroup._DEFAULT_CONFIG
4553 CalendarGroup._DEFAULT_CONFIG = Calendar._DEFAULT_CONFIG;
4554 CalendarGroup._DEFAULT_CONFIG.PAGES = {key:"pages", value:2};
4556 var DEF_CFG = CalendarGroup._DEFAULT_CONFIG;
4558 CalendarGroup.prototype = {
4561 * Initializes the calendar group. All subclasses must call this method in order for the
4562 * group to be initialized properly.
4564 * @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.
4565 * @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.
4566 * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
4568 init : function(id, container, config) {
4570 // Normalize 2.4.0, pre 2.4.0 args
4571 var nArgs = this._parseArgs(arguments);
4574 container = nArgs.container;
4575 config = nArgs.config;
4577 this.oDomContainer = Dom.get(container);
4579 if (!this.oDomContainer.id) {
4580 this.oDomContainer.id = Dom.generateId();
4583 id = this.oDomContainer.id + "_t";
4587 * The unique id associated with the CalendarGroup
4594 * The unique id associated with the CalendarGroup container
4595 * @property containerId
4598 this.containerId = this.oDomContainer.id;
4604 * The collection of Calendar pages contained within the CalendarGroup
4606 * @type YAHOO.widget.Calendar[]
4610 Dom.addClass(this.oDomContainer, CalendarGroup.CSS_CONTAINER);
4611 Dom.addClass(this.oDomContainer, CalendarGroup.CSS_MULTI_UP);
4614 * The Config object used to hold the configuration variables for the CalendarGroup
4616 * @type YAHOO.util.Config
4618 this.cfg = new YAHOO.util.Config(this);
4621 * The local object which contains the CalendarGroup's options
4628 * The local object which contains the CalendarGroup's locale settings
4637 this.cfg.applyConfig(config, true);
4640 this.cfg.fireQueue();
4642 // OPERA HACK FOR MISWRAPPED FLOATS
4643 if (YAHOO.env.ua.opera){
4644 this.renderEvent.subscribe(this._fixWidth, this, true);
4645 this.showEvent.subscribe(this._fixWidth, this, true);
4650 setupConfig : function() {
4655 * The number of pages to include in the CalendarGroup. This value can only be set once, in the CalendarGroup's constructor arguments.
4660 cfg.addProperty(DEF_CFG.PAGES.key, { value:DEF_CFG.PAGES.value, validator:cfg.checkNumber, handler:this.configPages } );
4663 * The month/year representing the current visible Calendar date (mm/yyyy)
4665 * @type String | Date
4666 * @default today's date
4668 cfg.addProperty(DEF_CFG.PAGEDATE.key, { value:new Date(), handler:this.configPageDate } );
4671 * The date or range of dates representing the current Calendar selection
4677 cfg.addProperty(DEF_CFG.SELECTED.key, { value:[], handler:this.configSelected } );
4680 * The title to display above the CalendarGroup's month header
4685 cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
4688 * Whether or not a close button should be displayed for this CalendarGroup
4693 cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
4696 * 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.
4697 * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be
4698 * enabled if required.
4702 * @default true for IE6 and below, false for all other browsers
4704 cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
4707 * The minimum selectable date in the current Calendar (mm/dd/yyyy)
4709 * @type String | Date
4712 cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.delegateConfig } );
4715 * The maximum selectable date in the current Calendar (mm/dd/yyyy)
4717 * @type String | Date
4720 cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.delegateConfig } );
4722 // Options properties
4725 * True if the Calendar should allow multiple selections. False by default.
4726 * @config MULTI_SELECT
4730 cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4733 * The weekday the week begins on. Default is 0 (Sunday).
4734 * @config START_WEEKDAY
4738 cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4741 * True if the Calendar should show weekday labels. True by default.
4742 * @config SHOW_WEEKDAYS
4746 cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4749 * True if the Calendar should show week row headers. False by default.
4750 * @config SHOW_WEEK_HEADER
4754 cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key,{ value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4757 * True if the Calendar should show week row footers. False by default.
4758 * @config SHOW_WEEK_FOOTER
4762 cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4765 * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
4766 * @config HIDE_BLANK_WEEKS
4770 cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key,{ value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4773 * The image that should be used for the left navigation arrow.
4774 * @config NAV_ARROW_LEFT
4776 * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
4779 cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.delegateConfig } );
4782 * The image that should be used for the right navigation arrow.
4783 * @config NAV_ARROW_RIGHT
4785 * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
4788 cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.delegateConfig } );
4790 // Locale properties
4793 * The short month labels for the current locale.
4794 * @config MONTHS_SHORT
4796 * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
4798 cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.delegateConfig } );
4801 * The long month labels for the current locale.
4802 * @config MONTHS_LONG
4804 * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
4806 cfg.addProperty(DEF_CFG.MONTHS_LONG.key, { value:DEF_CFG.MONTHS_LONG.value, handler:this.delegateConfig } );
4809 * The 1-character weekday labels for the current locale.
4810 * @config WEEKDAYS_1CHAR
4812 * @default ["S", "M", "T", "W", "T", "F", "S"]
4814 cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.delegateConfig } );
4817 * The short weekday labels for the current locale.
4818 * @config WEEKDAYS_SHORT
4820 * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
4822 cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.delegateConfig } );
4825 * The medium weekday labels for the current locale.
4826 * @config WEEKDAYS_MEDIUM
4828 * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
4830 cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.delegateConfig } );
4833 * The long weekday labels for the current locale.
4834 * @config WEEKDAYS_LONG
4836 * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
4838 cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.delegateConfig } );
4841 * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
4842 * @config LOCALE_MONTHS
4846 cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.delegateConfig } );
4849 * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
4850 * @config LOCALE_WEEKDAYS
4854 cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.delegateConfig } );
4857 * The value used to delimit individual dates in a date string passed to various Calendar functions.
4858 * @config DATE_DELIMITER
4862 cfg.addProperty(DEF_CFG.DATE_DELIMITER.key, { value:DEF_CFG.DATE_DELIMITER.value, handler:this.delegateConfig } );
4865 * The value used to delimit date fields in a date string passed to various Calendar functions.
4866 * @config DATE_FIELD_DELIMITER
4870 cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key,{ value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.delegateConfig } );
4873 * The value used to delimit date ranges in a date string passed to various Calendar functions.
4874 * @config DATE_RANGE_DELIMITER
4878 cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key,{ value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.delegateConfig } );
4881 * The position of the month in a month/year date string
4882 * @config MY_MONTH_POSITION
4886 cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4889 * The position of the year in a month/year date string
4890 * @config MY_YEAR_POSITION
4894 cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4897 * The position of the month in a month/day date string
4898 * @config MD_MONTH_POSITION
4902 cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4905 * The position of the day in a month/year date string
4906 * @config MD_DAY_POSITION
4910 cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key, { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4913 * The position of the month in a month/day/year date string
4914 * @config MDY_MONTH_POSITION
4918 cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4921 * The position of the day in a month/day/year date string
4922 * @config MDY_DAY_POSITION
4926 cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4929 * The position of the year in a month/day/year date string
4930 * @config MDY_YEAR_POSITION
4934 cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4937 * The position of the month in the month year label string used as the Calendar header
4938 * @config MY_LABEL_MONTH_POSITION
4942 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4945 * The position of the year in the month year label string used as the Calendar header
4946 * @config MY_LABEL_YEAR_POSITION
4950 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
4953 * The suffix used after the month when rendering the Calendar header
4954 * @config MY_LABEL_MONTH_SUFFIX
4958 cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.delegateConfig } );
4961 * The suffix used after the year when rendering the Calendar header
4962 * @config MY_LABEL_YEAR_SUFFIX
4966 cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.delegateConfig } );
4969 * Configuration for the Month Year Navigation UI. By default it is disabled
4974 cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
4977 * The map of UI strings which the CalendarGroup UI uses.
4981 * @default An object with the properties shown below:
4983 * <dt>previousMonth</dt><dd><em>String</em> : The string to use for the "Previous Month" navigation UI. Defaults to "Previous Month".</dd>
4984 * <dt>nextMonth</dt><dd><em>String</em> : The string to use for the "Next Month" navigation UI. Defaults to "Next Month".</dd>
4985 * <dt>close</dt><dd><em>String</em> : The string to use for the close button label. Defaults to "Close".</dd>
4988 cfg.addProperty(DEF_CFG.STRINGS.key, {
4989 value:DEF_CFG.STRINGS.value,
4990 handler:this.configStrings,
4991 validator: function(val) {
4992 return Lang.isObject(val);
4994 supercedes: DEF_CFG.STRINGS.supercedes
4999 * Initializes CalendarGroup's built-in CustomEvents
5000 * @method initEvents
5002 initEvents : function() {
5006 CE = YAHOO.util.CustomEvent;
5009 * Proxy subscriber to subscribe to the CalendarGroup's child Calendars' CustomEvents
5012 * @param {Function} fn The function to subscribe to this CustomEvent
5013 * @param {Object} obj The CustomEvent's scope object
5014 * @param {Boolean} bOverride Whether or not to apply scope correction
5016 var sub = function(fn, obj, bOverride) {
5017 for (var p=0;p<me.pages.length;++p) {
5018 var cal = me.pages[p];
5019 cal[this.type + strEvent].subscribe(fn, obj, bOverride);
5024 * Proxy unsubscriber to unsubscribe from the CalendarGroup's child Calendars' CustomEvents
5027 * @param {Function} fn The function to subscribe to this CustomEvent
5028 * @param {Object} obj The CustomEvent's scope object
5030 var unsub = function(fn, obj) {
5031 for (var p=0;p<me.pages.length;++p) {
5032 var cal = me.pages[p];
5033 cal[this.type + strEvent].unsubscribe(fn, obj);
5037 var defEvents = Calendar._EVENT_TYPES;
5040 * Fired before a date selection is made
5041 * @event beforeSelectEvent
5043 me.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
5044 me.beforeSelectEvent.subscribe = sub; me.beforeSelectEvent.unsubscribe = unsub;
5047 * Fired when a date selection is made
5048 * @event selectEvent
5049 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
5051 me.selectEvent = new CE(defEvents.SELECT);
5052 me.selectEvent.subscribe = sub; me.selectEvent.unsubscribe = unsub;
5055 * Fired before a date or set of dates is deselected
5056 * @event beforeDeselectEvent
5058 me.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
5059 me.beforeDeselectEvent.subscribe = sub; me.beforeDeselectEvent.unsubscribe = unsub;
5062 * Fired when a date or set of dates has been deselected
5063 * @event deselectEvent
5064 * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
5066 me.deselectEvent = new CE(defEvents.DESELECT);
5067 me.deselectEvent.subscribe = sub; me.deselectEvent.unsubscribe = unsub;
5070 * Fired when the Calendar page is changed
5071 * @event changePageEvent
5073 me.changePageEvent = new CE(defEvents.CHANGE_PAGE);
5074 me.changePageEvent.subscribe = sub; me.changePageEvent.unsubscribe = unsub;
5077 * Fired before the Calendar is rendered
5078 * @event beforeRenderEvent
5080 me.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
5081 me.beforeRenderEvent.subscribe = sub; me.beforeRenderEvent.unsubscribe = unsub;
5084 * Fired when the Calendar is rendered
5085 * @event renderEvent
5087 me.renderEvent = new CE(defEvents.RENDER);
5088 me.renderEvent.subscribe = sub; me.renderEvent.unsubscribe = unsub;
5091 * Fired when the Calendar is reset
5094 me.resetEvent = new CE(defEvents.RESET);
5095 me.resetEvent.subscribe = sub; me.resetEvent.unsubscribe = unsub;
5098 * Fired when the Calendar is cleared
5101 me.clearEvent = new CE(defEvents.CLEAR);
5102 me.clearEvent.subscribe = sub; me.clearEvent.unsubscribe = unsub;
5105 * Fired just before the CalendarGroup is to be shown
5106 * @event beforeShowEvent
5108 me.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
5111 * Fired after the CalendarGroup is shown
5114 me.showEvent = new CE(defEvents.SHOW);
5117 * Fired just before the CalendarGroup is to be hidden
5118 * @event beforeHideEvent
5120 me.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
5123 * Fired after the CalendarGroup is hidden
5126 me.hideEvent = new CE(defEvents.HIDE);
5129 * Fired just before the CalendarNavigator is to be shown
5130 * @event beforeShowNavEvent
5132 me.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
5135 * Fired after the CalendarNavigator is shown
5136 * @event showNavEvent
5138 me.showNavEvent = new CE(defEvents.SHOW_NAV);
5141 * Fired just before the CalendarNavigator is to be hidden
5142 * @event beforeHideNavEvent
5144 me.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
5147 * Fired after the CalendarNavigator is hidden
5148 * @event hideNavEvent
5150 me.hideNavEvent = new CE(defEvents.HIDE_NAV);
5153 * Fired just before the CalendarNavigator is to be rendered
5154 * @event beforeRenderNavEvent
5156 me.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
5159 * Fired after the CalendarNavigator is rendered
5160 * @event renderNavEvent
5162 me.renderNavEvent = new CE(defEvents.RENDER_NAV);
5165 * Fired just before the CalendarGroup is to be destroyed
5166 * @event beforeDestroyEvent
5168 me.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
5171 * Fired after the CalendarGroup is destroyed. This event should be used
5172 * for notification only. When this event is fired, important CalendarGroup instance
5173 * properties, dom references and event listeners have already been
5174 * removed/dereferenced, and hence the CalendarGroup instance is not in a usable
5177 * @event destroyEvent
5179 me.destroyEvent = new CE(defEvents.DESTROY);
5183 * The default Config handler for the "pages" property
5184 * @method configPages
5185 * @param {String} type The CustomEvent type (usually the property name)
5186 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5187 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5189 configPages : function(type, args, obj) {
5190 var pageCount = args[0],
5191 cfgPageDate = DEF_CFG.PAGEDATE.key,
5194 firstPageDate = null,
5195 groupCalClass = "groupcal",
5196 firstClass = "first-of-type",
5197 lastClass = "last-of-type";
5199 for (var p=0;p<pageCount;++p) {
5200 var calId = this.id + sep + p,
5201 calContainerId = this.containerId + sep + p,
5202 childConfig = this.cfg.getConfig();
5204 childConfig.close = false;
5205 childConfig.title = false;
5206 childConfig.navigator = null;
5209 caldate = new Date(firstPageDate);
5210 this._setMonthOnDate(caldate, caldate.getMonth() + p);
5211 childConfig.pageDate = caldate;
5214 var cal = this.constructChild(calId, calContainerId, childConfig);
5216 Dom.removeClass(cal.oDomContainer, this.Style.CSS_SINGLE);
5217 Dom.addClass(cal.oDomContainer, groupCalClass);
5220 firstPageDate = cal.cfg.getProperty(cfgPageDate);
5221 Dom.addClass(cal.oDomContainer, firstClass);
5224 if (p==(pageCount-1)) {
5225 Dom.addClass(cal.oDomContainer, lastClass);
5231 this.pages[this.pages.length] = cal;
5236 * The default Config handler for the "pagedate" property
5237 * @method configPageDate
5238 * @param {String} type The CustomEvent type (usually the property name)
5239 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5240 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5242 configPageDate : function(type, args, obj) {
5246 var cfgPageDate = DEF_CFG.PAGEDATE.key;
5248 for (var p=0;p<this.pages.length;++p) {
5249 var cal = this.pages[p];
5251 firstPageDate = cal._parsePageDate(val);
5252 cal.cfg.setProperty(cfgPageDate, firstPageDate);
5254 var pageDate = new Date(firstPageDate);
5255 this._setMonthOnDate(pageDate, pageDate.getMonth() + p);
5256 cal.cfg.setProperty(cfgPageDate, pageDate);
5262 * The default Config handler for the CalendarGroup "selected" property
5263 * @method configSelected
5264 * @param {String} type The CustomEvent type (usually the property name)
5265 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5266 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5268 configSelected : function(type, args, obj) {
5269 var cfgSelected = DEF_CFG.SELECTED.key;
5270 this.delegateConfig(type, args, obj);
5271 var selected = (this.pages.length > 0) ? this.pages[0].cfg.getProperty(cfgSelected) : [];
5272 this.cfg.setProperty(cfgSelected, selected, true);
5277 * Delegates a configuration property to the CustomEvents associated with the CalendarGroup's children
5278 * @method delegateConfig
5279 * @param {String} type The CustomEvent type (usually the property name)
5280 * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5281 * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5283 delegateConfig : function(type, args, obj) {
5287 for (var p=0;p<this.pages.length;p++) {
5288 cal = this.pages[p];
5289 cal.cfg.setProperty(type, val);
5294 * Adds a function to all child Calendars within this CalendarGroup.
5295 * @method setChildFunction
5296 * @param {String} fnName The name of the function
5297 * @param {Function} fn The function to apply to each Calendar page object
5299 setChildFunction : function(fnName, fn) {
5300 var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
5302 for (var p=0;p<pageCount;++p) {
5303 this.pages[p][fnName] = fn;
5308 * Calls a function within all child Calendars within this CalendarGroup.
5309 * @method callChildFunction
5310 * @param {String} fnName The name of the function
5311 * @param {Array} args The arguments to pass to the function
5313 callChildFunction : function(fnName, args) {
5314 var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
5316 for (var p=0;p<pageCount;++p) {
5317 var page = this.pages[p];
5319 var fn = page[fnName];
5320 fn.call(page, args);
5326 * Constructs a child calendar. This method can be overridden if a subclassed version of the default
5327 * calendar is to be used.
5328 * @method constructChild
5329 * @param {String} id The id of the table element that will represent the calendar widget
5330 * @param {String} containerId The id of the container div element that will wrap the calendar table
5331 * @param {Object} config The configuration object containing the Calendar's arguments
5332 * @return {YAHOO.widget.Calendar} The YAHOO.widget.Calendar instance that is constructed
5334 constructChild : function(id,containerId,config) {
5335 var container = document.getElementById(containerId);
5337 container = document.createElement("div");
5338 container.id = containerId;
5339 this.oDomContainer.appendChild(container);
5341 return new Calendar(id,containerId,config);
5345 * Sets the calendar group's month explicitly. This month will be set into the first
5346 * page of the multi-page calendar, and all other months will be iterated appropriately.
5348 * @param {Number} month The numeric month, from 0 (January) to 11 (December)
5350 setMonth : function(month) {
5351 month = parseInt(month, 10);
5354 var cfgPageDate = DEF_CFG.PAGEDATE.key;
5356 for (var p=0; p<this.pages.length; ++p) {
5357 var cal = this.pages[p];
5358 var pageDate = cal.cfg.getProperty(cfgPageDate);
5360 currYear = pageDate.getFullYear();
5362 pageDate.setFullYear(currYear);
5364 this._setMonthOnDate(pageDate, month+p);
5365 cal.cfg.setProperty(cfgPageDate, pageDate);
5370 * Sets the calendar group's year explicitly. This year will be set into the first
5371 * page of the multi-page calendar, and all other months will be iterated appropriately.
5373 * @param {Number} year The numeric 4-digit year
5375 setYear : function(year) {
5377 var cfgPageDate = DEF_CFG.PAGEDATE.key;
5379 year = parseInt(year, 10);
5380 for (var p=0;p<this.pages.length;++p) {
5381 var cal = this.pages[p];
5382 var pageDate = cal.cfg.getProperty(cfgPageDate);
5384 if ((pageDate.getMonth()+1) == 1 && p>0) {
5392 * Calls the render function of all child calendars within the group.
5395 render : function() {
5396 this.renderHeader();
5397 for (var p=0;p<this.pages.length;++p) {
5398 var cal = this.pages[p];
5401 this.renderFooter();
5405 * Selects a date or a collection of dates on the current calendar. This method, by default,
5406 * does not call the render method explicitly. Once selection has completed, render must be
5407 * called for the changes to be reflected visually.
5409 * @param {String/Date/Date[]} date The date string of dates to select in the current calendar. Valid formats are
5410 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
5411 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
5412 * This method can also take a JavaScript Date object or an array of Date objects.
5413 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5415 select : function(date) {
5416 for (var p=0;p<this.pages.length;++p) {
5417 var cal = this.pages[p];
5420 return this.getSelectedDates();
5424 * 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.
5425 * The value of the MULTI_SELECT Configuration attribute will determine the set of dates which get selected.
5427 * <li>If MULTI_SELECT is false, selectCell will select the cell at the specified index for only the last displayed Calendar page.</li>
5428 * <li>If MULTI_SELECT is true, selectCell will select the cell at the specified index, on each displayed Calendar page.</li>
5430 * @method selectCell
5431 * @param {Number} cellIndex The index of the cell to be selected.
5432 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5434 selectCell : function(cellIndex) {
5435 for (var p=0;p<this.pages.length;++p) {
5436 var cal = this.pages[p];
5437 cal.selectCell(cellIndex);
5439 return this.getSelectedDates();
5443 * Deselects a date or a collection of dates on the current calendar. This method, by default,
5444 * does not call the render method explicitly. Once deselection has completed, render must be
5445 * called for the changes to be reflected visually.
5447 * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
5448 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
5449 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
5450 * This method can also take a JavaScript Date object or an array of Date objects.
5451 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5453 deselect : function(date) {
5454 for (var p=0;p<this.pages.length;++p) {
5455 var cal = this.pages[p];
5458 return this.getSelectedDates();
5462 * Deselects all dates on the current calendar.
5463 * @method deselectAll
5464 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5465 * Assuming that this function executes properly, the return value should be an empty array.
5466 * However, the empty array is returned for the sake of being able to check the selection status
5469 deselectAll : function() {
5470 for (var p=0;p<this.pages.length;++p) {
5471 var cal = this.pages[p];
5474 return this.getSelectedDates();
5478 * 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.
5479 * deselectCell will deselect the cell at the specified index on each displayed Calendar page.
5481 * @method deselectCell
5482 * @param {Number} cellIndex The index of the cell to deselect.
5483 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5485 deselectCell : function(cellIndex) {
5486 for (var p=0;p<this.pages.length;++p) {
5487 var cal = this.pages[p];
5488 cal.deselectCell(cellIndex);
5490 return this.getSelectedDates();
5494 * Resets the calendar widget to the originally selected month and year, and
5495 * sets the calendar to the initial selection(s).
5498 reset : function() {
5499 for (var p=0;p<this.pages.length;++p) {
5500 var cal = this.pages[p];
5506 * Clears the selected dates in the current calendar widget and sets the calendar
5507 * to the current month and year.
5510 clear : function() {
5511 for (var p=0;p<this.pages.length;++p) {
5512 var cal = this.pages[p];
5516 this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
5517 this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.pages[0].today.getTime()));
5522 * Navigates to the next month page in the calendar widget.
5525 nextMonth : function() {
5526 for (var p=0;p<this.pages.length;++p) {
5527 var cal = this.pages[p];
5533 * Navigates to the previous month page in the calendar widget.
5534 * @method previousMonth
5536 previousMonth : function() {
5537 for (var p=this.pages.length-1;p>=0;--p) {
5538 var cal = this.pages[p];
5539 cal.previousMonth();
5544 * Navigates to the next year in the currently selected month in the calendar widget.
5547 nextYear : function() {
5548 for (var p=0;p<this.pages.length;++p) {
5549 var cal = this.pages[p];
5555 * Navigates to the previous year in the currently selected month in the calendar widget.
5556 * @method previousYear
5558 previousYear : function() {
5559 for (var p=0;p<this.pages.length;++p) {
5560 var cal = this.pages[p];
5566 * Gets the list of currently selected dates from the calendar.
5567 * @return An array of currently selected JavaScript Date objects.
5570 getSelectedDates : function() {
5571 var returnDates = [];
5572 var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
5573 for (var d=0;d<selected.length;++d) {
5574 var dateArray = selected[d];
5576 var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
5577 returnDates.push(date);
5580 returnDates.sort( function(a,b) { return a-b; } );
5585 * Adds a renderer to the render stack. The function reference passed to this method will be executed
5586 * when a date cell matches the conditions specified in the date string for this renderer.
5587 * @method addRenderer
5588 * @param {String} sDates A date string to associate with the specified renderer. Valid formats
5589 * include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
5590 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5592 addRenderer : function(sDates, fnRender) {
5593 for (var p=0;p<this.pages.length;++p) {
5594 var cal = this.pages[p];
5595 cal.addRenderer(sDates, fnRender);
5600 * Adds a month to the render stack. The function reference passed to this method will be executed
5601 * when a date cell matches the month passed to this method.
5602 * @method addMonthRenderer
5603 * @param {Number} month The month (1-12) to associate with this renderer
5604 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5606 addMonthRenderer : function(month, fnRender) {
5607 for (var p=0;p<this.pages.length;++p) {
5608 var cal = this.pages[p];
5609 cal.addMonthRenderer(month, fnRender);
5614 * Adds a weekday to the render stack. The function reference passed to this method will be executed
5615 * when a date cell matches the weekday passed to this method.
5616 * @method addWeekdayRenderer
5617 * @param {Number} weekday The weekday (1-7) to associate with this renderer. 1=Sunday, 2=Monday etc.
5618 * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5620 addWeekdayRenderer : function(weekday, fnRender) {
5621 for (var p=0;p<this.pages.length;++p) {
5622 var cal = this.pages[p];
5623 cal.addWeekdayRenderer(weekday, fnRender);
5628 * Removes all custom renderers added to the CalendarGroup through the addRenderer, addMonthRenderer and
5629 * addWeekRenderer methods. CalendarGroup's render method needs to be called to after removing renderers
5630 * to see the changes applied.
5632 * @method removeRenderers
5634 removeRenderers : function() {
5635 this.callChildFunction("removeRenderers");
5639 * Renders the header for the CalendarGroup.
5640 * @method renderHeader
5642 renderHeader : function() {
5643 // EMPTY DEFAULT IMPL
5647 * Renders a footer for the 2-up calendar container. By default, this method is
5649 * @method renderFooter
5651 renderFooter : function() {
5652 // EMPTY DEFAULT IMPL
5656 * Adds the designated number of months to the current calendar month, and sets the current
5657 * calendar page date to the new month.
5659 * @param {Number} count The number of months to add to the current calendar
5661 addMonths : function(count) {
5662 this.callChildFunction("addMonths", count);
5666 * Subtracts the designated number of months from the current calendar month, and sets the current
5667 * calendar page date to the new month.
5668 * @method subtractMonths
5669 * @param {Number} count The number of months to subtract from the current calendar
5671 subtractMonths : function(count) {
5672 this.callChildFunction("subtractMonths", count);
5676 * Adds the designated number of years to the current calendar, and sets the current
5677 * calendar page date to the new month.
5679 * @param {Number} count The number of years to add to the current calendar
5681 addYears : function(count) {
5682 this.callChildFunction("addYears", count);
5686 * Subtcats the designated number of years from the current calendar, and sets the current
5687 * calendar page date to the new month.
5688 * @method subtractYears
5689 * @param {Number} count The number of years to subtract from the current calendar
5691 subtractYears : function(count) {
5692 this.callChildFunction("subtractYears", count);
5696 * Returns the Calendar page instance which has a pagedate (month/year) matching the given date.
5697 * Returns null if no match is found.
5699 * @method getCalendarPage
5700 * @param {Date} date The JavaScript Date object for which a Calendar page is to be found.
5701 * @return {Calendar} The Calendar page instance representing the month to which the date
5704 getCalendarPage : function(date) {
5707 var y = date.getFullYear(),
5708 m = date.getMonth();
5710 var pages = this.pages;
5711 for (var i = 0; i < pages.length; ++i) {
5712 var pageDate = pages[i].cfg.getProperty("pagedate");
5713 if (pageDate.getFullYear() === y && pageDate.getMonth() === m) {
5723 * Sets the month on a Date object, taking into account year rollover if the month is less than 0 or greater than 11.
5724 * 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
5725 * @method _setMonthOnDate
5727 * @param {Date} date The Date object on which to set the month index
5728 * @param {Number} iMonth The month index to set
5730 _setMonthOnDate : function(date, iMonth) {
5731 // Bug in Safari 1.3, 2.0 (WebKit build < 420), Date.setMonth does not work consistently if iMonth is not 0-11
5732 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420 && (iMonth < 0 || iMonth > 11)) {
5733 var newDate = DateMath.add(date, DateMath.MONTH, iMonth-date.getMonth());
5734 date.setTime(newDate.getTime());
5736 date.setMonth(iMonth);
5741 * Fixes the width of the CalendarGroup container element, to account for miswrapped floats
5745 _fixWidth : function() {
5747 for (var p=0;p<this.pages.length;++p) {
5748 var cal = this.pages[p];
5749 w += cal.oDomContainer.offsetWidth;
5752 this.oDomContainer.style.width = w + "px";
5757 * Returns a string representation of the object.
5759 * @return {String} A string representation of the CalendarGroup object.
5761 toString : function() {
5762 return "CalendarGroup " + this.id;
5766 * Destroys the CalendarGroup instance. The method will remove references
5767 * to HTML elements, remove any event listeners added by the CalendarGroup.
5769 * It will also destroy the Config and CalendarNavigator instances created by the
5770 * CalendarGroup and the individual Calendar instances created for each page.
5774 destroy : function() {
5776 if (this.beforeDestroyEvent.fire()) {
5781 if (cal.navigator) {
5782 cal.navigator.destroy();
5789 // DOM event listeners
5790 Event.purgeElement(cal.oDomContainer, true);
5792 // Generated markup/DOM - Not removing the container DIV since we didn't create it.
5793 Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_CONTAINER);
5794 Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_MULTI_UP);
5796 for (var i = 0, l = cal.pages.length; i < l; i++) {
5797 cal.pages[i].destroy();
5798 cal.pages[i] = null;
5801 cal.oDomContainer.innerHTML = "";
5803 // JS-to-DOM references
5804 cal.oDomContainer = null;
5806 this.destroyEvent.fire();
5812 * CSS class representing the container for the calendar
5813 * @property YAHOO.widget.CalendarGroup.CSS_CONTAINER
5818 CalendarGroup.CSS_CONTAINER = "yui-calcontainer";
5821 * CSS class representing the container for the calendar
5822 * @property YAHOO.widget.CalendarGroup.CSS_MULTI_UP
5827 CalendarGroup.CSS_MULTI_UP = "multi";
5830 * CSS class representing the title for the 2-up calendar
5831 * @property YAHOO.widget.CalendarGroup.CSS_2UPTITLE
5836 CalendarGroup.CSS_2UPTITLE = "title";
5839 * CSS class representing the close icon for the 2-up calendar
5840 * @property YAHOO.widget.CalendarGroup.CSS_2UPCLOSE
5843 * @deprecated Along with Calendar.IMG_ROOT and NAV_ARROW_LEFT, NAV_ARROW_RIGHT configuration properties.
5844 * 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
5847 CalendarGroup.CSS_2UPCLOSE = "close-icon";
5849 YAHOO.lang.augmentProto(CalendarGroup, Calendar, "buildDayLabel",
5851 "renderOutOfBoundsDate",
5854 "renderCellDefault",
5856 "renderCellStyleHighlight1",
5857 "renderCellStyleHighlight2",
5858 "renderCellStyleHighlight3",
5859 "renderCellStyleHighlight4",
5860 "renderCellStyleToday",
5861 "renderCellStyleSelected",
5862 "renderCellNotThisMonth",
5863 "renderBodyCellRestricted",
5871 "createCloseButton",
5873 "removeCloseButton",
5881 YAHOO.widget.CalGrp = CalendarGroup;
5882 YAHOO.widget.CalendarGroup = CalendarGroup;
5885 * @class YAHOO.widget.Calendar2up
5886 * @extends YAHOO.widget.CalendarGroup
5887 * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
5889 YAHOO.widget.Calendar2up = function(id, containerId, config) {
5890 this.init(id, containerId, config);
5893 YAHOO.extend(YAHOO.widget.Calendar2up, CalendarGroup);
5896 * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
5898 YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up;
5903 * The CalendarNavigator is used along with a Calendar/CalendarGroup to
5904 * provide a Month/Year popup navigation control, allowing the user to navigate
5905 * to a specific month/year in the Calendar/CalendarGroup without having to
5906 * scroll through months sequentially
5908 * @namespace YAHOO.widget
5909 * @class CalendarNavigator
5911 * @param {Calendar|CalendarGroup} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached.
5913 YAHOO.widget.CalendarNavigator = function(cal) {
5918 // Setup static properties (inside anon fn, so that we can use shortcuts)
5919 var CN = YAHOO.widget.CalendarNavigator;
5922 * YAHOO.widget.CalendarNavigator.CLASSES contains constants
5923 * for the class values applied to the CalendarNaviatgator's
5925 * @property YAHOO.widget.CalendarNavigator.CLASSES
5931 * Class applied to the Calendar Navigator's bounding box
5932 * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV
5938 * Class applied to the Calendar/CalendarGroup's bounding box to indicate
5939 * the Navigator is currently visible
5940 * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV_VISIBLE
5944 NAV_VISIBLE: "yui-cal-nav-visible",
5946 * Class applied to the Navigator mask's bounding box
5947 * @property YAHOO.widget.CalendarNavigator.CLASSES.MASK
5951 MASK : "yui-cal-nav-mask",
5953 * Class applied to the year label/control bounding box
5954 * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR
5958 YEAR : "yui-cal-nav-y",
5960 * Class applied to the month label/control bounding box
5961 * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH
5965 MONTH : "yui-cal-nav-m",
5967 * Class applied to the submit/cancel button's bounding box
5968 * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTONS
5972 BUTTONS : "yui-cal-nav-b",
5974 * Class applied to buttons wrapping element
5975 * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTON
5979 BUTTON : "yui-cal-nav-btn",
5981 * Class applied to the validation error area's bounding box
5982 * @property YAHOO.widget.CalendarNavigator.CLASSES.ERROR
5986 ERROR : "yui-cal-nav-e",
5988 * Class applied to the year input control
5989 * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR_CTRL
5993 YEAR_CTRL : "yui-cal-nav-yc",
5995 * Class applied to the month input control
5996 * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH_CTRL
6000 MONTH_CTRL : "yui-cal-nav-mc",
6002 * Class applied to controls with invalid data (e.g. a year input field with invalid an year)
6003 * @property YAHOO.widget.CalendarNavigator.CLASSES.INVALID
6007 INVALID : "yui-invalid",
6009 * Class applied to default controls
6010 * @property YAHOO.widget.CalendarNavigator.CLASSES.DEFAULT
6014 DEFAULT : "yui-default"
6018 * Object literal containing the default configuration values for the CalendarNavigator
6019 * The configuration object is expected to follow the format below, with the properties being
6023 * <dd><em>Object</em> : An object with the properties shown below, defining the string labels to use in the Navigator's UI
6025 * <dt>month</dt><dd><em>String</em> : The string to use for the month label. Defaults to "Month".</dd>
6026 * <dt>year</dt><dd><em>String</em> : The string to use for the year label. Defaults to "Year".</dd>
6027 * <dt>submit</dt><dd><em>String</em> : The string to use for the submit button label. Defaults to "Okay".</dd>
6028 * <dt>cancel</dt><dd><em>String</em> : The string to use for the cancel button label. Defaults to "Cancel".</dd>
6029 * <dt>invalidYear</dt><dd><em>String</em> : The string to use for invalid year values. Defaults to "Year needs to be a number".</dd>
6032 * <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>
6033 * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
6035 * @property _DEFAULT_CFG
6046 invalidYear : "Year needs to be a number"
6048 monthFormat: YAHOO.widget.Calendar.LONG,
6049 initialFocus: "year"
6053 * The suffix added to the Calendar/CalendarGroup's ID, to generate
6054 * a unique ID for the Navigator and it's bounding box.
6055 * @property YAHOO.widget.CalendarNavigator.ID_SUFFIX
6060 CN.ID_SUFFIX = "_nav";
6062 * The suffix added to the Navigator's ID, to generate
6063 * a unique ID for the month control.
6064 * @property YAHOO.widget.CalendarNavigator.MONTH_SUFFIX
6069 CN.MONTH_SUFFIX = "_month";
6071 * The suffix added to the Navigator's ID, to generate
6072 * a unique ID for the year control.
6073 * @property YAHOO.widget.CalendarNavigator.YEAR_SUFFIX
6078 CN.YEAR_SUFFIX = "_year";
6080 * The suffix added to the Navigator's ID, to generate
6081 * a unique ID for the error bounding box.
6082 * @property YAHOO.widget.CalendarNavigator.ERROR_SUFFIX
6087 CN.ERROR_SUFFIX = "_error";
6089 * The suffix added to the Navigator's ID, to generate
6090 * a unique ID for the "Cancel" button.
6091 * @property YAHOO.widget.CalendarNavigator.CANCEL_SUFFIX
6096 CN.CANCEL_SUFFIX = "_cancel";
6098 * The suffix added to the Navigator's ID, to generate
6099 * a unique ID for the "Submit" button.
6100 * @property YAHOO.widget.CalendarNavigator.SUBMIT_SUFFIX
6105 CN.SUBMIT_SUFFIX = "_submit";
6108 * The number of digits to which the year input control is to be limited.
6109 * @property YAHOO.widget.CalendarNavigator.YR_MAX_DIGITS
6113 CN.YR_MAX_DIGITS = 4;
6116 * The amount by which to increment the current year value,
6117 * when the arrow up/down key is pressed on the year control
6118 * @property YAHOO.widget.CalendarNavigator.YR_MINOR_INC
6122 CN.YR_MINOR_INC = 1;
6125 * The amount by which to increment the current year value,
6126 * when the page up/down key is pressed on the year control
6127 * @property YAHOO.widget.CalendarNavigator.YR_MAJOR_INC
6131 CN.YR_MAJOR_INC = 10;
6134 * Artificial delay (in ms) between the time the Navigator is hidden
6135 * and the Calendar/CalendarGroup state is updated. Allows the user
6136 * the see the Calendar/CalendarGroup page changing. If set to 0
6137 * the Calendar/CalendarGroup page will be updated instantly
6138 * @property YAHOO.widget.CalendarNavigator.UPDATE_DELAY
6142 CN.UPDATE_DELAY = 50;
6145 * Regular expression used to validate the year input
6146 * @property YAHOO.widget.CalendarNavigator.YR_PATTERN
6150 CN.YR_PATTERN = /^\d+$/;
6152 * Regular expression used to trim strings
6153 * @property YAHOO.widget.CalendarNavigator.TRIM
6157 CN.TRIM = /^\s*(.*?)\s*$/;
6160 YAHOO.widget.CalendarNavigator.prototype = {
6163 * The unique ID for this CalendarNavigator instance
6170 * The Calendar/CalendarGroup instance to which the navigator belongs
6172 * @type {Calendar|CalendarGroup}
6177 * Reference to the HTMLElement used to render the navigator's bounding box
6184 * Reference to the HTMLElement used to render the navigator's mask
6191 * Reference to the HTMLElement used to input the year
6198 * Reference to the HTMLElement used to input the month
6205 * Reference to the HTMLElement used to display validation errors
6212 * Reference to the HTMLElement used to update the Calendar/Calendar group
6213 * with the month/year values
6214 * @property submitEl
6220 * Reference to the HTMLElement used to hide the navigator without updating the
6221 * Calendar/Calendar group
6222 * @property cancelEl
6228 * Reference to the first focusable control in the navigator (by default monthEl)
6229 * @property firstCtrl
6235 * Reference to the last focusable control in the navigator (by default cancelEl)
6236 * @property lastCtrl
6242 * The document containing the Calendar/Calendar group instance
6245 * @type HTMLDocument
6250 * Internal state property for the current year displayed in the navigator
6258 * Internal state property for the current month index displayed in the navigator
6266 * Private internal state property which indicates whether or not the
6267 * Navigator has been rendered.
6269 * @property __rendered
6275 * Init lifecycle method called as part of construction
6278 * @param {Calendar} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached
6280 init : function(cal) {
6281 var calBox = cal.oDomContainer;
6284 this.id = calBox.id + YAHOO.widget.CalendarNavigator.ID_SUFFIX;
6285 this._doc = calBox.ownerDocument;
6288 * Private flag, to identify IE Quirks
6290 * @property __isIEQuirks
6292 var ie = YAHOO.env.ua.ie;
6293 this.__isIEQuirks = (ie && ((ie <= 6) || (this._doc.compatMode == "BackCompat")));
6297 * Displays the navigator and mask, updating the input controls to reflect the
6298 * currently set month and year. The show method will invoke the render method
6299 * if the navigator has not been renderered already, allowing for lazy rendering
6302 * The show method will fire the Calendar/CalendarGroup's beforeShowNav and showNav events
6307 var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
6309 if (this.cal.beforeShowNavEvent.fire()) {
6310 if (!this.__rendered) {
6315 this._updateMonthUI();
6316 this._updateYearUI();
6317 this._show(this.navEl, true);
6319 this.setInitialFocus();
6322 YAHOO.util.Dom.addClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
6323 this.cal.showNavEvent.fire();
6328 * Hides the navigator and mask
6330 * The show method will fire the Calendar/CalendarGroup's beforeHideNav event and hideNav events
6334 var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
6336 if (this.cal.beforeHideNavEvent.fire()) {
6337 this._show(this.navEl, false);
6339 YAHOO.util.Dom.removeClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
6340 this.cal.hideNavEvent.fire();
6346 * Displays the navigator's mask element
6350 showMask : function() {
6351 this._show(this.maskEl, true);
6352 if (this.__isIEQuirks) {
6358 * Hides the navigator's mask element
6362 hideMask : function() {
6363 this._show(this.maskEl, false);
6367 * Returns the current month set on the navigator
6369 * Note: This may not be the month set in the UI, if
6370 * the UI contains an invalid value.
6373 * @return {Number} The Navigator's current month index
6375 getMonth: function() {
6380 * Returns the current year set on the navigator
6382 * Note: This may not be the year set in the UI, if
6383 * the UI contains an invalid value.
6386 * @return {Number} The Navigator's current year value
6388 getYear: function() {
6393 * Sets the current month on the Navigator, and updates the UI
6396 * @param {Number} nMonth The month index, from 0 (Jan) through 11 (Dec).
6398 setMonth : function(nMonth) {
6399 if (nMonth >= 0 && nMonth < 12) {
6400 this._month = nMonth;
6402 this._updateMonthUI();
6406 * Sets the current year on the Navigator, and updates the UI. If the
6407 * provided year is invalid, it will not be set.
6410 * @param {Number} nYear The full year value to set the Navigator to.
6412 setYear : function(nYear) {
6413 var yrPattern = YAHOO.widget.CalendarNavigator.YR_PATTERN;
6414 if (YAHOO.lang.isNumber(nYear) && yrPattern.test(nYear+"")) {
6417 this._updateYearUI();
6421 * Renders the HTML for the navigator, adding it to the
6422 * document and attaches event listeners if it has not
6423 * already been rendered.
6427 render: function() {
6428 this.cal.beforeRenderNavEvent.fire();
6429 if (!this.__rendered) {
6432 this.applyListeners();
6433 this.__rendered = true;
6435 this.cal.renderNavEvent.fire();
6439 * Creates the navigator's containing HTMLElement, it's contents, and appends
6440 * the containg element to the Calendar/CalendarGroup's container.
6444 createNav : function() {
6445 var NAV = YAHOO.widget.CalendarNavigator;
6446 var doc = this._doc;
6448 var d = doc.createElement("div");
6449 d.className = NAV.CLASSES.NAV;
6451 var htmlBuf = this.renderNavContents([]);
6453 d.innerHTML = htmlBuf.join('');
6454 this.cal.oDomContainer.appendChild(d);
6458 this.yearEl = doc.getElementById(this.id + NAV.YEAR_SUFFIX);
6459 this.monthEl = doc.getElementById(this.id + NAV.MONTH_SUFFIX);
6460 this.errorEl = doc.getElementById(this.id + NAV.ERROR_SUFFIX);
6461 this.submitEl = doc.getElementById(this.id + NAV.SUBMIT_SUFFIX);
6462 this.cancelEl = doc.getElementById(this.id + NAV.CANCEL_SUFFIX);
6464 if (YAHOO.env.ua.gecko && this.yearEl && this.yearEl.type == "text") {
6465 // Avoid XUL error on focus, select [ https://bugzilla.mozilla.org/show_bug.cgi?id=236791,
6466 // supposedly fixed in 1.8.1, but there are reports of it still being around for methods other than blur ]
6467 this.yearEl.setAttribute("autocomplete", "off");
6470 this._setFirstLastElements();
6474 * Creates the Mask HTMLElement and appends it to the Calendar/CalendarGroups
6477 * @method createMask
6479 createMask : function() {
6480 var C = YAHOO.widget.CalendarNavigator.CLASSES;
6482 var d = this._doc.createElement("div");
6483 d.className = C.MASK;
6485 this.cal.oDomContainer.appendChild(d);
6490 * Used to set the width/height of the mask in pixels to match the Calendar Container.
6491 * Currently only used for IE6 or IE in quirks mode. The other A-Grade browser are handled using CSS (width/height 100%).
6493 * The method is also registered as an HTMLElement resize listener on the Calendars container element.
6498 _syncMask : function() {
6499 var c = this.cal.oDomContainer;
6500 if (c && this.maskEl) {
6501 var r = YAHOO.util.Dom.getRegion(c);
6502 YAHOO.util.Dom.setStyle(this.maskEl, "width", r.right - r.left + "px");
6503 YAHOO.util.Dom.setStyle(this.maskEl, "height", r.bottom - r.top + "px");
6508 * Renders the contents of the navigator
6510 * @method renderNavContents
6512 * @param {Array} html The HTML buffer to append the HTML to.
6513 * @return {Array} A reference to the buffer passed in.
6515 renderNavContents : function(html) {
6516 var NAV = YAHOO.widget.CalendarNavigator,
6518 h = html; // just to use a shorter name
6520 h[h.length] = '<div class="' + C.MONTH + '">';
6521 this.renderMonth(h);
6522 h[h.length] = '</div>';
6523 h[h.length] = '<div class="' + C.YEAR + '">';
6525 h[h.length] = '</div>';
6526 h[h.length] = '<div class="' + C.BUTTONS + '">';
6527 this.renderButtons(h);
6528 h[h.length] = '</div>';
6529 h[h.length] = '<div class="' + C.ERROR + '" id="' + this.id + NAV.ERROR_SUFFIX + '"></div>';
6535 * Renders the month label and control for the navigator
6537 * @method renderNavContents
6538 * @param {Array} html The HTML buffer to append the HTML to.
6539 * @return {Array} A reference to the buffer passed in.
6541 renderMonth : function(html) {
6542 var NAV = YAHOO.widget.CalendarNavigator,
6545 var id = this.id + NAV.MONTH_SUFFIX,
6546 mf = this.__getCfg("monthFormat"),
6547 months = this.cal.cfg.getProperty((mf == YAHOO.widget.Calendar.SHORT) ? "MONTHS_SHORT" : "MONTHS_LONG"),
6550 if (months && months.length > 0) {
6551 h[h.length] = '<label for="' + id + '">';
6552 h[h.length] = this.__getCfg("month", true);
6553 h[h.length] = '</label>';
6554 h[h.length] = '<select name="' + id + '" id="' + id + '" class="' + C.MONTH_CTRL + '">';
6555 for (var i = 0; i < months.length; i++) {
6556 h[h.length] = '<option value="' + i + '">';
6557 h[h.length] = months[i];
6558 h[h.length] = '</option>';
6560 h[h.length] = '</select>';
6566 * Renders the year label and control for the navigator
6568 * @method renderYear
6569 * @param {Array} html The HTML buffer to append the HTML to.
6570 * @return {Array} A reference to the buffer passed in.
6572 renderYear : function(html) {
6573 var NAV = YAHOO.widget.CalendarNavigator,
6576 var id = this.id + NAV.YEAR_SUFFIX,
6577 size = NAV.YR_MAX_DIGITS,
6580 h[h.length] = '<label for="' + id + '">';
6581 h[h.length] = this.__getCfg("year", true);
6582 h[h.length] = '</label>';
6583 h[h.length] = '<input type="text" name="' + id + '" id="' + id + '" class="' + C.YEAR_CTRL + '" maxlength="' + size + '"/>';
6588 * Renders the submit/cancel buttons for the navigator
6590 * @method renderButton
6591 * @return {String} The HTML created for the Button UI
6593 renderButtons : function(html) {
6594 var C = YAHOO.widget.CalendarNavigator.CLASSES;
6597 h[h.length] = '<span class="' + C.BUTTON + ' ' + C.DEFAULT + '">';
6598 h[h.length] = '<button type="button" id="' + this.id + '_submit' + '">';
6599 h[h.length] = this.__getCfg("submit", true);
6600 h[h.length] = '</button>';
6601 h[h.length] = '</span>';
6602 h[h.length] = '<span class="' + C.BUTTON +'">';
6603 h[h.length] = '<button type="button" id="' + this.id + '_cancel' + '">';
6604 h[h.length] = this.__getCfg("cancel", true);
6605 h[h.length] = '</button>';
6606 h[h.length] = '</span>';
6612 * Attaches DOM event listeners to the rendered elements
6614 * The method will call applyKeyListeners, to setup keyboard specific
6617 * @method applyListeners
6619 applyListeners : function() {
6620 var E = YAHOO.util.Event;
6622 function yearUpdateHandler() {
6623 if (this.validate()) {
6624 this.setYear(this._getYearFromUI());
6628 function monthUpdateHandler() {
6629 this.setMonth(this._getMonthFromUI());
6632 E.on(this.submitEl, "click", this.submit, this, true);
6633 E.on(this.cancelEl, "click", this.cancel, this, true);
6634 E.on(this.yearEl, "blur", yearUpdateHandler, this, true);
6635 E.on(this.monthEl, "change", monthUpdateHandler, this, true);
6637 if (this.__isIEQuirks) {
6638 YAHOO.util.Event.on(this.cal.oDomContainer, "resize", this._syncMask, this, true);
6641 this.applyKeyListeners();
6645 * Removes/purges DOM event listeners from the rendered elements
6647 * @method purgeListeners
6649 purgeListeners : function() {
6650 var E = YAHOO.util.Event;
6651 E.removeListener(this.submitEl, "click", this.submit);
6652 E.removeListener(this.cancelEl, "click", this.cancel);
6653 E.removeListener(this.yearEl, "blur");
6654 E.removeListener(this.monthEl, "change");
6655 if (this.__isIEQuirks) {
6656 E.removeListener(this.cal.oDomContainer, "resize", this._syncMask);
6659 this.purgeKeyListeners();
6663 * Attaches DOM listeners for keyboard support.
6664 * Tab/Shift-Tab looping, Enter Key Submit on Year element,
6665 * Up/Down/PgUp/PgDown year increment on Year element
6667 * NOTE: MacOSX Safari 2.x doesn't let you tab to buttons and
6668 * MacOSX Gecko does not let you tab to buttons or select controls,
6669 * so for these browsers, Tab/Shift-Tab looping is limited to the
6670 * elements which can be reached using the tab key.
6672 * @method applyKeyListeners
6674 applyKeyListeners : function() {
6675 var E = YAHOO.util.Event,
6678 // IE/Safari 3.1 doesn't fire keypress for arrow/pg keys (non-char keys)
6679 var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
6681 // - IE/Safari 3.1 doesn't fire keypress for non-char keys
6682 // - Opera doesn't allow us to cancel keydown or keypress for tab, but
6683 // changes focus successfully on keydown (keypress is too late to change focus - opera's already moved on).
6684 var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
6686 // Everyone likes keypress for Enter (char keys) - whoo hoo!
6687 E.on(this.yearEl, "keypress", this._handleEnterKey, this, true);
6689 E.on(this.yearEl, arrowEvt, this._handleDirectionKeys, this, true);
6690 E.on(this.lastCtrl, tabEvt, this._handleTabKey, this, true);
6691 E.on(this.firstCtrl, tabEvt, this._handleShiftTabKey, this, true);
6695 * Removes/purges DOM listeners for keyboard support
6697 * @method purgeKeyListeners
6699 purgeKeyListeners : function() {
6700 var E = YAHOO.util.Event,
6703 var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
6704 var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
6706 E.removeListener(this.yearEl, "keypress", this._handleEnterKey);
6707 E.removeListener(this.yearEl, arrowEvt, this._handleDirectionKeys);
6708 E.removeListener(this.lastCtrl, tabEvt, this._handleTabKey);
6709 E.removeListener(this.firstCtrl, tabEvt, this._handleShiftTabKey);
6713 * Updates the Calendar/CalendarGroup's pagedate with the currently set month and year if valid.
6715 * If the currently set month/year is invalid, a validation error will be displayed and the
6716 * Calendar/CalendarGroup's pagedate will not be updated.
6720 submit : function() {
6721 if (this.validate()) {
6724 this.setMonth(this._getMonthFromUI());
6725 this.setYear(this._getYearFromUI());
6729 // Artificial delay, just to help the user see something changed
6730 var delay = YAHOO.widget.CalendarNavigator.UPDATE_DELAY;
6733 window.setTimeout(function(){ nav._update(cal); }, delay);
6741 * Updates the Calendar rendered state, based on the state of the CalendarNavigator
6743 * @param cal The Calendar instance to update
6746 _update : function(cal) {
6747 cal.setYear(this.getYear());
6748 cal.setMonth(this.getMonth());
6753 * Hides the navigator and mask, without updating the Calendar/CalendarGroup's state
6757 cancel : function() {
6762 * Validates the current state of the UI controls
6765 * @return {Boolean} true, if the current UI state contains valid values, false if not
6767 validate : function() {
6768 if (this._getYearFromUI() !== null) {
6772 this.setYearError();
6773 this.setError(this.__getCfg("invalidYear", true));
6779 * Displays an error message in the Navigator's error panel
6781 * @param {String} msg The error message to display
6783 setError : function(msg) {
6785 this.errorEl.innerHTML = msg;
6786 this._show(this.errorEl, true);
6791 * Clears the navigator's error message and hides the error panel
6792 * @method clearError
6794 clearError : function() {
6796 this.errorEl.innerHTML = "";
6797 this._show(this.errorEl, false);
6802 * Displays the validation error UI for the year control
6803 * @method setYearError
6805 setYearError : function() {
6806 YAHOO.util.Dom.addClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
6810 * Removes the validation error UI for the year control
6811 * @method clearYearError
6813 clearYearError : function() {
6814 YAHOO.util.Dom.removeClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
6818 * Clears all validation and error messages in the UI
6819 * @method clearErrors
6821 clearErrors : function() {
6823 this.clearYearError();
6827 * Sets the initial focus, based on the configured value
6828 * @method setInitialFocus
6830 setInitialFocus : function() {
6831 var el = this.submitEl,
6832 f = this.__getCfg("initialFocus");
6834 if (f && f.toLowerCase) {
6835 f = f.toLowerCase();
6839 this.yearEl.select();
6843 } else if (f == "month") {
6848 if (el && YAHOO.lang.isFunction(el.focus)) {
6851 } catch (focusErr) {
6852 // TODO: Fall back if focus fails?
6858 * Removes all renderered HTML elements for the Navigator from
6859 * the DOM, purges event listeners and clears (nulls) any property
6860 * references to HTML references
6863 erase : function() {
6864 if (this.__rendered) {
6865 this.purgeListeners();
6867 // Clear out innerHTML references
6869 this.monthEl = null;
6870 this.errorEl = null;
6871 this.submitEl = null;
6872 this.cancelEl = null;
6873 this.firstCtrl = null;
6874 this.lastCtrl = null;
6876 this.navEl.innerHTML = "";
6879 var p = this.navEl.parentNode;
6881 p.removeChild(this.navEl);
6885 var pm = this.maskEl.parentNode;
6887 pm.removeChild(this.maskEl);
6890 this.__rendered = false;
6895 * Destroys the Navigator object and any HTML references
6898 destroy : function() {
6906 * Protected implementation to handle how UI elements are
6912 _show : function(el, bShow) {
6914 YAHOO.util.Dom.setStyle(el, "display", (bShow) ? "block" : "none");
6919 * Returns the month value (index), from the month UI element
6921 * @method _getMonthFromUI
6922 * @return {Number} The month index, or 0 if a UI element for the month
6925 _getMonthFromUI : function() {
6927 return this.monthEl.selectedIndex;
6929 return 0; // Default to Jan
6934 * Returns the year value, from the Navitator's year UI element
6936 * @method _getYearFromUI
6937 * @return {Number} The year value set in the UI, if valid. null is returned if
6938 * the UI does not contain a valid year value.
6940 _getYearFromUI : function() {
6941 var NAV = YAHOO.widget.CalendarNavigator;
6945 var value = this.yearEl.value;
6946 value = value.replace(NAV.TRIM, "$1");
6948 if (NAV.YR_PATTERN.test(value)) {
6949 yr = parseInt(value, 10);
6956 * Updates the Navigator's year UI, based on the year value set on the Navigator object
6958 * @method _updateYearUI
6960 _updateYearUI : function() {
6961 if (this.yearEl && this._year !== null) {
6962 this.yearEl.value = this._year;
6967 * Updates the Navigator's month UI, based on the month value set on the Navigator object
6969 * @method _updateMonthUI
6971 _updateMonthUI : function() {
6973 this.monthEl.selectedIndex = this._month;
6978 * Sets up references to the first and last focusable element in the Navigator's UI
6979 * in terms of tab order (Naviagator's firstEl and lastEl properties). The references
6980 * are used to control modality by looping around from the first to the last control
6981 * and visa versa for tab/shift-tab navigation.
6983 * See <a href="#applyKeyListeners">applyKeyListeners</a>
6986 * @method _setFirstLastElements
6988 _setFirstLastElements : function() {
6989 this.firstCtrl = this.monthEl;
6990 this.lastCtrl = this.cancelEl;
6992 // Special handling for MacOSX.
6993 // - Safari 2.x can't focus on buttons
6994 // - Gecko can't focus on select boxes or buttons
6996 if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420){
6997 this.firstCtrl = this.monthEl;
6998 this.lastCtrl = this.yearEl;
7000 if (YAHOO.env.ua.gecko) {
7001 this.firstCtrl = this.yearEl;
7002 this.lastCtrl = this.yearEl;
7008 * Default Keyboard event handler to capture Enter
7009 * on the Navigator's year control (yearEl)
7011 * @method _handleEnterKey
7013 * @param {Event} e The DOM event being handled
7015 _handleEnterKey : function(e) {
7016 var KEYS = YAHOO.util.KeyListener.KEY;
7018 if (YAHOO.util.Event.getCharCode(e) == KEYS.ENTER) {
7019 YAHOO.util.Event.preventDefault(e);
7025 * Default Keyboard event handler to capture up/down/pgup/pgdown
7026 * on the Navigator's year control (yearEl).
7028 * @method _handleDirectionKeys
7030 * @param {Event} e The DOM event being handled
7032 _handleDirectionKeys : function(e) {
7033 var E = YAHOO.util.Event,
7034 KEYS = YAHOO.util.KeyListener.KEY,
7035 NAV = YAHOO.widget.CalendarNavigator;
7037 var value = (this.yearEl.value) ? parseInt(this.yearEl.value, 10) : null;
7038 if (isFinite(value)) {
7040 switch(E.getCharCode(e)) {
7042 this.yearEl.value = value + NAV.YR_MINOR_INC;
7046 this.yearEl.value = Math.max(value - NAV.YR_MINOR_INC, 0);
7050 this.yearEl.value = value + NAV.YR_MAJOR_INC;
7053 case KEYS.PAGE_DOWN:
7054 this.yearEl.value = Math.max(value - NAV.YR_MAJOR_INC, 0);
7061 E.preventDefault(e);
7063 this.yearEl.select();
7072 * Default Keyboard event handler to capture Tab
7073 * on the last control (lastCtrl) in the Navigator.
7075 * @method _handleTabKey
7077 * @param {Event} e The DOM event being handled
7079 _handleTabKey : function(e) {
7080 var E = YAHOO.util.Event,
7081 KEYS = YAHOO.util.KeyListener.KEY;
7083 if (E.getCharCode(e) == KEYS.TAB && !e.shiftKey) {
7085 E.preventDefault(e);
7086 this.firstCtrl.focus();
7088 // Ignore - mainly for focus edge cases
7094 * Default Keyboard event handler to capture Shift-Tab
7095 * on the first control (firstCtrl) in the Navigator.
7097 * @method _handleShiftTabKey
7099 * @param {Event} e The DOM event being handled
7101 _handleShiftTabKey : function(e) {
7102 var E = YAHOO.util.Event,
7103 KEYS = YAHOO.util.KeyListener.KEY;
7105 if (e.shiftKey && E.getCharCode(e) == KEYS.TAB) {
7107 E.preventDefault(e);
7108 this.lastCtrl.focus();
7110 // Ignore - mainly for focus edge cases
7116 * Retrieve Navigator configuration values from
7117 * the parent Calendar/CalendarGroup's config value.
7119 * If it has not been set in the user provided configuration, the method will
7120 * return the default value of the configuration property, as set in _DEFAULT_CFG
7124 * @param {String} Case sensitive property name.
7125 * @param {Boolean} true, if the property is a string property, false if not.
7126 * @return The value of the configuration property
7128 __getCfg : function(prop, bIsStr) {
7129 var DEF_CFG = YAHOO.widget.CalendarNavigator._DEFAULT_CFG;
7130 var cfg = this.cal.cfg.getProperty("navigator");
7133 return (cfg !== true && cfg.strings && cfg.strings[prop]) ? cfg.strings[prop] : DEF_CFG.strings[prop];
7135 return (cfg !== true && cfg[prop]) ? cfg[prop] : DEF_CFG[prop];
7140 * Private flag, to identify MacOS
7144 __isMac : (navigator.userAgent.toLowerCase().indexOf("macintosh") != -1)
7148 YAHOO.register("calendar", YAHOO.widget.Calendar, {version: "2.7.0", build: "1799"});