MDL-11517 reserved word MOD used in table alias in questions backup code
[moodle-pu.git] / lib / yui / calendar / calendar.js
blob0f554186af867f38d14c0dc7754ced3d298e6be3
1 /*
2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.3.0
6 */
7 (function () {
9     /**
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
16     * @class Config
17     * @constructor
18     * @param {Object} owner The owner Object to which this Config Object belongs
19     */
20     YAHOO.util.Config = function (owner) {
21     
22         if (owner) {
23     
24             this.init(owner);
25     
26         }
27     
28         if (!owner) { 
29         
30     
31         }
32     
33     };
36     var Lang = YAHOO.lang,
37         CustomEvent = YAHOO.util.CustomEvent,        
38         Config = YAHOO.util.Config;
39     
41     /**
42      * Constant representing the CustomEvent type for the config changed event.
43      * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT
44      * @private
45      * @static
46      * @final
47      */
48     Config.CONFIG_CHANGED_EVENT = "configChanged";
49     
50     /**
51      * Constant representing the boolean type string
52      * @property YAHOO.util.Config.BOOLEAN_TYPE
53      * @private
54      * @static
55      * @final
56      */
57     Config.BOOLEAN_TYPE = "boolean";
58     
59     Config.prototype = {
60      
61         /**
62         * Object reference to the owner of this Config Object
63         * @property owner
64         * @type Object
65         */
66         owner: null,
67         
68         /**
69         * Boolean flag that specifies whether a queue is currently 
70         * being executed
71         * @property queueInProgress
72         * @type Boolean
73         */
74         queueInProgress: false,
75         
76         /**
77         * Maintains the local collection of configuration property objects and 
78         * their specified values
79         * @property config
80         * @private
81         * @type Object
82         */ 
83         config: null,
84         
85         /**
86         * Maintains the local collection of configuration property objects as 
87         * they were initially applied.
88         * This object is used when resetting a property.
89         * @property initialConfig
90         * @private
91         * @type Object
92         */ 
93         initialConfig: null,
94         
95         /**
96         * Maintains the local, normalized CustomEvent queue
97         * @property eventQueue
98         * @private
99         * @type Object
100         */ 
101         eventQueue: null,
102         
103         /**
104         * Custom Event, notifying subscribers when Config properties are set 
105         * (setProperty is called without the silent flag
106         * @event configChangedEvent
107         */
108         configChangedEvent: null,
109     
110         /**
111         * Initializes the configuration Object and all of its local members.
112         * @method init
113         * @param {Object} owner The owner Object to which this Config 
114         * Object belongs
115         */
116         init: function (owner) {
117     
118             this.owner = owner;
119     
120             this.configChangedEvent = 
121                 this.createEvent(Config.CONFIG_CHANGED_EVENT);
122     
123             this.configChangedEvent.signature = CustomEvent.LIST;
124             this.queueInProgress = false;
125             this.config = {};
126             this.initialConfig = {};
127             this.eventQueue = [];
128         
129         },
130         
131         /**
132         * Validates that the value passed in is a Boolean.
133         * @method checkBoolean
134         * @param {Object} val The value to validate
135         * @return {Boolean} true, if the value is valid
136         */ 
137         checkBoolean: function (val) {
138             return (typeof val == Config.BOOLEAN_TYPE);
139         },
140         
141         /**
142         * Validates that the value passed in is a number.
143         * @method checkNumber
144         * @param {Object} val The value to validate
145         * @return {Boolean} true, if the value is valid
146         */
147         checkNumber: function (val) {
148             return (!isNaN(val));
149         },
150         
151         /**
152         * Fires a configuration property event using the specified value. 
153         * @method fireEvent
154         * @private
155         * @param {String} key The configuration property's name
156         * @param {value} Object The value of the correct type for the property
157         */ 
158         fireEvent: function ( key, value ) {
159             var property = this.config[key];
160         
161             if (property && property.event) {
162                 property.event.fire(value);
163             } 
164         },
165         
166         /**
167         * Adds a property to the Config Object's private config hash.
168         * @method addProperty
169         * @param {String} key The configuration property's name
170         * @param {Object} propertyObject The Object containing all of this 
171         * property's arguments
172         */
173         addProperty: function ( key, propertyObject ) {
174             key = key.toLowerCase();
175         
176             this.config[key] = propertyObject;
177         
178             propertyObject.event = this.createEvent(key, { scope: this.owner });
179             propertyObject.event.signature = CustomEvent.LIST;
180             
181             
182             propertyObject.key = key;
183         
184             if (propertyObject.handler) {
185                 propertyObject.event.subscribe(propertyObject.handler, 
186                     this.owner);
187             }
188         
189             this.setProperty(key, propertyObject.value, true);
190             
191             if (! propertyObject.suppressEvent) {
192                 this.queueProperty(key, propertyObject.value);
193             }
194             
195         },
196         
197         /**
198         * Returns a key-value configuration map of the values currently set in  
199         * the Config Object.
200         * @method getConfig
201         * @return {Object} The current config, represented in a key-value map
202         */
203         getConfig: function () {
204         
205             var cfg = {},
206                 prop,
207                 property;
208                 
209             for (prop in this.config) {
210                 property = this.config[prop];
211                 if (property && property.event) {
212                     cfg[prop] = property.value;
213                 }
214             }
215             
216             return cfg;
217         },
218         
219         /**
220         * Returns the value of specified property.
221         * @method getProperty
222         * @param {String} key The name of the property
223         * @return {Object}  The value of the specified property
224         */
225         getProperty: function (key) {
226             var property = this.config[key.toLowerCase()];
227             if (property && property.event) {
228                 return property.value;
229             } else {
230                 return undefined;
231             }
232         },
233         
234         /**
235         * Resets the specified property's value to its initial value.
236         * @method resetProperty
237         * @param {String} key The name of the property
238         * @return {Boolean} True is the property was reset, false if not
239         */
240         resetProperty: function (key) {
241     
242             key = key.toLowerCase();
243         
244             var property = this.config[key];
245     
246             if (property && property.event) {
247     
248                 if (this.initialConfig[key] && 
249                     !Lang.isUndefined(this.initialConfig[key])) {
250     
251                     this.setProperty(key, this.initialConfig[key]);
252     
253                 }
254     
255                 return true;
256     
257             } else {
258     
259                 return false;
260             }
261     
262         },
263         
264         /**
265         * Sets the value of a property. If the silent property is passed as 
266         * true, the property's event will not be fired.
267         * @method setProperty
268         * @param {String} key The name of the property
269         * @param {String} value The value to set the property to
270         * @param {Boolean} silent Whether the value should be set silently, 
271         * without firing the property event.
272         * @return {Boolean} True, if the set was successful, false if it failed.
273         */
274         setProperty: function (key, value, silent) {
275         
276             var property;
277         
278             key = key.toLowerCase();
279         
280             if (this.queueInProgress && ! silent) {
281                 // Currently running through a queue... 
282                 this.queueProperty(key,value);
283                 return true;
284     
285             } else {
286                 property = this.config[key];
287                 if (property && property.event) {
288                     if (property.validator && !property.validator(value)) {
289                         return false;
290                     } else {
291                         property.value = value;
292                         if (! silent) {
293                             this.fireEvent(key, value);
294                             this.configChangedEvent.fire([key, value]);
295                         }
296                         return true;
297                     }
298                 } else {
299                     return false;
300                 }
301             }
302         },
303         
304         /**
305         * Sets the value of a property and queues its event to execute. If the 
306         * event is already scheduled to execute, it is
307         * moved from its current position to the end of the queue.
308         * @method queueProperty
309         * @param {String} key The name of the property
310         * @param {String} value The value to set the property to
311         * @return {Boolean}  true, if the set was successful, false if 
312         * it failed.
313         */ 
314         queueProperty: function (key, value) {
315         
316             key = key.toLowerCase();
317         
318             var property = this.config[key],
319                 foundDuplicate = false,
320                 iLen,
321                 queueItem,
322                 queueItemKey,
323                 queueItemValue,
324                 sLen,
325                 supercedesCheck,
326                 qLen,
327                 queueItemCheck,
328                 queueItemCheckKey,
329                 queueItemCheckValue,
330                 i,
331                 s,
332                 q;
333                                 
334             if (property && property.event) {
335     
336                 if (!Lang.isUndefined(value) && property.validator && 
337                     !property.validator(value)) { // validator
338                     return false;
339                 } else {
340         
341                     if (!Lang.isUndefined(value)) {
342                         property.value = value;
343                     } else {
344                         value = property.value;
345                     }
346         
347                     foundDuplicate = false;
348                     iLen = this.eventQueue.length;
349         
350                     for (i = 0; i < iLen; i++) {
351                         queueItem = this.eventQueue[i];
352         
353                         if (queueItem) {
354                             queueItemKey = queueItem[0];
355                             queueItemValue = queueItem[1];
356                             
357                             if (queueItemKey == key) {
358     
359                                 /*
360                                     found a dupe... push to end of queue, null 
361                                     current item, and break
362                                 */
363     
364                                 this.eventQueue[i] = null;
365     
366                                 this.eventQueue.push(
367                                     [key, (!Lang.isUndefined(value) ? 
368                                     value : queueItemValue)]);
369     
370                                 foundDuplicate = true;
371                                 break;
372                             }
373                         }
374                     }
375                     
376                     // this is a refire, or a new property in the queue
377     
378                     if (! foundDuplicate && !Lang.isUndefined(value)) { 
379                         this.eventQueue.push([key, value]);
380                     }
381                 }
382         
383                 if (property.supercedes) {
384         
385                     sLen = property.supercedes.length;
386         
387                     for (s = 0; s < sLen; s++) {
388         
389                         supercedesCheck = property.supercedes[s];
390                         qLen = this.eventQueue.length;
391         
392                         for (q = 0; q < qLen; q++) {
393                             queueItemCheck = this.eventQueue[q];
394         
395                             if (queueItemCheck) {
396                                 queueItemCheckKey = queueItemCheck[0];
397                                 queueItemCheckValue = queueItemCheck[1];
398                                 
399                                 if (queueItemCheckKey == 
400                                     supercedesCheck.toLowerCase() ) {
401     
402                                     this.eventQueue.push([queueItemCheckKey, 
403                                         queueItemCheckValue]);
404     
405                                     this.eventQueue[q] = null;
406                                     break;
407     
408                                 }
409                             }
410                         }
411                     }
412                 }
414         
415                 return true;
416             } else {
417                 return false;
418             }
419         },
420         
421         /**
422         * Fires the event for a property using the property's current value.
423         * @method refireEvent
424         * @param {String} key The name of the property
425         */
426         refireEvent: function (key) {
427     
428             key = key.toLowerCase();
429         
430             var property = this.config[key];
431     
432             if (property && property.event && 
433     
434                 !Lang.isUndefined(property.value)) {
435     
436                 if (this.queueInProgress) {
437     
438                     this.queueProperty(key);
439     
440                 } else {
441     
442                     this.fireEvent(key, property.value);
443     
444                 }
445     
446             }
447         },
448         
449         /**
450         * Applies a key-value Object literal to the configuration, replacing  
451         * any existing values, and queueing the property events.
452         * Although the values will be set, fireQueue() must be called for their 
453         * associated events to execute.
454         * @method applyConfig
455         * @param {Object} userConfig The configuration Object literal
456         * @param {Boolean} init  When set to true, the initialConfig will 
457         * be set to the userConfig passed in, so that calling a reset will 
458         * reset the properties to the passed values.
459         */
460         applyConfig: function (userConfig, init) {
461         
462             var prop;
463         
464             if (init) {
465                 this.initialConfig = userConfig;
466             }
467             for (prop in userConfig) {
468                 this.queueProperty(prop, userConfig[prop]);
469             }
470         },
471         
472         /**
473         * Refires the events for all configuration properties using their 
474         * current values.
475         * @method refresh
476         */
477         refresh: function () {
478         
479             var prop;
480         
481             for (prop in this.config) {
482                 this.refireEvent(prop);
483             }
484         },
485         
486         /**
487         * Fires the normalized list of queued property change events
488         * @method fireQueue
489         */
490         fireQueue: function () {
491         
492             var i, 
493                 queueItem,
494                 key,
495                 value,
496                 property;
497         
498             this.queueInProgress = true;
499             for (i = 0;i < this.eventQueue.length; i++) {
500                 queueItem = this.eventQueue[i];
501                 if (queueItem) {
502         
503                     key = queueItem[0];
504                     value = queueItem[1];
505                     property = this.config[key];
506         
507                     property.value = value;
508         
509                     this.fireEvent(key,value);
510                 }
511             }
512             
513             this.queueInProgress = false;
514             this.eventQueue = [];
515         },
516         
517         /**
518         * Subscribes an external handler to the change event for any 
519         * given property. 
520         * @method subscribeToConfigEvent
521         * @param {String} key The property name
522         * @param {Function} handler The handler function to use subscribe to 
523         * the property's event
524         * @param {Object} obj The Object to use for scoping the event handler 
525         * (see CustomEvent documentation)
526         * @param {Boolean} override Optional. If true, will override "this"  
527         * within the handler to map to the scope Object passed into the method.
528         * @return {Boolean} True, if the subscription was successful, 
529         * otherwise false.
530         */ 
531         subscribeToConfigEvent: function (key, handler, obj, override) {
532     
533             var property = this.config[key.toLowerCase()];
534     
535             if (property && property.event) {
536     
537                 if (!Config.alreadySubscribed(property.event, handler, obj)) {
538     
539                     property.event.subscribe(handler, obj, override);
540     
541                 }
542     
543                 return true;
544     
545             } else {
546     
547                 return false;
548     
549             }
550     
551         },
552         
553         /**
554         * Unsubscribes an external handler from the change event for any 
555         * given property. 
556         * @method unsubscribeFromConfigEvent
557         * @param {String} key The property name
558         * @param {Function} handler The handler function to use subscribe to 
559         * the property's event
560         * @param {Object} obj The Object to use for scoping the event 
561         * handler (see CustomEvent documentation)
562         * @return {Boolean} True, if the unsubscription was successful, 
563         * otherwise false.
564         */
565         unsubscribeFromConfigEvent: function (key, handler, obj) {
566             var property = this.config[key.toLowerCase()];
567             if (property && property.event) {
568                 return property.event.unsubscribe(handler, obj);
569             } else {
570                 return false;
571             }
572         },
573         
574         /**
575         * Returns a string representation of the Config object
576         * @method toString
577         * @return {String} The Config object in string format.
578         */
579         toString: function () {
580             var output = "Config";
581             if (this.owner) {
582                 output += " [" + this.owner.toString() + "]";
583             }
584             return output;
585         },
586         
587         /**
588         * Returns a string representation of the Config object's current 
589         * CustomEvent queue
590         * @method outputEventQueue
591         * @return {String} The string list of CustomEvents currently queued 
592         * for execution
593         */
594         outputEventQueue: function () {
596             var output = "",
597                 queueItem,
598                 q,
599                 nQueue = this.eventQueue.length;
600               
601             for (q = 0; q < nQueue; q++) {
602                 queueItem = this.eventQueue[q];
603                 if (queueItem) {
604                     output += queueItem[0] + "=" + queueItem[1] + ", ";
605                 }
606             }
607             return output;
608         },
610         /**
611         * Sets all properties to null, unsubscribes all listeners from each 
612         * property's change event and all listeners from the configChangedEvent.
613         * @method destroy
614         */
615         destroy: function () {
617             var oConfig = this.config,
618                 sProperty,
619                 oProperty;
622             for (sProperty in oConfig) {
623             
624                 if (Lang.hasOwnProperty(oConfig, sProperty)) {
626                     oProperty = oConfig[sProperty];
628                     oProperty.event.unsubscribeAll();
629                     oProperty.event = null;
631                 }
632             
633             }
634             
635             this.configChangedEvent.unsubscribeAll();
636             
637             this.configChangedEvent = null;
638             this.owner = null;
639             this.config = null;
640             this.initialConfig = null;
641             this.eventQueue = null;
642         
643         }
645     };
646     
647     
648     
649     /**
650     * Checks to determine if a particular function/Object pair are already 
651     * subscribed to the specified CustomEvent
652     * @method YAHOO.util.Config.alreadySubscribed
653     * @static
654     * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check 
655     * the subscriptions
656     * @param {Function} fn The function to look for in the subscribers list
657     * @param {Object} obj The execution scope Object for the subscription
658     * @return {Boolean} true, if the function/Object pair is already subscribed 
659     * to the CustomEvent passed in
660     */
661     Config.alreadySubscribed = function (evt, fn, obj) {
662     
663         var nSubscribers = evt.subscribers.length,
664             subsc,
665             i;
667         if (nSubscribers > 0) {
669             i = nSubscribers - 1;
670         
671             do {
673                 subsc = evt.subscribers[i];
675                 if (subsc && subsc.obj == obj && subsc.fn == fn) {
676         
677                     return true;
678         
679                 }    
680             
681             }
682             while (i--);
683         
684         }
685     
686         return false;
687     
688     };
689     
690     YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider);
692 }());
695 * YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility
696 * used for adding, subtracting, and comparing dates.
697 * @namespace YAHOO.widget
698 * @class DateMath
700 YAHOO.widget.DateMath = {
701         /**
702         * Constant field representing Day
703         * @property DAY
704         * @static
705         * @final
706         * @type String
707         */
708         DAY : "D",
710         /**
711         * Constant field representing Week
712         * @property WEEK
713         * @static
714         * @final
715         * @type String
716         */
717         WEEK : "W",
719         /**
720         * Constant field representing Year
721         * @property YEAR
722         * @static
723         * @final
724         * @type String
725         */
726         YEAR : "Y",
728         /**
729         * Constant field representing Month
730         * @property MONTH
731         * @static
732         * @final
733         * @type String
734         */
735         MONTH : "M",
737         /**
738         * Constant field representing one day, in milliseconds
739         * @property ONE_DAY_MS
740         * @static
741         * @final
742         * @type Number
743         */
744         ONE_DAY_MS : 1000*60*60*24,
746         /**
747         * Adds the specified amount of time to the this instance.
748         * @method add
749         * @param {Date} date    The JavaScript Date object to perform addition on
750         * @param {String} field The field constant to be used for performing addition.
751         * @param {Number} amount        The number of units (measured in the field constant) to add to the date.
752         * @return {Date} The resulting Date object
753         */
754         add : function(date, field, amount) {
755                 var d = new Date(date.getTime());
756                 switch (field) {
757                         case this.MONTH:
758                                 var newMonth = date.getMonth() + amount;
759                                 var years = 0;
762                                 if (newMonth < 0) {
763                                         while (newMonth < 0) {
764                                                 newMonth += 12;
765                                                 years -= 1;
766                                         }
767                                 } else if (newMonth > 11) {
768                                         while (newMonth > 11) {
769                                                 newMonth -= 12;
770                                                 years += 1;
771                                         }
772                                 }
773                                 
774                                 d.setMonth(newMonth);
775                                 d.setFullYear(date.getFullYear() + years);
776                                 break;
777                         case this.DAY:
778                                 d.setDate(date.getDate() + amount);
779                                 break;
780                         case this.YEAR:
781                                 d.setFullYear(date.getFullYear() + amount);
782                                 break;
783                         case this.WEEK:
784                                 d.setDate(date.getDate() + (amount * 7));
785                                 break;
786                 }
787                 return d;
788         },
790         /**
791         * Subtracts the specified amount of time from the this instance.
792         * @method subtract
793         * @param {Date} date    The JavaScript Date object to perform subtraction on
794         * @param {Number} field The this field constant to be used for performing subtraction.
795         * @param {Number} amount        The number of units (measured in the field constant) to subtract from the date.
796         * @return {Date} The resulting Date object
797         */
798         subtract : function(date, field, amount) {
799                 return this.add(date, field, (amount*-1));
800         },
802         /**
803         * Determines whether a given date is before another date on the calendar.
804         * @method before
805         * @param {Date} date            The Date object to compare with the compare argument
806         * @param {Date} compareTo       The Date object to use for the comparison
807         * @return {Boolean} true if the date occurs before the compared date; false if not.
808         */
809         before : function(date, compareTo) {
810                 var ms = compareTo.getTime();
811                 if (date.getTime() < ms) {
812                         return true;
813                 } else {
814                         return false;
815                 }
816         },
818         /**
819         * Determines whether a given date is after another date on the calendar.
820         * @method after
821         * @param {Date} date            The Date object to compare with the compare argument
822         * @param {Date} compareTo       The Date object to use for the comparison
823         * @return {Boolean} true if the date occurs after the compared date; false if not.
824         */
825         after : function(date, compareTo) {
826                 var ms = compareTo.getTime();
827                 if (date.getTime() > ms) {
828                         return true;
829                 } else {
830                         return false;
831                 }
832         },
834         /**
835         * Determines whether a given date is between two other dates on the calendar.
836         * @method between
837         * @param {Date} date            The date to check for
838         * @param {Date} dateBegin       The start of the range
839         * @param {Date} dateEnd         The end of the range
840         * @return {Boolean} true if the date occurs between the compared dates; false if not.
841         */
842         between : function(date, dateBegin, dateEnd) {
843                 if (this.after(date, dateBegin) && this.before(date, dateEnd)) {
844                         return true;
845                 } else {
846                         return false;
847                 }
848         },
849         
850         /**
851         * Retrieves a JavaScript Date object representing January 1 of any given year.
852         * @method getJan1
853         * @param {Number} calendarYear          The calendar year for which to retrieve January 1
854         * @return {Date}        January 1 of the calendar year specified.
855         */
856         getJan1 : function(calendarYear) {
857                 return new Date(calendarYear,0,1); 
858         },
860         /**
861         * Calculates the number of days the specified date is from January 1 of the specified calendar year.
862         * Passing January 1 to this function would return an offset value of zero.
863         * @method getDayOffset
864         * @param {Date} date    The JavaScript date for which to find the offset
865         * @param {Number} calendarYear  The calendar year to use for determining the offset
866         * @return {Number}      The number of days since January 1 of the given year
867         */
868         getDayOffset : function(date, calendarYear) {
869                 var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1.
870                 
871                 // Find the number of days the passed in date is away from the calendar year start
872                 var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS);
873                 return dayOffset;
874         },
876         /**
877         * Calculates the week number for the given date. This function assumes that week 1 is the
878         * week in which January 1 appears, regardless of whether the week consists of a full 7 days.
879         * The calendar year can be specified to help find what a the week number would be for a given
880         * date if the date overlaps years. For instance, a week may be considered week 1 of 2005, or
881         * week 53 of 2004. Specifying the optional calendarYear allows one to make this distinction
882         * easily.
883         * @method getWeekNumber
884         * @param {Date} date    The JavaScript date for which to find the week number
885         * @param {Number} calendarYear  OPTIONAL - The calendar year to use for determining the week number. Default is
886         *                                                                                       the calendar year of parameter "date".
887         * @return {Number}      The week number of the given date.
888         */
889         getWeekNumber : function(date, calendarYear) {
890                 date = this.clearTime(date);
891                 var nearestThurs = new Date(date.getTime() + (4 * this.ONE_DAY_MS) - ((date.getDay()) * this.ONE_DAY_MS));
893                 var jan1 = new Date(nearestThurs.getFullYear(),0,1);
894                 var dayOfYear = ((nearestThurs.getTime() - jan1.getTime()) / this.ONE_DAY_MS) - 1;
896                 var weekNum = Math.ceil((dayOfYear)/ 7);
897                 return weekNum;
898         },
900         /**
901         * Determines if a given week overlaps two different years.
902         * @method isYearOverlapWeek
903         * @param {Date} weekBeginDate   The JavaScript Date representing the first day of the week.
904         * @return {Boolean}     true if the date overlaps two different years.
905         */
906         isYearOverlapWeek : function(weekBeginDate) {
907                 var overlaps = false;
908                 var nextWeek = this.add(weekBeginDate, this.DAY, 6);
909                 if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) {
910                         overlaps = true;
911                 }
912                 return overlaps;
913         },
915         /**
916         * Determines if a given week overlaps two different months.
917         * @method isMonthOverlapWeek
918         * @param {Date} weekBeginDate   The JavaScript Date representing the first day of the week.
919         * @return {Boolean}     true if the date overlaps two different months.
920         */
921         isMonthOverlapWeek : function(weekBeginDate) {
922                 var overlaps = false;
923                 var nextWeek = this.add(weekBeginDate, this.DAY, 6);
924                 if (nextWeek.getMonth() != weekBeginDate.getMonth()) {
925                         overlaps = true;
926                 }
927                 return overlaps;
928         },
930         /**
931         * Gets the first day of a month containing a given date.
932         * @method findMonthStart
933         * @param {Date} date    The JavaScript Date used to calculate the month start
934         * @return {Date}                The JavaScript Date representing the first day of the month
935         */
936         findMonthStart : function(date) {
937                 var start = new Date(date.getFullYear(), date.getMonth(), 1);
938                 return start;
939         },
941         /**
942         * Gets the last day of a month containing a given date.
943         * @method findMonthEnd
944         * @param {Date} date    The JavaScript Date used to calculate the month end
945         * @return {Date}                The JavaScript Date representing the last day of the month
946         */
947         findMonthEnd : function(date) {
948                 var start = this.findMonthStart(date);
949                 var nextMonth = this.add(start, this.MONTH, 1);
950                 var end = this.subtract(nextMonth, this.DAY, 1);
951                 return end;
952         },
954         /**
955         * Clears the time fields from a given date, effectively setting the time to 12 noon.
956         * @method clearTime
957         * @param {Date} date    The JavaScript Date for which the time fields will be cleared
958         * @return {Date}                The JavaScript Date cleared of all time fields
959         */
960         clearTime : function(date) {
961                 date.setHours(12,0,0,0);
962                 return date;
963         }
967 * The Calendar component is a UI control that enables users to choose one or more dates from a graphical calendar presented in a one-month  or multi-month interface. Calendars are generated entirely via script and can be navigated without any page refreshes.
968 * @module    calendar
969 * @title     Calendar
970 * @namespace YAHOO.widget
971 * @requires  yahoo,dom,event
975 * Calendar is the base class for the Calendar widget. In its most basic
976 * implementation, it has the ability to render a calendar widget on the page
977 * that can be manipulated to select a single date, move back and forth between
978 * months and years.
979 * <p>To construct the placeholder for the calendar widget, the code is as
980 * follows:
981 *       <xmp>
982 *               <div id="cal1Container"></div>
983 *       </xmp>
984 * </p>
985 * @namespace YAHOO.widget
986 * @class Calendar
987 * @constructor
988 * @param {String}       id                      The id of the table element that will represent the calendar widget
989 * @param {String}       containerId     The id of the container div element that will wrap the calendar table
990 * @param {Object}       config          The configuration object containing the Calendar's arguments
992 YAHOO.widget.Calendar = function(id, containerId, config) {
993         this.init(id, containerId, config);
997 * The path to be used for images loaded for the Calendar
998 * @property YAHOO.widget.Calendar.IMG_ROOT
999 * @static
1000 * @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
1001 * @type String
1003 YAHOO.widget.Calendar.IMG_ROOT = null;
1006 * Type constant used for renderers to represent an individual date (M/D/Y)
1007 * @property YAHOO.widget.Calendar.DATE
1008 * @static
1009 * @final
1010 * @type String
1012 YAHOO.widget.Calendar.DATE = "D";
1015 * Type constant used for renderers to represent an individual date across any year (M/D)
1016 * @property YAHOO.widget.Calendar.MONTH_DAY
1017 * @static
1018 * @final
1019 * @type String
1021 YAHOO.widget.Calendar.MONTH_DAY = "MD";
1024 * Type constant used for renderers to represent a weekday
1025 * @property YAHOO.widget.Calendar.WEEKDAY
1026 * @static
1027 * @final
1028 * @type String
1030 YAHOO.widget.Calendar.WEEKDAY = "WD";
1033 * Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
1034 * @property YAHOO.widget.Calendar.RANGE
1035 * @static
1036 * @final
1037 * @type String
1039 YAHOO.widget.Calendar.RANGE = "R";
1042 * Type constant used for renderers to represent a month across any year
1043 * @property YAHOO.widget.Calendar.MONTH
1044 * @static
1045 * @final
1046 * @type String
1048 YAHOO.widget.Calendar.MONTH = "M";
1051 * Constant that represents the total number of date cells that are displayed in a given month
1052 * @property YAHOO.widget.Calendar.DISPLAY_DAYS
1053 * @static
1054 * @final
1055 * @type Number
1057 YAHOO.widget.Calendar.DISPLAY_DAYS = 42;
1060 * Constant used for halting the execution of the remainder of the render stack
1061 * @property YAHOO.widget.Calendar.STOP_RENDER
1062 * @static
1063 * @final
1064 * @type String
1066 YAHOO.widget.Calendar.STOP_RENDER = "S";
1069 * Constant used to represent short date field string formats (e.g. Tu or Feb)
1070 * @property YAHOO.widget.Calendar.SHORT
1071 * @static
1072 * @final
1073 * @type String
1075 YAHOO.widget.Calendar.SHORT = "short";
1078 * Constant used to represent long date field string formats (e.g. Monday or February)
1079 * @property YAHOO.widget.Calendar.LONG
1080 * @static
1081 * @final
1082 * @type String
1084 YAHOO.widget.Calendar.LONG = "long";
1087 * Constant used to represent medium date field string formats (e.g. Mon)
1088 * @property YAHOO.widget.Calendar.MEDIUM
1089 * @static
1090 * @final
1091 * @type String
1093 YAHOO.widget.Calendar.MEDIUM = "medium";
1096 * Constant used to represent single character date field string formats (e.g. M, T, W)
1097 * @property YAHOO.widget.Calendar.ONE_CHAR
1098 * @static
1099 * @final
1100 * @type String
1102 YAHOO.widget.Calendar.ONE_CHAR = "1char";
1105 * The set of default Config property keys and values for the Calendar
1106 * @property YAHOO.widget.Calendar._DEFAULT_CONFIG
1107 * @final
1108 * @static
1109 * @private
1110 * @type Object
1112 YAHOO.widget.Calendar._DEFAULT_CONFIG = {
1113         // Default values for pagedate and selected are not class level constants - they are set during instance creation 
1114         PAGEDATE : {key:"pagedate", value:null},
1115         SELECTED : {key:"selected", value:null},
1116         TITLE : {key:"title", value:""},
1117         CLOSE : {key:"close", value:false},
1118         IFRAME : {key:"iframe", value:(YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) ? true : false},
1119         MINDATE : {key:"mindate", value:null},
1120         MAXDATE : {key:"maxdate", value:null},
1121         MULTI_SELECT : {key:"multi_select", value:false},
1122         START_WEEKDAY : {key:"start_weekday", value:0},
1123         SHOW_WEEKDAYS : {key:"show_weekdays", value:true},
1124         SHOW_WEEK_HEADER : {key:"show_week_header", value:false},
1125         SHOW_WEEK_FOOTER : {key:"show_week_footer", value:false},
1126         HIDE_BLANK_WEEKS : {key:"hide_blank_weeks", value:false},
1127         NAV_ARROW_LEFT: {key:"nav_arrow_left", value:null} ,
1128         NAV_ARROW_RIGHT : {key:"nav_arrow_right", value:null} ,
1129         MONTHS_SHORT : {key:"months_short", value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]},
1130         MONTHS_LONG: {key:"months_long", value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]},
1131         WEEKDAYS_1CHAR: {key:"weekdays_1char", value:["S", "M", "T", "W", "T", "F", "S"]},
1132         WEEKDAYS_SHORT: {key:"weekdays_short", value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]},
1133         WEEKDAYS_MEDIUM: {key:"weekdays_medium", value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]},
1134         WEEKDAYS_LONG: {key:"weekdays_long", value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]},
1135         LOCALE_MONTHS:{key:"locale_months", value:"long"},
1136         LOCALE_WEEKDAYS:{key:"locale_weekdays", value:"short"},
1137         DATE_DELIMITER:{key:"date_delimiter", value:","},
1138         DATE_FIELD_DELIMITER:{key:"date_field_delimiter", value:"/"},
1139         DATE_RANGE_DELIMITER:{key:"date_range_delimiter", value:"-"},
1140         MY_MONTH_POSITION:{key:"my_month_position", value:1},
1141         MY_YEAR_POSITION:{key:"my_year_position", value:2},
1142         MD_MONTH_POSITION:{key:"md_month_position", value:1},
1143         MD_DAY_POSITION:{key:"md_day_position", value:2},
1144         MDY_MONTH_POSITION:{key:"mdy_month_position", value:1},
1145         MDY_DAY_POSITION:{key:"mdy_day_position", value:2},
1146         MDY_YEAR_POSITION:{key:"mdy_year_position", value:3},
1147         MY_LABEL_MONTH_POSITION:{key:"my_label_month_position", value:1},
1148         MY_LABEL_YEAR_POSITION:{key:"my_label_year_position", value:2},
1149         MY_LABEL_MONTH_SUFFIX:{key:"my_label_month_suffix", value:" "},
1150         MY_LABEL_YEAR_SUFFIX:{key:"my_label_year_suffix", value:""}
1154 * The set of Custom Event types supported by the Calendar
1155 * @property YAHOO.widget.Calendar._EVENT_TYPES
1156 * @final
1157 * @static
1158 * @private
1159 * @type Object
1161 YAHOO.widget.Calendar._EVENT_TYPES = {
1162         BEFORE_SELECT : "beforeSelect", 
1163         SELECT : "select",
1164         BEFORE_DESELECT : "beforeDeselect",
1165         DESELECT : "deselect",
1166         CHANGE_PAGE : "changePage",
1167         BEFORE_RENDER : "beforeRender",
1168         RENDER : "render",
1169         RESET : "reset",
1170         CLEAR : "clear"
1174 * The set of default style constants for the Calendar
1175 * @property YAHOO.widget.Calendar._STYLES
1176 * @final
1177 * @static
1178 * @private
1179 * @type Object
1181 YAHOO.widget.Calendar._STYLES = {
1182         CSS_ROW_HEADER: "calrowhead",
1183         CSS_ROW_FOOTER: "calrowfoot",
1184         CSS_CELL : "calcell",
1185         CSS_CELL_SELECTOR : "selector",
1186         CSS_CELL_SELECTED : "selected",
1187         CSS_CELL_SELECTABLE : "selectable",
1188         CSS_CELL_RESTRICTED : "restricted",
1189         CSS_CELL_TODAY : "today",
1190         CSS_CELL_OOM : "oom",
1191         CSS_CELL_OOB : "previous",
1192         CSS_HEADER : "calheader",
1193         CSS_HEADER_TEXT : "calhead",
1194         CSS_BODY : "calbody",
1195         CSS_WEEKDAY_CELL : "calweekdaycell",
1196         CSS_WEEKDAY_ROW : "calweekdayrow",
1197         CSS_FOOTER : "calfoot",
1198         CSS_CALENDAR : "yui-calendar",
1199         CSS_SINGLE : "single",
1200         CSS_CONTAINER : "yui-calcontainer",
1201         CSS_NAV_LEFT : "calnavleft",
1202         CSS_NAV_RIGHT : "calnavright",
1203         CSS_CLOSE : "calclose",
1204         CSS_CELL_TOP : "calcelltop",
1205         CSS_CELL_LEFT : "calcellleft",
1206         CSS_CELL_RIGHT : "calcellright",
1207         CSS_CELL_BOTTOM : "calcellbottom",
1208         CSS_CELL_HOVER : "calcellhover",
1209         CSS_CELL_HIGHLIGHT1 : "highlight1",
1210         CSS_CELL_HIGHLIGHT2 : "highlight2",
1211         CSS_CELL_HIGHLIGHT3 : "highlight3",
1212         CSS_CELL_HIGHLIGHT4 : "highlight4"
1215 YAHOO.widget.Calendar.prototype = {
1217         /**
1218         * The configuration object used to set up the calendars various locale and style options.
1219         * @property Config
1220         * @private
1221         * @deprecated Configuration properties should be set by calling Calendar.cfg.setProperty.
1222         * @type Object
1223         */
1224         Config : null,
1226         /**
1227         * The parent CalendarGroup, only to be set explicitly by the parent group
1228         * @property parent
1229         * @type CalendarGroup
1230         */      
1231         parent : null,
1233         /**
1234         * The index of this item in the parent group
1235         * @property index
1236         * @type Number
1237         */
1238         index : -1,
1240         /**
1241         * The collection of calendar table cells
1242         * @property cells
1243         * @type HTMLTableCellElement[]
1244         */
1245         cells : null,
1246         
1247         /**
1248         * 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].
1249         * @property cellDates
1250         * @type Array[](Number[])
1251         */
1252         cellDates : null,
1254         /**
1255         * The id that uniquely identifies this calendar. This id should match the id of the placeholder element on the page.
1256         * @property id
1257         * @type String
1258         */
1259         id : null,
1261         /**
1262         * 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.
1263         * @property oDomContainer
1264         * @type HTMLElement
1265         */
1266         oDomContainer : null,
1268         /**
1269         * A Date object representing today's date.
1270         * @property today
1271         * @type Date
1272         */
1273         today : null,
1275         /**
1276         * The list of render functions, along with required parameters, used to render cells. 
1277         * @property renderStack
1278         * @type Array[]
1279         */
1280         renderStack : null,
1282         /**
1283         * A copy of the initial render functions created before rendering.
1284         * @property _renderStack
1285         * @private
1286         * @type Array
1287         */
1288         _renderStack : null,
1290         /**
1291         * The private list of initially selected dates.
1292         * @property _selectedDates
1293         * @private
1294         * @type Array
1295         */
1296         _selectedDates : null,
1298         /**
1299         * A map of DOM event handlers to attach to cells associated with specific CSS class names
1300         * @property domEventMap
1301         * @type Object
1302         */
1303         domEventMap : null
1309 * Initializes the Calendar widget.
1310 * @method init
1311 * @param {String}       id                      The id of the table element that will represent the calendar widget
1312 * @param {String}       containerId     The id of the container div element that will wrap the calendar table
1313 * @param {Object}       config          The configuration object containing the Calendar's arguments
1315 YAHOO.widget.Calendar.prototype.init = function(id, containerId, config) {
1316         this.initEvents();
1317         this.today = new Date();
1318         YAHOO.widget.DateMath.clearTime(this.today);
1320         this.id = id;
1321         this.oDomContainer = document.getElementById(containerId);
1323         /**
1324         * The Config object used to hold the configuration variables for the Calendar
1325         * @property cfg
1326         * @type YAHOO.util.Config
1327         */
1328         this.cfg = new YAHOO.util.Config(this);
1329         
1330         /**
1331         * The local object which contains the Calendar's options
1332         * @property Options
1333         * @type Object
1334         */
1335         this.Options = {};
1337         /**
1338         * The local object which contains the Calendar's locale settings
1339         * @property Locale
1340         * @type Object
1341         */
1342         this.Locale = {};
1344         this.initStyles();
1346         YAHOO.util.Dom.addClass(this.oDomContainer, this.Style.CSS_CONTAINER);  
1347         YAHOO.util.Dom.addClass(this.oDomContainer, this.Style.CSS_SINGLE);
1348         
1349         this.cellDates = [];
1350         this.cells = [];
1351         this.renderStack = [];
1352         this._renderStack = [];
1354         this.setupConfig();
1355         
1356         if (config) {
1357                 this.cfg.applyConfig(config, true);
1358         }
1359         
1360         this.cfg.fireQueue();
1364 * Default Config listener for the iframe property. If the iframe config property is set to true, 
1365 * renders the built-in IFRAME shim if the container is relatively or absolutely positioned.
1367 * @method configIframe
1369 YAHOO.widget.Calendar.prototype.configIframe = function(type, args, obj) {
1370         var useIframe = args[0];
1372         if (!this.parent) {
1373                 if (YAHOO.util.Dom.inDocument(this.oDomContainer)) {
1374                         if (useIframe) {
1375                                 var pos = YAHOO.util.Dom.getStyle(this.oDomContainer, "position");
1376                                 
1377                                 if (pos == "absolute" || pos == "relative") {
1378                                         
1379                                         if (!YAHOO.util.Dom.inDocument(this.iframe)) {
1380                                                 this.iframe = document.createElement("iframe");
1381                                                 this.iframe.src = "javascript:false;";
1383                                                 YAHOO.util.Dom.setStyle(this.iframe, "opacity", "0");
1385                                                 if (YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) {
1386                                                         YAHOO.util.Dom.addClass(this.iframe, "fixedsize");
1387                                                 }
1389                                                 this.oDomContainer.insertBefore(this.iframe, this.oDomContainer.firstChild);
1390                                         }
1391                                 }
1392                         } else {
1393                                 if (this.iframe) {
1394                                         if (this.iframe.parentNode) {
1395                                                 this.iframe.parentNode.removeChild(this.iframe);
1396                                         }
1397                                         this.iframe = null;
1398                                 }
1399                         }
1400                 }
1401         }
1405 * Default handler for the "title" property
1406 * @method configTitle
1408 YAHOO.widget.Calendar.prototype.configTitle = function(type, args, obj) {
1409         var title = args[0];
1410         var close = this.cfg.getProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.CLOSE.key);
1411         
1412         var titleDiv;
1414         if (title && title !== "") {
1415                 titleDiv = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || document.createElement("div");
1416                 titleDiv.className = YAHOO.widget.CalendarGroup.CSS_2UPTITLE;
1417                 titleDiv.innerHTML = title;
1418                 this.oDomContainer.insertBefore(titleDiv, this.oDomContainer.firstChild);
1419                 YAHOO.util.Dom.addClass(this.oDomContainer, "withtitle");
1420         } else {
1421                 titleDiv = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || null;
1423                 if (titleDiv) {
1424                         YAHOO.util.Event.purgeElement(titleDiv);
1425                         this.oDomContainer.removeChild(titleDiv);
1426                 }
1427                 if (! close) {
1428                         YAHOO.util.Dom.removeClass(this.oDomContainer, "withtitle");
1429                 }
1430         }
1434 * Default handler for the "close" property
1435 * @method configClose
1437 YAHOO.widget.Calendar.prototype.configClose = function(type, args, obj) {
1438         var close = args[0];
1439         var title = this.cfg.getProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.TITLE.key);
1440         
1441         var DEPR_CLOSE_PATH = "us/my/bn/x_d.gif";
1443         var linkClose;
1445         if (close === true) {
1446                 linkClose = YAHOO.util.Dom.getElementsByClassName("link-close", "a", this.oDomContainer)[0] || document.createElement("a");
1447                 linkClose.href = "#";
1448                 linkClose.className = "link-close";
1449                 YAHOO.util.Event.addListener(linkClose, "click", function(e, cal) {cal.hide(); YAHOO.util.Event.preventDefault(e); }, this);
1450                 
1451                 if (YAHOO.widget.Calendar.IMG_ROOT !== null) {
1452                         var imgClose = document.createElement("img");
1453                         imgClose.src = YAHOO.widget.Calendar.IMG_ROOT + DEPR_CLOSE_PATH;
1454                         imgClose.className = YAHOO.widget.CalendarGroup.CSS_2UPCLOSE;
1455                         linkClose.appendChild(imgClose);
1456                 } else {
1457                         linkClose.innerHTML = '<span class="' + YAHOO.widget.CalendarGroup.CSS_2UPCLOSE + ' ' + this.Style.CSS_CLOSE + '"></span>';
1458                 }
1459                 
1460                 this.oDomContainer.appendChild(linkClose);
1461                 YAHOO.util.Dom.addClass(this.oDomContainer, "withtitle");
1462         } else {
1463                 linkClose = YAHOO.util.Dom.getElementsByClassName("link-close", "a", this.oDomContainer)[0] || null;
1464                 if (linkClose) {
1465                         YAHOO.util.Event.purgeElement(linkClose);
1466                         this.oDomContainer.removeChild(linkClose);
1467                 }
1468                 if (! title || title === "") {
1469                         YAHOO.util.Dom.removeClass(this.oDomContainer, "withtitle");
1470                 }
1471         }
1475 * Initializes Calendar's built-in CustomEvents
1476 * @method initEvents
1478 YAHOO.widget.Calendar.prototype.initEvents = function() {
1480         var defEvents = YAHOO.widget.Calendar._EVENT_TYPES;
1482         /**
1483         * Fired before a selection is made
1484         * @event beforeSelectEvent
1485         */
1486         this.beforeSelectEvent = new YAHOO.util.CustomEvent(defEvents.BEFORE_SELECT); 
1488         /**
1489         * Fired when a selection is made
1490         * @event selectEvent
1491         * @param {Array}        Array of Date field arrays in the format [YYYY, MM, DD].
1492         */
1493         this.selectEvent = new YAHOO.util.CustomEvent(defEvents.SELECT);
1495         /**
1496         * Fired before a selection is made
1497         * @event beforeDeselectEvent
1498         */
1499         this.beforeDeselectEvent = new YAHOO.util.CustomEvent(defEvents.BEFORE_DESELECT);
1501         /**
1502         * Fired when a selection is made
1503         * @event deselectEvent
1504         * @param {Array}        Array of Date field arrays in the format [YYYY, MM, DD].
1505         */
1506         this.deselectEvent = new YAHOO.util.CustomEvent(defEvents.DESELECT);
1508         /**
1509         * Fired when the Calendar page is changed
1510         * @event changePageEvent
1511         */
1512         this.changePageEvent = new YAHOO.util.CustomEvent(defEvents.CHANGE_PAGE);
1514         /**
1515         * Fired before the Calendar is rendered
1516         * @event beforeRenderEvent
1517         */
1518         this.beforeRenderEvent = new YAHOO.util.CustomEvent(defEvents.BEFORE_RENDER);
1520         /**
1521         * Fired when the Calendar is rendered
1522         * @event renderEvent
1523         */
1524         this.renderEvent = new YAHOO.util.CustomEvent(defEvents.RENDER);
1526         /**
1527         * Fired when the Calendar is reset
1528         * @event resetEvent
1529         */
1530         this.resetEvent = new YAHOO.util.CustomEvent(defEvents.RESET);
1532         /**
1533         * Fired when the Calendar is cleared
1534         * @event clearEvent
1535         */
1536         this.clearEvent = new YAHOO.util.CustomEvent(defEvents.CLEAR);
1538         this.beforeSelectEvent.subscribe(this.onBeforeSelect, this, true);
1539         this.selectEvent.subscribe(this.onSelect, this, true);
1540         this.beforeDeselectEvent.subscribe(this.onBeforeDeselect, this, true);
1541         this.deselectEvent.subscribe(this.onDeselect, this, true);
1542         this.changePageEvent.subscribe(this.onChangePage, this, true);
1543         this.renderEvent.subscribe(this.onRender, this, true);
1544         this.resetEvent.subscribe(this.onReset, this, true);
1545         this.clearEvent.subscribe(this.onClear, this, true);
1549 * The default event function that is attached to a date link within a calendar cell
1550 * when the calendar is rendered.
1551 * @method doSelectCell
1552 * @param {DOMEvent} e   The event
1553 * @param {Calendar} cal A reference to the calendar passed by the Event utility
1555 YAHOO.widget.Calendar.prototype.doSelectCell = function(e, cal) {
1556         var cell,index,d,date;
1558         var target = YAHOO.util.Event.getTarget(e);
1559         var tagName = target.tagName.toLowerCase();
1560         var defSelector = false;
1562         while (tagName != "td" && ! YAHOO.util.Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1564                 if (!defSelector && tagName == "a" && YAHOO.util.Dom.hasClass(target, cal.Style.CSS_CELL_SELECTOR)) {
1565                         defSelector = true;     
1566                 }
1568                 target = target.parentNode;
1569                 tagName = target.tagName.toLowerCase(); 
1570                 if (tagName == "html") {
1571                         return;
1572                 }
1573         }
1575         if (defSelector) {
1576                 // Stop link href navigation for default renderer
1577                 YAHOO.util.Event.preventDefault(e);
1578         }
1580         cell = target;
1582         if (YAHOO.util.Dom.hasClass(cell, cal.Style.CSS_CELL_SELECTABLE)) {
1583                 index = cell.id.split("cell")[1];
1584                 d = cal.cellDates[index];
1585                 date = new Date(d[0],d[1]-1,d[2]);
1586         
1587                 var link;
1589                 if (cal.Options.MULTI_SELECT) {
1590                         link = cell.getElementsByTagName("a")[0];
1591                         if (link) {
1592                                 link.blur();
1593                         }
1595                         var cellDate = cal.cellDates[index];
1596                         var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
1598                         if (cellDateIndex > -1) {       
1599                                 cal.deselectCell(index);
1600                         } else {
1601                                 cal.selectCell(index);
1602                         }       
1604                 } else {
1605                         link = cell.getElementsByTagName("a")[0];
1606                         if (link) {
1607                                 link.blur();
1608                         }
1609                         cal.selectCell(index);
1610                 }
1611         }
1615 * The event that is executed when the user hovers over a cell
1616 * @method doCellMouseOver
1617 * @param {DOMEvent} e   The event
1618 * @param {Calendar} cal A reference to the calendar passed by the Event utility
1620 YAHOO.widget.Calendar.prototype.doCellMouseOver = function(e, cal) {
1621         var target;
1622         if (e) {
1623                 target = YAHOO.util.Event.getTarget(e);
1624         } else {
1625                 target = this;
1626         }
1628         while (target.tagName.toLowerCase() != "td") {
1629                 target = target.parentNode;
1630                 if (target.tagName.toLowerCase() == "html") {
1631                         return;
1632                 }
1633         }
1635         if (YAHOO.util.Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1636                 YAHOO.util.Dom.addClass(target, cal.Style.CSS_CELL_HOVER);
1637         }
1641 * The event that is executed when the user moves the mouse out of a cell
1642 * @method doCellMouseOut
1643 * @param {DOMEvent} e   The event
1644 * @param {Calendar} cal A reference to the calendar passed by the Event utility
1646 YAHOO.widget.Calendar.prototype.doCellMouseOut = function(e, cal) {
1647         var target;
1648         if (e) {
1649                 target = YAHOO.util.Event.getTarget(e);
1650         } else {
1651                 target = this;
1652         }
1654         while (target.tagName.toLowerCase() != "td") {
1655                 target = target.parentNode;
1656                 if (target.tagName.toLowerCase() == "html") {
1657                         return;
1658                 }
1659         }
1661         if (YAHOO.util.Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1662                 YAHOO.util.Dom.removeClass(target, cal.Style.CSS_CELL_HOVER);
1663         }
1666 YAHOO.widget.Calendar.prototype.setupConfig = function() {
1668         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
1670         /**
1671         * The month/year representing the current visible Calendar date (mm/yyyy)
1672         * @config pagedate
1673         * @type String
1674         * @default today's date
1675         */
1676         this.cfg.addProperty(defCfg.PAGEDATE.key, { value:new Date(), handler:this.configPageDate } );
1678         /**
1679         * The date or range of dates representing the current Calendar selection
1680         * @config selected
1681         * @type String
1682         * @default []
1683         */
1684         this.cfg.addProperty(defCfg.SELECTED.key, { value:[], handler:this.configSelected } );
1686         /**
1687         * The title to display above the Calendar's month header
1688         * @config title
1689         * @type String
1690         * @default ""
1691         */
1692         this.cfg.addProperty(defCfg.TITLE.key, { value:defCfg.TITLE.value, handler:this.configTitle } );
1694         /**
1695         * Whether or not a close button should be displayed for this Calendar
1696         * @config close
1697         * @type Boolean
1698         * @default false
1699         */
1700         this.cfg.addProperty(defCfg.CLOSE.key, { value:defCfg.CLOSE.value, handler:this.configClose } );
1702         /**
1703         * 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.
1704         * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be 
1705         * enabled if required.
1706         * 
1707         * @config iframe
1708         * @type Boolean
1709         * @default true for IE6 and below, false for all other browsers
1710         */
1711         this.cfg.addProperty(defCfg.IFRAME.key, { value:defCfg.IFRAME.value, handler:this.configIframe, validator:this.cfg.checkBoolean } );
1713         /**
1714         * The minimum selectable date in the current Calendar (mm/dd/yyyy)
1715         * @config mindate
1716         * @type String
1717         * @default null
1718         */
1719         this.cfg.addProperty(defCfg.MINDATE.key, { value:defCfg.MINDATE.value, handler:this.configMinDate } );
1721         /**
1722         * The maximum selectable date in the current Calendar (mm/dd/yyyy)
1723         * @config maxdate
1724         * @type String
1725         * @default null
1726         */
1727         this.cfg.addProperty(defCfg.MAXDATE.key, { value:defCfg.MAXDATE.value, handler:this.configMaxDate } );
1730         // Options properties
1732         /**
1733         * True if the Calendar should allow multiple selections. False by default.
1734         * @config MULTI_SELECT
1735         * @type Boolean
1736         * @default false
1737         */
1738         this.cfg.addProperty(defCfg.MULTI_SELECT.key,   { value:defCfg.MULTI_SELECT.value, handler:this.configOptions, validator:this.cfg.checkBoolean } );
1740         /**
1741         * The weekday the week begins on. Default is 0 (Sunday).
1742         * @config START_WEEKDAY
1743         * @type number
1744         * @default 0
1745         */
1746         this.cfg.addProperty(defCfg.START_WEEKDAY.key,  { value:defCfg.START_WEEKDAY.value, handler:this.configOptions, validator:this.cfg.checkNumber  } );
1748         /**
1749         * True if the Calendar should show weekday labels. True by default.
1750         * @config SHOW_WEEKDAYS
1751         * @type Boolean
1752         * @default true
1753         */
1754         this.cfg.addProperty(defCfg.SHOW_WEEKDAYS.key,  { value:defCfg.SHOW_WEEKDAYS.value, handler:this.configOptions, validator:this.cfg.checkBoolean  } );
1756         /**
1757         * True if the Calendar should show week row headers. False by default.
1758         * @config SHOW_WEEK_HEADER
1759         * @type Boolean
1760         * @default false
1761         */
1762         this.cfg.addProperty(defCfg.SHOW_WEEK_HEADER.key, { value:defCfg.SHOW_WEEK_HEADER.value, handler:this.configOptions, validator:this.cfg.checkBoolean } );
1764         /**
1765         * True if the Calendar should show week row footers. False by default.
1766         * @config SHOW_WEEK_FOOTER
1767         * @type Boolean
1768         * @default false
1769         */      
1770         this.cfg.addProperty(defCfg.SHOW_WEEK_FOOTER.key,{ value:defCfg.SHOW_WEEK_FOOTER.value, handler:this.configOptions, validator:this.cfg.checkBoolean } );
1772         /**
1773         * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
1774         * @config HIDE_BLANK_WEEKS
1775         * @type Boolean
1776         * @default false
1777         */      
1778         this.cfg.addProperty(defCfg.HIDE_BLANK_WEEKS.key, { value:defCfg.HIDE_BLANK_WEEKS.value, handler:this.configOptions, validator:this.cfg.checkBoolean } );
1779         
1780         /**
1781         * The image that should be used for the left navigation arrow.
1782         * @config NAV_ARROW_LEFT
1783         * @type String
1784         * @deprecated   You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"  
1785         * @default null
1786         */      
1787         this.cfg.addProperty(defCfg.NAV_ARROW_LEFT.key, { value:defCfg.NAV_ARROW_LEFT.value, handler:this.configOptions } );
1789         /**
1790         * The image that should be used for the right navigation arrow.
1791         * @config NAV_ARROW_RIGHT
1792         * @type String
1793         * @deprecated   You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
1794         * @default null
1795         */      
1796         this.cfg.addProperty(defCfg.NAV_ARROW_RIGHT.key, { value:defCfg.NAV_ARROW_RIGHT.value, handler:this.configOptions } );
1798         // Locale properties
1800         /**
1801         * The short month labels for the current locale.
1802         * @config MONTHS_SHORT
1803         * @type String[]
1804         * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
1805         */
1806         this.cfg.addProperty(defCfg.MONTHS_SHORT.key,   { value:defCfg.MONTHS_SHORT.value, handler:this.configLocale } );
1807         
1808         /**
1809         * The long month labels for the current locale.
1810         * @config MONTHS_LONG
1811         * @type String[]
1812         * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
1813         */      
1814         this.cfg.addProperty(defCfg.MONTHS_LONG.key,            { value:defCfg.MONTHS_LONG.value, handler:this.configLocale } );
1815         
1816         /**
1817         * The 1-character weekday labels for the current locale.
1818         * @config WEEKDAYS_1CHAR
1819         * @type String[]
1820         * @default ["S", "M", "T", "W", "T", "F", "S"]
1821         */      
1822         this.cfg.addProperty(defCfg.WEEKDAYS_1CHAR.key, { value:defCfg.WEEKDAYS_1CHAR.value, handler:this.configLocale } );
1823         
1824         /**
1825         * The short weekday labels for the current locale.
1826         * @config WEEKDAYS_SHORT
1827         * @type String[]
1828         * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
1829         */      
1830         this.cfg.addProperty(defCfg.WEEKDAYS_SHORT.key, { value:defCfg.WEEKDAYS_SHORT.value, handler:this.configLocale } );
1831         
1832         /**
1833         * The medium weekday labels for the current locale.
1834         * @config WEEKDAYS_MEDIUM
1835         * @type String[]
1836         * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
1837         */      
1838         this.cfg.addProperty(defCfg.WEEKDAYS_MEDIUM.key,        { value:defCfg.WEEKDAYS_MEDIUM.value, handler:this.configLocale } );
1839         
1840         /**
1841         * The long weekday labels for the current locale.
1842         * @config WEEKDAYS_LONG
1843         * @type String[]
1844         * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
1845         */      
1846         this.cfg.addProperty(defCfg.WEEKDAYS_LONG.key,  { value:defCfg.WEEKDAYS_LONG.value, handler:this.configLocale } );
1848         /**
1849         * Refreshes the locale values used to build the Calendar.
1850         * @method refreshLocale
1851         * @private
1852         */
1853         var refreshLocale = function() {
1854                 this.cfg.refireEvent(defCfg.LOCALE_MONTHS.key);
1855                 this.cfg.refireEvent(defCfg.LOCALE_WEEKDAYS.key);
1856         };
1858         this.cfg.subscribeToConfigEvent(defCfg.START_WEEKDAY.key, refreshLocale, this, true);
1859         this.cfg.subscribeToConfigEvent(defCfg.MONTHS_SHORT.key, refreshLocale, this, true);
1860         this.cfg.subscribeToConfigEvent(defCfg.MONTHS_LONG.key, refreshLocale, this, true);
1861         this.cfg.subscribeToConfigEvent(defCfg.WEEKDAYS_1CHAR.key, refreshLocale, this, true);
1862         this.cfg.subscribeToConfigEvent(defCfg.WEEKDAYS_SHORT.key, refreshLocale, this, true);
1863         this.cfg.subscribeToConfigEvent(defCfg.WEEKDAYS_MEDIUM.key, refreshLocale, this, true);
1864         this.cfg.subscribeToConfigEvent(defCfg.WEEKDAYS_LONG.key, refreshLocale, this, true);
1865         
1866         /**
1867         * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
1868         * @config LOCALE_MONTHS
1869         * @type String
1870         * @default "long"
1871         */      
1872         this.cfg.addProperty(defCfg.LOCALE_MONTHS.key,  { value:defCfg.LOCALE_MONTHS.value, handler:this.configLocaleValues } );
1873         
1874         /**
1875         * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
1876         * @config LOCALE_WEEKDAYS
1877         * @type String
1878         * @default "short"
1879         */      
1880         this.cfg.addProperty(defCfg.LOCALE_WEEKDAYS.key,        { value:defCfg.LOCALE_WEEKDAYS.value, handler:this.configLocaleValues } );
1882         /**
1883         * The value used to delimit individual dates in a date string passed to various Calendar functions.
1884         * @config DATE_DELIMITER
1885         * @type String
1886         * @default ","
1887         */      
1888         this.cfg.addProperty(defCfg.DATE_DELIMITER.key,         { value:defCfg.DATE_DELIMITER.value, handler:this.configLocale } );
1890         /**
1891         * The value used to delimit date fields in a date string passed to various Calendar functions.
1892         * @config DATE_FIELD_DELIMITER
1893         * @type String
1894         * @default "/"
1895         */      
1896         this.cfg.addProperty(defCfg.DATE_FIELD_DELIMITER.key, { value:defCfg.DATE_FIELD_DELIMITER.value, handler:this.configLocale } );
1898         /**
1899         * The value used to delimit date ranges in a date string passed to various Calendar functions.
1900         * @config DATE_RANGE_DELIMITER
1901         * @type String
1902         * @default "-"
1903         */
1904         this.cfg.addProperty(defCfg.DATE_RANGE_DELIMITER.key, { value:defCfg.DATE_RANGE_DELIMITER.value, handler:this.configLocale } );
1906         /**
1907         * The position of the month in a month/year date string
1908         * @config MY_MONTH_POSITION
1909         * @type Number
1910         * @default 1
1911         */
1912         this.cfg.addProperty(defCfg.MY_MONTH_POSITION.key,      { value:defCfg.MY_MONTH_POSITION.value, handler:this.configLocale, validator:this.cfg.checkNumber } );
1914         /**
1915         * The position of the year in a month/year date string
1916         * @config MY_YEAR_POSITION
1917         * @type Number
1918         * @default 2
1919         */
1920         this.cfg.addProperty(defCfg.MY_YEAR_POSITION.key,       { value:defCfg.MY_YEAR_POSITION.value, handler:this.configLocale, validator:this.cfg.checkNumber } );
1922         /**
1923         * The position of the month in a month/day date string
1924         * @config MD_MONTH_POSITION
1925         * @type Number
1926         * @default 1
1927         */
1928         this.cfg.addProperty(defCfg.MD_MONTH_POSITION.key,      { value:defCfg.MD_MONTH_POSITION.value, handler:this.configLocale, validator:this.cfg.checkNumber } );
1930         /**
1931         * The position of the day in a month/year date string
1932         * @config MD_DAY_POSITION
1933         * @type Number
1934         * @default 2
1935         */
1936         this.cfg.addProperty(defCfg.MD_DAY_POSITION.key,                { value:defCfg.MD_DAY_POSITION.value, handler:this.configLocale, validator:this.cfg.checkNumber } );
1938         /**
1939         * The position of the month in a month/day/year date string
1940         * @config MDY_MONTH_POSITION
1941         * @type Number
1942         * @default 1
1943         */
1944         this.cfg.addProperty(defCfg.MDY_MONTH_POSITION.key,     { value:defCfg.MDY_MONTH_POSITION.value, handler:this.configLocale, validator:this.cfg.checkNumber } );
1946         /**
1947         * The position of the day in a month/day/year date string
1948         * @config MDY_DAY_POSITION
1949         * @type Number
1950         * @default 2
1951         */
1952         this.cfg.addProperty(defCfg.MDY_DAY_POSITION.key,       { value:defCfg.MDY_DAY_POSITION.value, handler:this.configLocale, validator:this.cfg.checkNumber } );
1954         /**
1955         * The position of the year in a month/day/year date string
1956         * @config MDY_YEAR_POSITION
1957         * @type Number
1958         * @default 3
1959         */
1960         this.cfg.addProperty(defCfg.MDY_YEAR_POSITION.key,      { value:defCfg.MDY_YEAR_POSITION.value, handler:this.configLocale, validator:this.cfg.checkNumber } );
1961         
1962         /**
1963         * The position of the month in the month year label string used as the Calendar header
1964         * @config MY_LABEL_MONTH_POSITION
1965         * @type Number
1966         * @default 1
1967         */
1968         this.cfg.addProperty(defCfg.MY_LABEL_MONTH_POSITION.key,        { value:defCfg.MY_LABEL_MONTH_POSITION.value, handler:this.configLocale, validator:this.cfg.checkNumber } );
1970         /**
1971         * The position of the year in the month year label string used as the Calendar header
1972         * @config MY_LABEL_YEAR_POSITION
1973         * @type Number
1974         * @default 2
1975         */
1976         this.cfg.addProperty(defCfg.MY_LABEL_YEAR_POSITION.key, { value:defCfg.MY_LABEL_YEAR_POSITION.value, handler:this.configLocale, validator:this.cfg.checkNumber } );
1977         
1978         /**
1979         * The suffix used after the month when rendering the Calendar header
1980         * @config MY_LABEL_MONTH_SUFFIX
1981         * @type String
1982         * @default " "
1983         */
1984         this.cfg.addProperty(defCfg.MY_LABEL_MONTH_SUFFIX.key,  { value:defCfg.MY_LABEL_MONTH_SUFFIX.value, handler:this.configLocale } );
1985         
1986         /**
1987         * The suffix used after the year when rendering the Calendar header
1988         * @config MY_LABEL_YEAR_SUFFIX
1989         * @type String
1990         * @default ""
1991         */
1992         this.cfg.addProperty(defCfg.MY_LABEL_YEAR_SUFFIX.key, { value:defCfg.MY_LABEL_YEAR_SUFFIX.value, handler:this.configLocale } );
1996 * The default handler for the "pagedate" property
1997 * @method configPageDate
1999 YAHOO.widget.Calendar.prototype.configPageDate = function(type, args, obj) {
2000         this.cfg.setProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.PAGEDATE.key, this._parsePageDate(args[0]), true);
2004 * The default handler for the "mindate" property
2005 * @method configMinDate
2007 YAHOO.widget.Calendar.prototype.configMinDate = function(type, args, obj) {
2008         var val = args[0];
2009         if (YAHOO.lang.isString(val)) {
2010                 val = this._parseDate(val);
2011                 this.cfg.setProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.MINDATE.key, new Date(val[0],(val[1]-1),val[2]));
2012         }
2016 * The default handler for the "maxdate" property
2017 * @method configMaxDate
2019 YAHOO.widget.Calendar.prototype.configMaxDate = function(type, args, obj) {
2020         var val = args[0];
2021         if (YAHOO.lang.isString(val)) {
2022                 val = this._parseDate(val);
2023                 this.cfg.setProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.MAXDATE.key, new Date(val[0],(val[1]-1),val[2]));
2024         }
2028 * The default handler for the "selected" property
2029 * @method configSelected
2031 YAHOO.widget.Calendar.prototype.configSelected = function(type, args, obj) {
2032         var selected = args[0];
2033         var cfgSelected = YAHOO.widget.Calendar._DEFAULT_CONFIG.SELECTED.key;
2034         
2035         if (selected) {
2036                 if (YAHOO.lang.isString(selected)) {
2037                         this.cfg.setProperty(cfgSelected, this._parseDates(selected), true);
2038                 } 
2039         }
2040         if (! this._selectedDates) {
2041                 this._selectedDates = this.cfg.getProperty(cfgSelected);
2042         }
2046 * The default handler for all configuration options properties
2047 * @method configOptions
2049 YAHOO.widget.Calendar.prototype.configOptions = function(type, args, obj) {
2050         this.Options[type.toUpperCase()] = args[0];
2054 * The default handler for all configuration locale properties
2055 * @method configLocale
2057 YAHOO.widget.Calendar.prototype.configLocale = function(type, args, obj) {
2058         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
2059         this.Locale[type.toUpperCase()] = args[0];
2061         this.cfg.refireEvent(defCfg.LOCALE_MONTHS.key);
2062         this.cfg.refireEvent(defCfg.LOCALE_WEEKDAYS.key);
2066 * The default handler for all configuration locale field length properties
2067 * @method configLocaleValues
2069 YAHOO.widget.Calendar.prototype.configLocaleValues = function(type, args, obj) {
2070         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG; 
2072         type = type.toLowerCase();
2073         var val = args[0];
2075         switch (type) {
2076                 case defCfg.LOCALE_MONTHS.key:
2077                         switch (val) {
2078                                 case YAHOO.widget.Calendar.SHORT:
2079                                         this.Locale.LOCALE_MONTHS = this.cfg.getProperty(defCfg.MONTHS_SHORT.key).concat();
2080                                         break;
2081                                 case YAHOO.widget.Calendar.LONG:
2082                                         this.Locale.LOCALE_MONTHS = this.cfg.getProperty(defCfg.MONTHS_LONG.key).concat();
2083                                         break;
2084                         }
2085                         break;
2086                 case defCfg.LOCALE_WEEKDAYS.key:
2087                         switch (val) {
2088                                 case YAHOO.widget.Calendar.ONE_CHAR:
2089                                         this.Locale.LOCALE_WEEKDAYS = this.cfg.getProperty(defCfg.WEEKDAYS_1CHAR.key).concat();
2090                                         break;
2091                                 case YAHOO.widget.Calendar.SHORT:
2092                                         this.Locale.LOCALE_WEEKDAYS = this.cfg.getProperty(defCfg.WEEKDAYS_SHORT.key).concat();
2093                                         break;
2094                                 case YAHOO.widget.Calendar.MEDIUM:
2095                                         this.Locale.LOCALE_WEEKDAYS = this.cfg.getProperty(defCfg.WEEKDAYS_MEDIUM.key).concat();
2096                                         break;
2097                                 case YAHOO.widget.Calendar.LONG:
2098                                         this.Locale.LOCALE_WEEKDAYS = this.cfg.getProperty(defCfg.WEEKDAYS_LONG.key).concat();
2099                                         break;
2100                         }
2101                         
2102                         var START_WEEKDAY = this.cfg.getProperty(defCfg.START_WEEKDAY.key);
2104                         if (START_WEEKDAY > 0) {
2105                                 for (var w=0;w<START_WEEKDAY;++w) {
2106                                         this.Locale.LOCALE_WEEKDAYS.push(this.Locale.LOCALE_WEEKDAYS.shift());
2107                                 }
2108                         }
2109                         break;
2110         }
2114 * Defines the style constants for the Calendar
2115 * @method initStyles
2117 YAHOO.widget.Calendar.prototype.initStyles = function() {
2119         var defStyle = YAHOO.widget.Calendar._STYLES;
2121         this.Style = {
2122                 /**
2123                 * @property Style.CSS_ROW_HEADER
2124                 */
2125                 CSS_ROW_HEADER: defStyle.CSS_ROW_HEADER,
2126                 /**
2127                 * @property Style.CSS_ROW_FOOTER
2128                 */
2129                 CSS_ROW_FOOTER: defStyle.CSS_ROW_FOOTER,
2130                 /**
2131                 * @property Style.CSS_CELL
2132                 */
2133                 CSS_CELL : defStyle.CSS_CELL,
2134                 /**
2135                 * @property Style.CSS_CELL_SELECTOR
2136                 */
2137                 CSS_CELL_SELECTOR : defStyle.CSS_CELL_SELECTOR,
2138                 /**
2139                 * @property Style.CSS_CELL_SELECTED
2140                 */
2141                 CSS_CELL_SELECTED : defStyle.CSS_CELL_SELECTED,
2142                 /**
2143                 * @property Style.CSS_CELL_SELECTABLE
2144                 */
2145                 CSS_CELL_SELECTABLE : defStyle.CSS_CELL_SELECTABLE,
2146                 /**
2147                 * @property Style.CSS_CELL_RESTRICTED
2148                 */
2149                 CSS_CELL_RESTRICTED : defStyle.CSS_CELL_RESTRICTED,
2150                 /**
2151                 * @property Style.CSS_CELL_TODAY
2152                 */
2153                 CSS_CELL_TODAY : defStyle.CSS_CELL_TODAY,
2154                 /**
2155                 * @property Style.CSS_CELL_OOM
2156                 */
2157                 CSS_CELL_OOM : defStyle.CSS_CELL_OOM,
2158                 /**
2159                 * @property Style.CSS_CELL_OOB
2160                 */
2161                 CSS_CELL_OOB : defStyle.CSS_CELL_OOB,
2162                 /**
2163                 * @property Style.CSS_HEADER
2164                 */
2165                 CSS_HEADER : defStyle.CSS_HEADER,
2166                 /**
2167                 * @property Style.CSS_HEADER_TEXT
2168                 */
2169                 CSS_HEADER_TEXT : defStyle.CSS_HEADER_TEXT,
2170                 /**
2171                 * @property Style.CSS_BODY
2172                 */
2173                 CSS_BODY : defStyle.CSS_BODY,
2174                 /**
2175                 * @property Style.CSS_WEEKDAY_CELL
2176                 */
2177                 CSS_WEEKDAY_CELL : defStyle.CSS_WEEKDAY_CELL,
2178                 /**
2179                 * @property Style.CSS_WEEKDAY_ROW
2180                 */
2181                 CSS_WEEKDAY_ROW : defStyle.CSS_WEEKDAY_ROW,
2182                 /**
2183                 * @property Style.CSS_FOOTER
2184                 */
2185                 CSS_FOOTER : defStyle.CSS_FOOTER,
2186                 /**
2187                 * @property Style.CSS_CALENDAR
2188                 */
2189                 CSS_CALENDAR : defStyle.CSS_CALENDAR,
2190                 /**
2191                 * @property Style.CSS_SINGLE
2192                 */
2193                 CSS_SINGLE : defStyle.CSS_SINGLE,
2194                 /**
2195                 * @property Style.CSS_CONTAINER
2196                 */
2197                 CSS_CONTAINER : defStyle.CSS_CONTAINER,
2198                 /**
2199                 * @property Style.CSS_NAV_LEFT
2200                 */
2201                 CSS_NAV_LEFT : defStyle.CSS_NAV_LEFT,
2202                 /**
2203                 * @property Style.CSS_NAV_RIGHT
2204                 */
2205                 CSS_NAV_RIGHT : defStyle.CSS_NAV_RIGHT,
2206                 /**
2207                 * @property Style.CSS_CLOSE
2208                 */
2209                 CSS_CLOSE : defStyle.CSS_CLOSE,
2210                 /**
2211                 * @property Style.CSS_CELL_TOP
2212                 */
2213                 CSS_CELL_TOP : defStyle.CSS_CELL_TOP,
2214                 /**
2215                 * @property Style.CSS_CELL_LEFT
2216                 */
2217                 CSS_CELL_LEFT : defStyle.CSS_CELL_LEFT,
2218                 /**
2219                 * @property Style.CSS_CELL_RIGHT
2220                 */
2221                 CSS_CELL_RIGHT : defStyle.CSS_CELL_RIGHT,
2222                 /**
2223                 * @property Style.CSS_CELL_BOTTOM
2224                 */
2225                 CSS_CELL_BOTTOM : defStyle.CSS_CELL_BOTTOM,
2226                 /**
2227                 * @property Style.CSS_CELL_HOVER
2228                 */
2229                 CSS_CELL_HOVER : defStyle.CSS_CELL_HOVER,
2230                 /**
2231                 * @property Style.CSS_CELL_HIGHLIGHT1
2232                 */
2233                 CSS_CELL_HIGHLIGHT1 : defStyle.CSS_CELL_HIGHLIGHT1,
2234                 /**
2235                 * @property Style.CSS_CELL_HIGHLIGHT2
2236                 */
2237                 CSS_CELL_HIGHLIGHT2 : defStyle.CSS_CELL_HIGHLIGHT2,
2238                 /**
2239                 * @property Style.CSS_CELL_HIGHLIGHT3
2240                 */
2241                 CSS_CELL_HIGHLIGHT3 : defStyle.CSS_CELL_HIGHLIGHT3,
2242                 /**
2243                 * @property Style.CSS_CELL_HIGHLIGHT4
2244                 */
2245                 CSS_CELL_HIGHLIGHT4 : defStyle.CSS_CELL_HIGHLIGHT4
2246         };
2250 * Builds the date label that will be displayed in the calendar header or
2251 * footer, depending on configuration.
2252 * @method buildMonthLabel
2253 * @return       {String}        The formatted calendar month label
2255 YAHOO.widget.Calendar.prototype.buildMonthLabel = function() {
2256         var pageDate = this.cfg.getProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.PAGEDATE.key);
2258         var monthLabel  = this.Locale.LOCALE_MONTHS[pageDate.getMonth()] + this.Locale.MY_LABEL_MONTH_SUFFIX;
2259         var yearLabel = pageDate.getFullYear() + this.Locale.MY_LABEL_YEAR_SUFFIX;
2261         if (this.Locale.MY_LABEL_MONTH_POSITION == 2 || this.Locale.MY_LABEL_YEAR_POSITION == 1) {
2262                 return yearLabel + monthLabel;
2263         } else {
2264                 return monthLabel + yearLabel;
2265         }
2269 * Builds the date digit that will be displayed in calendar cells
2270 * @method buildDayLabel
2271 * @param {Date} workingDate     The current working date
2272 * @return       {String}        The formatted day label
2274 YAHOO.widget.Calendar.prototype.buildDayLabel = function(workingDate) {
2275         return workingDate.getDate();
2279 * Renders the calendar header.
2280 * @method renderHeader
2281 * @param {Array}        html    The current working HTML array
2282 * @return {Array} The current working HTML array
2284 YAHOO.widget.Calendar.prototype.renderHeader = function(html) {
2285         var colSpan = 7;
2286         
2287         var DEPR_NAV_LEFT = "us/tr/callt.gif";
2288         var DEPR_NAV_RIGHT = "us/tr/calrt.gif"; 
2289         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
2290         
2291         if (this.cfg.getProperty(defCfg.SHOW_WEEK_HEADER.key)) {
2292                 colSpan += 1;
2293         }
2295         if (this.cfg.getProperty(defCfg.SHOW_WEEK_FOOTER.key)) {
2296                 colSpan += 1;
2297         }
2299         html[html.length] = "<thead>";
2300         html[html.length] =             "<tr>";
2301         html[html.length] =                     '<th colspan="' + colSpan + '" class="' + this.Style.CSS_HEADER_TEXT + '">';
2302         html[html.length] =                             '<div class="' + this.Style.CSS_HEADER + '">';
2304         var renderLeft, renderRight = false;
2306         if (this.parent) {
2307                 if (this.index === 0) {
2308                         renderLeft = true;
2309                 }
2310                 if (this.index == (this.parent.cfg.getProperty("pages") -1)) {
2311                         renderRight = true;
2312                 }
2313         } else {
2314                 renderLeft = true;
2315                 renderRight = true;
2316         }
2318         var cal = this.parent || this;
2319         
2320         if (renderLeft) {
2321                 var leftArrow = this.cfg.getProperty(defCfg.NAV_ARROW_LEFT.key);
2322                 // 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
2323                 if (leftArrow === null && YAHOO.widget.Calendar.IMG_ROOT !== null) {
2324                         leftArrow = YAHOO.widget.Calendar.IMG_ROOT + DEPR_NAV_LEFT;
2325                 }
2326                 var leftStyle = (leftArrow === null) ? "" : ' style="background-image:url(' + leftArrow + ')"';
2327                 html[html.length] = '<a class="' + this.Style.CSS_NAV_LEFT + '"' + leftStyle + ' >&#160;</a>';
2328         }
2329         
2330         html[html.length] = this.buildMonthLabel();
2331         
2332         if (renderRight) {
2333                 var rightArrow = this.cfg.getProperty(defCfg.NAV_ARROW_RIGHT.key);
2334                 if (rightArrow === null && YAHOO.widget.Calendar.IMG_ROOT !== null) {
2335                         rightArrow = YAHOO.widget.Calendar.IMG_ROOT + DEPR_NAV_RIGHT;
2336                 }
2337                 var rightStyle = (rightArrow === null) ? "" : ' style="background-image:url(' + rightArrow + ')"';
2338                 html[html.length] = '<a class="' + this.Style.CSS_NAV_RIGHT + '"' + rightStyle + ' >&#160;</a>';
2339         }
2341         html[html.length] =     '</div>\n</th>\n</tr>';
2343         if (this.cfg.getProperty(defCfg.SHOW_WEEKDAYS.key)) {
2344                 html = this.buildWeekdays(html);
2345         }
2346         
2347         html[html.length] = '</thead>';
2349         return html;
2353 * Renders the Calendar's weekday headers.
2354 * @method buildWeekdays
2355 * @param {Array}        html    The current working HTML array
2356 * @return {Array} The current working HTML array
2358 YAHOO.widget.Calendar.prototype.buildWeekdays = function(html) {
2360         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
2362         html[html.length] = '<tr class="' + this.Style.CSS_WEEKDAY_ROW + '">';
2364         if (this.cfg.getProperty(defCfg.SHOW_WEEK_HEADER.key)) {
2365                 html[html.length] = '<th>&#160;</th>';
2366         }
2368         for(var i=0;i<this.Locale.LOCALE_WEEKDAYS.length;++i) {
2369                 html[html.length] = '<th class="calweekdaycell">' + this.Locale.LOCALE_WEEKDAYS[i] + '</th>';
2370         }
2372         if (this.cfg.getProperty(defCfg.SHOW_WEEK_FOOTER.key)) {
2373                 html[html.length] = '<th>&#160;</th>';
2374         }
2376         html[html.length] = '</tr>';
2378         return html;
2382 * Renders the calendar body.
2383 * @method renderBody
2384 * @param {Date} workingDate     The current working Date being used for the render process
2385 * @param {Array}        html    The current working HTML array
2386 * @return {Array} The current working HTML array
2388 YAHOO.widget.Calendar.prototype.renderBody = function(workingDate, html) {
2389         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
2391         var startDay = this.cfg.getProperty(defCfg.START_WEEKDAY.key);
2393         this.preMonthDays = workingDate.getDay();
2394         if (startDay > 0) {
2395                 this.preMonthDays -= startDay;
2396         }
2397         if (this.preMonthDays < 0) {
2398                 this.preMonthDays += 7;
2399         }
2400         
2401         this.monthDays = YAHOO.widget.DateMath.findMonthEnd(workingDate).getDate();
2402         this.postMonthDays = YAHOO.widget.Calendar.DISPLAY_DAYS-this.preMonthDays-this.monthDays;
2403         
2404         workingDate = YAHOO.widget.DateMath.subtract(workingDate, YAHOO.widget.DateMath.DAY, this.preMonthDays);
2406         var weekNum,weekClass;
2407         var weekPrefix = "w";
2408         var cellPrefix = "_cell";
2409         var workingDayPrefix = "wd";
2410         var dayPrefix = "d";
2411         
2412         var cellRenderers;
2413         var renderer;
2414         
2415         var todayYear = this.today.getFullYear();
2416         var todayMonth = this.today.getMonth();
2417         var todayDate = this.today.getDate();
2418         
2419         var useDate = this.cfg.getProperty(defCfg.PAGEDATE.key);
2420         var hideBlankWeeks = this.cfg.getProperty(defCfg.HIDE_BLANK_WEEKS.key);
2421         var showWeekFooter = this.cfg.getProperty(defCfg.SHOW_WEEK_FOOTER.key);
2422         var showWeekHeader = this.cfg.getProperty(defCfg.SHOW_WEEK_HEADER.key);
2423         var mindate = this.cfg.getProperty(defCfg.MINDATE.key);
2424         var maxdate = this.cfg.getProperty(defCfg.MAXDATE.key);
2426         if (mindate) {
2427                 mindate = YAHOO.widget.DateMath.clearTime(mindate);
2428         }
2429         if (maxdate) {
2430                 maxdate = YAHOO.widget.DateMath.clearTime(maxdate);
2431         }
2432         
2433         html[html.length] = '<tbody class="m' + (useDate.getMonth()+1) + ' ' + this.Style.CSS_BODY + '">';
2434         
2435         var i = 0;
2437         var tempDiv = document.createElement("div");
2438         var cell = document.createElement("td");
2439         tempDiv.appendChild(cell);
2441         var jan1 = new Date(useDate.getFullYear(),0,1);
2443         var cal = this.parent || this;
2445         for (var r=0;r<6;r++) {
2447                 weekNum = YAHOO.widget.DateMath.getWeekNumber(workingDate, useDate.getFullYear(), startDay);
2448                 weekClass = weekPrefix + weekNum;
2450                 // Local OOM check for performance, since we already have pagedate
2451                 if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth()) {
2452                         break;
2453                 } else {
2455                         html[html.length] = '<tr class="' + weekClass + '">';
2456                         
2457                         if (showWeekHeader) { html = this.renderRowHeader(weekNum, html); }
2458                         
2459                         for (var d=0;d<7;d++){ // Render actual days
2461                                 cellRenderers = [];
2462                                 renderer = null;
2464                                 this.clearElement(cell);
2465                                 cell.className = this.Style.CSS_CELL;
2466                                 cell.id = this.id + cellPrefix + i;
2468                                 if (workingDate.getDate()               == todayDate && 
2469                                         workingDate.getMonth()          == todayMonth &&
2470                                         workingDate.getFullYear()       == todayYear) {
2471                                         cellRenderers[cellRenderers.length]=cal.renderCellStyleToday;
2472                                 }
2473                                 
2474                                 var workingArray = [workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()];
2475                                 this.cellDates[this.cellDates.length] = workingArray; // Add this date to cellDates
2476                                 
2477                                 // Local OOM check for performance, since we already have pagedate
2478                                 if (workingDate.getMonth() != useDate.getMonth()) {
2479                                         cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth;
2480                                 } else {
2481                                         YAHOO.util.Dom.addClass(cell, workingDayPrefix + workingDate.getDay());
2482                                         YAHOO.util.Dom.addClass(cell, dayPrefix + workingDate.getDate());
2483                                 
2484                                         for (var s=0;s<this.renderStack.length;++s) {
2486                                                 var rArray = this.renderStack[s];
2487                                                 var type = rArray[0];
2488                                                 
2489                                                 var month;
2490                                                 var day;
2491                                                 var year;
2492                                                 
2493                                                 switch (type) {
2494                                                         case YAHOO.widget.Calendar.DATE:
2495                                                                 month = rArray[1][1];
2496                                                                 day = rArray[1][2];
2497                                                                 year = rArray[1][0];
2499                                                                 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == year) {
2500                                                                         renderer = rArray[2];
2501                                                                         this.renderStack.splice(s,1);
2502                                                                 }
2503                                                                 break;
2504                                                         case YAHOO.widget.Calendar.MONTH_DAY:
2505                                                                 month = rArray[1][0];
2506                                                                 day = rArray[1][1];
2507                                                                 
2508                                                                 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day) {
2509                                                                         renderer = rArray[2];
2510                                                                         this.renderStack.splice(s,1);
2511                                                                 }
2512                                                                 break;
2513                                                         case YAHOO.widget.Calendar.RANGE:
2514                                                                 var date1 = rArray[1][0];
2515                                                                 var date2 = rArray[1][1];
2517                                                                 var d1month = date1[1];
2518                                                                 var d1day = date1[2];
2519                                                                 var d1year = date1[0];
2520                                                                 
2521                                                                 var d1 = new Date(d1year, d1month-1, d1day);
2523                                                                 var d2month = date2[1];
2524                                                                 var d2day = date2[2];
2525                                                                 var d2year = date2[0];
2527                                                                 var d2 = new Date(d2year, d2month-1, d2day);
2529                                                                 if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime()) {
2530                                                                         renderer = rArray[2];
2532                                                                         if (workingDate.getTime()==d2.getTime()) { 
2533                                                                                 this.renderStack.splice(s,1);
2534                                                                         }
2535                                                                 }
2536                                                                 break;
2537                                                         case YAHOO.widget.Calendar.WEEKDAY:
2538                                                                 
2539                                                                 var weekday = rArray[1][0];
2540                                                                 if (workingDate.getDay()+1 == weekday) {
2541                                                                         renderer = rArray[2];
2542                                                                 }
2543                                                                 break;
2544                                                         case YAHOO.widget.Calendar.MONTH:
2545                                                                 
2546                                                                 month = rArray[1][0];
2547                                                                 if (workingDate.getMonth()+1 == month) {
2548                                                                         renderer = rArray[2];
2549                                                                 }
2550                                                                 break;
2551                                                 }
2552                                                 
2553                                                 if (renderer) {
2554                                                         cellRenderers[cellRenderers.length]=renderer;
2555                                                 }
2556                                         }
2558                                 }
2560                                 if (this._indexOfSelectedFieldArray(workingArray) > -1) {
2561                                         cellRenderers[cellRenderers.length]=cal.renderCellStyleSelected; 
2562                                 }
2564                                 if ((mindate && (workingDate.getTime() < mindate.getTime())) ||
2565                                         (maxdate && (workingDate.getTime() > maxdate.getTime()))
2566                                 ) {
2567                                         cellRenderers[cellRenderers.length]=cal.renderOutOfBoundsDate;
2568                                 } else {
2569                                         cellRenderers[cellRenderers.length]=cal.styleCellDefault;
2570                                         cellRenderers[cellRenderers.length]=cal.renderCellDefault;      
2571                                 }
2572                                 
2573                                 for (var x=0; x < cellRenderers.length; ++x) {
2574                                         if (cellRenderers[x].call(cal, workingDate, cell) == YAHOO.widget.Calendar.STOP_RENDER) {
2575                                                 break;
2576                                         }
2577                                 }
2579                                 workingDate.setTime(workingDate.getTime() + YAHOO.widget.DateMath.ONE_DAY_MS);
2581                                 if (i >= 0 && i <= 6) {
2582                                         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_TOP);
2583                                 }
2584                                 if ((i % 7) === 0) {
2585                                         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_LEFT);
2586                                 }
2587                                 if (((i+1) % 7) === 0) {
2588                                         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_RIGHT);
2589                                 }
2590                                 
2591                                 var postDays = this.postMonthDays; 
2592                                 if (hideBlankWeeks && postDays >= 7) {
2593                                         var blankWeeks = Math.floor(postDays/7);
2594                                         for (var p=0;p<blankWeeks;++p) {
2595                                                 postDays -= 7;
2596                                         }
2597                                 }
2598                                 
2599                                 if (i >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
2600                                         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM);
2601                                 }
2603                                 html[html.length] = tempDiv.innerHTML;
2604                                 i++;
2605                         }
2607                         if (showWeekFooter) { html = this.renderRowFooter(weekNum, html); }
2609                         html[html.length] = '</tr>';
2610                 }
2611         }
2613         html[html.length] = '</tbody>';
2615         return html;
2619 * Renders the calendar footer. In the default implementation, there is
2620 * no footer.
2621 * @method renderFooter
2622 * @param {Array}        html    The current working HTML array
2623 * @return {Array} The current working HTML array
2625 YAHOO.widget.Calendar.prototype.renderFooter = function(html) { return html; };
2628 * Renders the calendar after it has been configured. The render() method has a specific call chain that will execute
2629 * when the method is called: renderHeader, renderBody, renderFooter.
2630 * Refer to the documentation for those methods for information on 
2631 * individual render tasks.
2632 * @method render
2634 YAHOO.widget.Calendar.prototype.render = function() {
2635         this.beforeRenderEvent.fire();
2637         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
2639         // Find starting day of the current month
2640         var workingDate = YAHOO.widget.DateMath.findMonthStart(this.cfg.getProperty(defCfg.PAGEDATE.key));
2642         this.resetRenderers();
2643         this.cellDates.length = 0;
2645         YAHOO.util.Event.purgeElement(this.oDomContainer, true);
2647         var html = [];
2649         html[html.length] = '<table cellSpacing="0" class="' + this.Style.CSS_CALENDAR + ' y' + workingDate.getFullYear() + '" id="' + this.id + '">';
2650         html = this.renderHeader(html);
2651         html = this.renderBody(workingDate, html);
2652         html = this.renderFooter(html);
2653         html[html.length] = '</table>';
2655         this.oDomContainer.innerHTML = html.join("\n");
2657         this.applyListeners();
2658         this.cells = this.oDomContainer.getElementsByTagName("td");
2660         this.cfg.refireEvent(defCfg.TITLE.key);
2661         this.cfg.refireEvent(defCfg.CLOSE.key);
2662         this.cfg.refireEvent(defCfg.IFRAME.key);
2664         this.renderEvent.fire();
2668 * Applies the Calendar's DOM listeners to applicable elements.
2669 * @method applyListeners
2671 YAHOO.widget.Calendar.prototype.applyListeners = function() {
2672         
2673         var root = this.oDomContainer;
2674         var cal = this.parent || this;
2675         
2676         var anchor = "a";
2677         var mousedown = "mousedown";
2679         var linkLeft = YAHOO.util.Dom.getElementsByClassName(this.Style.CSS_NAV_LEFT, anchor, root);
2680         var linkRight = YAHOO.util.Dom.getElementsByClassName(this.Style.CSS_NAV_RIGHT, anchor, root);
2682         if (linkLeft && linkLeft.length > 0) {
2683                 this.linkLeft = linkLeft[0];
2684                 YAHOO.util.Event.addListener(this.linkLeft, mousedown, cal.previousMonth, cal, true);
2685         }
2687         if (linkRight && linkRight.length > 0) {
2688                 this.linkRight = linkRight[0];
2689                 YAHOO.util.Event.addListener(this.linkRight, mousedown, cal.nextMonth, cal, true);
2690         }
2692         if (this.domEventMap) {
2693                 var el,elements;
2694                 for (var cls in this.domEventMap) {     
2695                         if (YAHOO.lang.hasOwnProperty(this.domEventMap, cls)) {
2696                                 var items = this.domEventMap[cls];
2698                                 if (! (items instanceof Array)) {
2699                                         items = [items];
2700                                 }
2702                                 for (var i=0;i<items.length;i++)        {
2703                                         var item = items[i];
2704                                         elements = YAHOO.util.Dom.getElementsByClassName(cls, item.tag, this.oDomContainer);
2706                                         for (var c=0;c<elements.length;c++) {
2707                                                 el = elements[c];
2708                                                  YAHOO.util.Event.addListener(el, item.event, item.handler, item.scope, item.correct );
2709                                         }
2710                                 }
2711                         }
2712                 }
2713         }
2715         YAHOO.util.Event.addListener(this.oDomContainer, "click", this.doSelectCell, this);
2716         YAHOO.util.Event.addListener(this.oDomContainer, "mouseover", this.doCellMouseOver, this);
2717         YAHOO.util.Event.addListener(this.oDomContainer, "mouseout", this.doCellMouseOut, this);
2721 * Retrieves the Date object for the specified Calendar cell
2722 * @method getDateByCellId
2723 * @param {String}       id      The id of the cell
2724 * @return {Date} The Date object for the specified Calendar cell
2726 YAHOO.widget.Calendar.prototype.getDateByCellId = function(id) {
2727         var date = this.getDateFieldsByCellId(id);
2728         return new Date(date[0],date[1]-1,date[2]);
2732 * Retrieves the Date object for the specified Calendar cell
2733 * @method getDateFieldsByCellId
2734 * @param {String}       id      The id of the cell
2735 * @return {Array}       The array of Date fields for the specified Calendar cell
2737 YAHOO.widget.Calendar.prototype.getDateFieldsByCellId = function(id) {
2738         id = id.toLowerCase().split("_cell")[1];
2739         id = parseInt(id, 10);
2740         return this.cellDates[id];
2743 // BEGIN BUILT-IN TABLE CELL RENDERERS
2746 * Renders a cell that falls before the minimum date or after the maximum date.
2747 * widget class.
2748 * @method renderOutOfBoundsDate
2749 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2750 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2751 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
2752 *                       should not be terminated
2754 YAHOO.widget.Calendar.prototype.renderOutOfBoundsDate = function(workingDate, cell) {
2755         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOB);
2756         cell.innerHTML = workingDate.getDate();
2757         return YAHOO.widget.Calendar.STOP_RENDER;
2761 * Renders the row header for a week.
2762 * @method renderRowHeader
2763 * @param {Number}       weekNum The week number of the current row
2764 * @param {Array}        cell    The current working HTML array
2766 YAHOO.widget.Calendar.prototype.renderRowHeader = function(weekNum, html) {
2767         html[html.length] = '<th class="calrowhead">' + weekNum + '</th>';
2768         return html;
2772 * Renders the row footer for a week.
2773 * @method renderRowFooter
2774 * @param {Number}       weekNum The week number of the current row
2775 * @param {Array}        cell    The current working HTML array
2777 YAHOO.widget.Calendar.prototype.renderRowFooter = function(weekNum, html) {
2778         html[html.length] = '<th class="calrowfoot">' + weekNum + '</th>';
2779         return html;
2783 * Renders a single standard calendar cell in the calendar widget table.
2784 * All logic for determining how a standard default cell will be rendered is 
2785 * encapsulated in this method, and must be accounted for when extending the
2786 * widget class.
2787 * @method renderCellDefault
2788 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2789 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2791 YAHOO.widget.Calendar.prototype.renderCellDefault = function(workingDate, cell) {
2792         cell.innerHTML = '<a href="#" class="' + this.Style.CSS_CELL_SELECTOR + '">' + this.buildDayLabel(workingDate) + "</a>";
2796 * Styles a selectable cell.
2797 * @method styleCellDefault
2798 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2799 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2801 YAHOO.widget.Calendar.prototype.styleCellDefault = function(workingDate, cell) {
2802         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_SELECTABLE);
2807 * Renders a single standard calendar cell using the CSS hightlight1 style
2808 * @method renderCellStyleHighlight1
2809 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2810 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2812 YAHOO.widget.Calendar.prototype.renderCellStyleHighlight1 = function(workingDate, cell) {
2813         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
2817 * Renders a single standard calendar cell using the CSS hightlight2 style
2818 * @method renderCellStyleHighlight2
2819 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2820 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2822 YAHOO.widget.Calendar.prototype.renderCellStyleHighlight2 = function(workingDate, cell) {
2823         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
2827 * Renders a single standard calendar cell using the CSS hightlight3 style
2828 * @method renderCellStyleHighlight3
2829 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2830 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2832 YAHOO.widget.Calendar.prototype.renderCellStyleHighlight3 = function(workingDate, cell) {
2833         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
2837 * Renders a single standard calendar cell using the CSS hightlight4 style
2838 * @method renderCellStyleHighlight4
2839 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2840 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2842 YAHOO.widget.Calendar.prototype.renderCellStyleHighlight4 = function(workingDate, cell) {
2843         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
2847 * Applies the default style used for rendering today's date to the current calendar cell
2848 * @method renderCellStyleToday
2849 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2850 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2852 YAHOO.widget.Calendar.prototype.renderCellStyleToday = function(workingDate, cell) {
2853         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_TODAY);
2857 * Applies the default style used for rendering selected dates to the current calendar cell
2858 * @method renderCellStyleSelected
2859 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2860 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2861 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
2862 *                       should not be terminated
2864 YAHOO.widget.Calendar.prototype.renderCellStyleSelected = function(workingDate, cell) {
2865         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_SELECTED);
2869 * Applies the default style used for rendering dates that are not a part of the current
2870 * month (preceding or trailing the cells for the current month)
2871 * @method renderCellNotThisMonth
2872 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2873 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2874 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
2875 *                       should not be terminated
2877 YAHOO.widget.Calendar.prototype.renderCellNotThisMonth = function(workingDate, cell) {
2878         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM);
2879         cell.innerHTML=workingDate.getDate();
2880         return YAHOO.widget.Calendar.STOP_RENDER;
2884 * Renders the current calendar cell as a non-selectable "black-out" date using the default
2885 * restricted style.
2886 * @method renderBodyCellRestricted
2887 * @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
2888 * @param {HTMLTableCellElement} cell                    The current working cell in the calendar
2889 * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
2890 *                       should not be terminated
2892 YAHOO.widget.Calendar.prototype.renderBodyCellRestricted = function(workingDate, cell) {
2893         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL);
2894         YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_RESTRICTED);
2895         cell.innerHTML=workingDate.getDate();
2896         return YAHOO.widget.Calendar.STOP_RENDER;
2899 // END BUILT-IN TABLE CELL RENDERERS
2901 // BEGIN MONTH NAVIGATION METHODS
2904 * Adds the designated number of months to the current calendar month, and sets the current
2905 * calendar page date to the new month.
2906 * @method addMonths
2907 * @param {Number}       count   The number of months to add to the current calendar
2909 YAHOO.widget.Calendar.prototype.addMonths = function(count) {
2910         var cfgPageDate = YAHOO.widget.Calendar._DEFAULT_CONFIG.PAGEDATE.key;
2911         this.cfg.setProperty(cfgPageDate, YAHOO.widget.DateMath.add(this.cfg.getProperty(cfgPageDate), YAHOO.widget.DateMath.MONTH, count));
2912         this.resetRenderers();
2913         this.changePageEvent.fire();
2917 * Subtracts the designated number of months from the current calendar month, and sets the current
2918 * calendar page date to the new month.
2919 * @method subtractMonths
2920 * @param {Number}       count   The number of months to subtract from the current calendar
2922 YAHOO.widget.Calendar.prototype.subtractMonths = function(count) {
2923         var cfgPageDate = YAHOO.widget.Calendar._DEFAULT_CONFIG.PAGEDATE.key;
2924         this.cfg.setProperty(cfgPageDate, YAHOO.widget.DateMath.subtract(this.cfg.getProperty(cfgPageDate), YAHOO.widget.DateMath.MONTH, count));
2925         this.resetRenderers();
2926         this.changePageEvent.fire();
2930 * Adds the designated number of years to the current calendar, and sets the current
2931 * calendar page date to the new month.
2932 * @method addYears
2933 * @param {Number}       count   The number of years to add to the current calendar
2935 YAHOO.widget.Calendar.prototype.addYears = function(count) {
2936         var cfgPageDate = YAHOO.widget.Calendar._DEFAULT_CONFIG.PAGEDATE.key;
2937         this.cfg.setProperty(cfgPageDate, YAHOO.widget.DateMath.add(this.cfg.getProperty(cfgPageDate), YAHOO.widget.DateMath.YEAR, count));
2938         this.resetRenderers();
2939         this.changePageEvent.fire();
2943 * Subtcats the designated number of years from the current calendar, and sets the current
2944 * calendar page date to the new month.
2945 * @method subtractYears
2946 * @param {Number}       count   The number of years to subtract from the current calendar
2948 YAHOO.widget.Calendar.prototype.subtractYears = function(count) {
2949         var cfgPageDate = YAHOO.widget.Calendar._DEFAULT_CONFIG.PAGEDATE.key;
2950         this.cfg.setProperty(cfgPageDate, YAHOO.widget.DateMath.subtract(this.cfg.getProperty(cfgPageDate), YAHOO.widget.DateMath.YEAR, count));
2951         this.resetRenderers();
2952         this.changePageEvent.fire();
2956 * Navigates to the next month page in the calendar widget.
2957 * @method nextMonth
2959 YAHOO.widget.Calendar.prototype.nextMonth = function() {
2960         this.addMonths(1);
2964 * Navigates to the previous month page in the calendar widget.
2965 * @method previousMonth
2967 YAHOO.widget.Calendar.prototype.previousMonth = function() {
2968         this.subtractMonths(1);
2972 * Navigates to the next year in the currently selected month in the calendar widget.
2973 * @method nextYear
2975 YAHOO.widget.Calendar.prototype.nextYear = function() {
2976         this.addYears(1);
2980 * Navigates to the previous year in the currently selected month in the calendar widget.
2981 * @method previousYear
2983 YAHOO.widget.Calendar.prototype.previousYear = function() {
2984         this.subtractYears(1);
2987 // END MONTH NAVIGATION METHODS
2989 // BEGIN SELECTION METHODS
2992 * Resets the calendar widget to the originally selected month and year, and 
2993 * sets the calendar to the initial selection(s).
2994 * @method reset
2996 YAHOO.widget.Calendar.prototype.reset = function() {
2997         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
2998         this.cfg.resetProperty(defCfg.SELECTED.key);
2999         this.cfg.resetProperty(defCfg.PAGEDATE.key);
3000         this.resetEvent.fire();
3004 * Clears the selected dates in the current calendar widget and sets the calendar
3005 * to the current month and year.
3006 * @method clear
3008 YAHOO.widget.Calendar.prototype.clear = function() {
3009         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
3010         this.cfg.setProperty(defCfg.SELECTED.key, []);
3011         this.cfg.setProperty(defCfg.PAGEDATE.key, new Date(this.today.getTime()));
3012         this.clearEvent.fire();
3016 * Selects a date or a collection of dates on the current calendar. This method, by default,
3017 * does not call the render method explicitly. Once selection has completed, render must be 
3018 * called for the changes to be reflected visually.
3020 * Any dates which are OOB (out of bounds, not selectable) will not be selected and the array of 
3021 * selected dates passed to the selectEvent will not contain OOB dates.
3023 * If all dates are OOB, the no state change will occur; beforeSelect and select events will not be fired.
3025 * @method select
3026 * @param        {String/Date/Date[]}    date    The date string of dates to select in the current calendar. Valid formats are
3027 *                                                               individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3028 *                                                               Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3029 *                                                               This method can also take a JavaScript Date object or an array of Date objects.
3030 * @return       {Date[]}                        Array of JavaScript Date objects representing all individual dates that are currently selected.
3032 YAHOO.widget.Calendar.prototype.select = function(date) {
3034         var aToBeSelected = this._toFieldArray(date);
3036         // Filtered array of valid dates
3037         var validDates = [];
3038         var selected = [];
3039         var cfgSelected = YAHOO.widget.Calendar._DEFAULT_CONFIG.SELECTED.key;
3040         
3041         for (var a=0; a < aToBeSelected.length; ++a) {
3042                 var toSelect = aToBeSelected[a];
3044                 if (!this.isDateOOB(this._toDate(toSelect))) {
3045                         
3046                         if (validDates.length === 0) {
3047                                 this.beforeSelectEvent.fire();
3048                                 selected = this.cfg.getProperty(cfgSelected);
3049                         }
3051                         validDates.push(toSelect);
3052                         
3053                         if (this._indexOfSelectedFieldArray(toSelect) == -1) { 
3054                                 selected[selected.length] = toSelect;
3055                         }
3056                 }
3057         }
3058         
3060         if (validDates.length > 0) {
3061                 if (this.parent) {
3062                         this.parent.cfg.setProperty(cfgSelected, selected);
3063                 } else {
3064                         this.cfg.setProperty(cfgSelected, selected);
3065                 }
3066                 this.selectEvent.fire(validDates);
3067         }
3069         return this.getSelectedDates();
3073 * Selects a date on the current calendar by referencing the index of the cell that should be selected.
3074 * This method is used to easily select a single cell (usually with a mouse click) without having to do
3075 * a full render. The selected style is applied to the cell directly.
3077 * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month 
3078 * or out of bounds cells), it will not be selected and in such a case beforeSelect and select events will not be fired.
3080 * @method selectCell
3081 * @param        {Number}        cellIndex       The index of the cell to select in the current calendar. 
3082 * @return       {Date[]}        Array of JavaScript Date objects representing all individual dates that are currently selected.
3084 YAHOO.widget.Calendar.prototype.selectCell = function(cellIndex) {
3086         var cell = this.cells[cellIndex];
3087         var cellDate = this.cellDates[cellIndex];
3088         var dCellDate = this._toDate(cellDate);
3089         
3090         var selectable = YAHOO.util.Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3092         if (selectable) {
3094                 this.beforeSelectEvent.fire();
3096                 var cfgSelected = YAHOO.widget.Calendar._DEFAULT_CONFIG.SELECTED.key;
3097                 var selected = this.cfg.getProperty(cfgSelected);
3099                 var selectDate = cellDate.concat();
3101                 if (this._indexOfSelectedFieldArray(selectDate) == -1) {
3102                         selected[selected.length] = selectDate;
3103                 }
3104                 if (this.parent) {
3105                         this.parent.cfg.setProperty(cfgSelected, selected);
3106                 } else {
3107                         this.cfg.setProperty(cfgSelected, selected);
3108                 }
3109                 this.renderCellStyleSelected(dCellDate,cell);
3110                 this.selectEvent.fire([selectDate]);
3112                 this.doCellMouseOut.call(cell, null, this);             
3113         }
3115         return this.getSelectedDates();
3119 * Deselects a date or a collection of dates on the current calendar. This method, by default,
3120 * does not call the render method explicitly. Once deselection has completed, render must be 
3121 * called for the changes to be reflected visually.
3123 * The method will not attempt to deselect any dates which are OOB (out of bounds, and hence not selectable) 
3124 * and the array of deselected dates passed to the deselectEvent will not contain any OOB dates.
3126 * If all dates are OOB, beforeDeselect and deselect events will not be fired.
3128 * @method deselect
3129 * @param        {String/Date/Date[]}    date    The date string of dates to deselect in the current calendar. Valid formats are
3130 *                                                               individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3131 *                                                               Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3132 *                                                               This method can also take a JavaScript Date object or an array of Date objects. 
3133 * @return       {Date[]}                        Array of JavaScript Date objects representing all individual dates that are currently selected.
3135 YAHOO.widget.Calendar.prototype.deselect = function(date) {
3137         var aToBeDeselected = this._toFieldArray(date);
3139         var validDates = [];
3140         var selected = [];
3141         var cfgSelected = YAHOO.widget.Calendar._DEFAULT_CONFIG.SELECTED.key;
3143         for (var a=0; a < aToBeDeselected.length; ++a) {
3144                 var toDeselect = aToBeDeselected[a];
3146                 if (!this.isDateOOB(this._toDate(toDeselect))) {
3148                         if (validDates.length === 0) {
3149                                 this.beforeDeselectEvent.fire();
3150                                 selected = this.cfg.getProperty(cfgSelected);
3151                         }
3153                         validDates.push(toDeselect);
3155                         var index = this._indexOfSelectedFieldArray(toDeselect);
3156                         if (index != -1) {      
3157                                 selected.splice(index,1);
3158                         }
3159                 }
3160         }
3163         if (validDates.length > 0) {
3164                 if (this.parent) {
3165                         this.parent.cfg.setProperty(cfgSelected, selected);
3166                 } else {
3167                         this.cfg.setProperty(cfgSelected, selected);
3168                 }
3169                 this.deselectEvent.fire(validDates);
3170         }
3172         return this.getSelectedDates();
3176 * Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
3177 * This method is used to easily deselect a single cell (usually with a mouse click) without having to do
3178 * a full render. The selected style is removed from the cell directly.
3180 * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month 
3181 * or out of bounds cells), the method will not attempt to deselect it and in such a case, beforeDeselect and 
3182 * deselect events will not be fired.
3184 * @method deselectCell
3185 * @param        {Number}        cellIndex       The index of the cell to deselect in the current calendar. 
3186 * @return       {Date[]}        Array of JavaScript Date objects representing all individual dates that are currently selected.
3188 YAHOO.widget.Calendar.prototype.deselectCell = function(cellIndex) {
3189         var cell = this.cells[cellIndex];
3190         var cellDate = this.cellDates[cellIndex];
3191         var cellDateIndex = this._indexOfSelectedFieldArray(cellDate);
3192         
3193         var selectable = YAHOO.util.Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3195         if (selectable) {
3197                 this.beforeDeselectEvent.fire();
3199                 var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
3200                 var selected = this.cfg.getProperty(defCfg.SELECTED.key);
3202                 var dCellDate = this._toDate(cellDate);
3203                 var selectDate = cellDate.concat();
3205                 if (cellDateIndex > -1) {
3206                         if (this.cfg.getProperty(defCfg.PAGEDATE.key).getMonth() == dCellDate.getMonth() &&
3207                                 this.cfg.getProperty(defCfg.PAGEDATE.key).getFullYear() == dCellDate.getFullYear()) {
3208                                 YAHOO.util.Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
3209                         }
3210                         selected.splice(cellDateIndex, 1);
3211                 }
3213                 if (this.parent) {
3214                         this.parent.cfg.setProperty(defCfg.SELECTED.key, selected);
3215                 } else {
3216                         this.cfg.setProperty(defCfg.SELECTED.key, selected);
3217                 }
3219                 this.deselectEvent.fire(selectDate);
3220         }
3222         return this.getSelectedDates();
3226 * Deselects all dates on the current calendar.
3227 * @method deselectAll
3228 * @return {Date[]}              Array of JavaScript Date objects representing all individual dates that are currently selected.
3229 *                                               Assuming that this function executes properly, the return value should be an empty array.
3230 *                                               However, the empty array is returned for the sake of being able to check the selection status
3231 *                                               of the calendar.
3233 YAHOO.widget.Calendar.prototype.deselectAll = function() {
3234         this.beforeDeselectEvent.fire();
3235         
3236         var cfgSelected = YAHOO.widget.Calendar._DEFAULT_CONFIG.SELECTED.key;
3238         var selected = this.cfg.getProperty(cfgSelected);
3239         var count = selected.length;
3240         var sel = selected.concat();
3242         if (this.parent) {
3243                 this.parent.cfg.setProperty(cfgSelected, []);
3244         } else {
3245                 this.cfg.setProperty(cfgSelected, []);
3246         }
3247         
3248         if (count > 0) {
3249                 this.deselectEvent.fire(sel);
3250         }
3252         return this.getSelectedDates();
3255 // END SELECTION METHODS
3257 // BEGIN TYPE CONVERSION METHODS
3260 * Converts a date (either a JavaScript Date object, or a date string) to the internal data structure
3261 * used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]].
3262 * @method _toFieldArray
3263 * @private
3264 * @param        {String/Date/Date[]}    date    The date string of dates to deselect in the current calendar. Valid formats are
3265 *                                                               individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3266 *                                                               Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3267 *                                                               This method can also take a JavaScript Date object or an array of Date objects. 
3268 * @return {Array[](Number[])}   Array of date field arrays
3270 YAHOO.widget.Calendar.prototype._toFieldArray = function(date) {
3271         var returnDate = [];
3273         if (date instanceof Date) {
3274                 returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]];
3275         } else if (YAHOO.lang.isString(date)) {
3276                 returnDate = this._parseDates(date);
3277         } else if (YAHOO.lang.isArray(date)) {
3278                 for (var i=0;i<date.length;++i) {
3279                         var d = date[i];
3280                         returnDate[returnDate.length] = [d.getFullYear(),d.getMonth()+1,d.getDate()];
3281                 }
3282         }
3283         
3284         return returnDate;
3288 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
3289 * @method _toDate
3290 * @private
3291 * @param        {Number[]}              dateFieldArray  The date field array to convert to a JavaScript Date.
3292 * @return       {Date}  JavaScript Date object representing the date field array
3294 YAHOO.widget.Calendar.prototype._toDate = function(dateFieldArray) {
3295         if (dateFieldArray instanceof Date) {
3296                 return dateFieldArray;
3297         } else {
3298                 return new Date(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]);
3299         }
3302 // END TYPE CONVERSION METHODS 
3304 // BEGIN UTILITY METHODS
3307 * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
3308 * @method _fieldArraysAreEqual
3309 * @private
3310 * @param        {Number[]}      array1  The first date field array to compare
3311 * @param        {Number[]}      array2  The first date field array to compare
3312 * @return       {Boolean}       The boolean that represents the equality of the two arrays
3314 YAHOO.widget.Calendar.prototype._fieldArraysAreEqual = function(array1, array2) {
3315         var match = false;
3317         if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2]) {
3318                 match=true;     
3319         }
3321         return match;
3325 * Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates.
3326 * @method       _indexOfSelectedFieldArray
3327 * @private
3328 * @param        {Number[]}              find    The date field array to search for
3329 * @return       {Number}                        The index of the date field array within the collection of selected dates.
3330 *                                                               -1 will be returned if the date is not found.
3332 YAHOO.widget.Calendar.prototype._indexOfSelectedFieldArray = function(find) {
3333         var selected = -1;
3334         var seldates = this.cfg.getProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.SELECTED.key);
3336         for (var s=0;s<seldates.length;++s) {
3337                 var sArray = seldates[s];
3338                 if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2]) {
3339                         selected = s;
3340                         break;
3341                 }
3342         }
3344         return selected;
3348 * Determines whether a given date is OOM (out of month).
3349 * @method       isDateOOM
3350 * @param        {Date}  date    The JavaScript Date object for which to check the OOM status
3351 * @return       {Boolean}       true if the date is OOM
3353 YAHOO.widget.Calendar.prototype.isDateOOM = function(date) {
3354         return (date.getMonth() != this.cfg.getProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.PAGEDATE.key).getMonth());
3358 * Determines whether a given date is OOB (out of bounds - less than the mindate or more than the maxdate).
3360 * @method       isDateOOB
3361 * @param        {Date}  date    The JavaScript Date object for which to check the OOB status
3362 * @return       {Boolean}       true if the date is OOB
3364 YAHOO.widget.Calendar.prototype.isDateOOB = function(date) {
3365         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
3366         
3367         var minDate = this.cfg.getProperty(defCfg.MINDATE.key);
3368         var maxDate = this.cfg.getProperty(defCfg.MAXDATE.key);
3369         var dm = YAHOO.widget.DateMath;
3370         
3371         if (minDate) {
3372                 minDate = dm.clearTime(minDate);
3373         } 
3374         if (maxDate) {
3375                 maxDate = dm.clearTime(maxDate);
3376         }
3378         var clearedDate = new Date(date.getTime());
3379         clearedDate = dm.clearTime(clearedDate);
3381         return ((minDate && clearedDate.getTime() < minDate.getTime()) || (maxDate && clearedDate.getTime() > maxDate.getTime()));
3385  * Parses a pagedate configuration property value. The value can either be specified as a string of form "mm/yyyy" or a Date object 
3386  * 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 
3387  * @method      _parsePageDate
3388  * @private
3389  * @param {Date|String} date    Pagedate value which needs to be parsed
3390  * @return {Date}       The Date object representing the pagedate
3391  */
3392 YAHOO.widget.Calendar.prototype._parsePageDate = function(date) {
3393         var parsedDate;
3394         
3395         var defCfg = YAHOO.widget.Calendar._DEFAULT_CONFIG;
3397         if (date) {
3398                 if (date instanceof Date) {
3399                         parsedDate = YAHOO.widget.DateMath.findMonthStart(date);
3400                 } else {
3401                         var month, year, aMonthYear;
3402                         aMonthYear = date.split(this.cfg.getProperty(defCfg.DATE_FIELD_DELIMITER.key));
3403                         month = parseInt(aMonthYear[this.cfg.getProperty(defCfg.MY_MONTH_POSITION.key)-1], 10)-1;
3404                         year = parseInt(aMonthYear[this.cfg.getProperty(defCfg.MY_YEAR_POSITION.key)-1], 10);
3405                         
3406                         parsedDate = new Date(year, month, 1);
3407                 }
3408         } else {
3409                 parsedDate = new Date(this.today.getFullYear(), this.today.getMonth(), 1);
3410         }
3411         return parsedDate;
3414 // END UTILITY METHODS
3416 // BEGIN EVENT HANDLERS
3419 * Event executed before a date is selected in the calendar widget.
3420 * @deprecated Event handlers for this event should be susbcribed to beforeSelectEvent.
3422 YAHOO.widget.Calendar.prototype.onBeforeSelect = function() {
3423         if (this.cfg.getProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.MULTI_SELECT.key) === false) {
3424                 if (this.parent) {
3425                         this.parent.callChildFunction("clearAllBodyCellStyles", this.Style.CSS_CELL_SELECTED);
3426                         this.parent.deselectAll();
3427                 } else {
3428                         this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED);
3429                         this.deselectAll();
3430                 }
3431         }
3435 * Event executed when a date is selected in the calendar widget.
3436 * @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] ]
3437 * @deprecated Event handlers for this event should be susbcribed to selectEvent.
3439 YAHOO.widget.Calendar.prototype.onSelect = function(selected) { };
3442 * Event executed before a date is deselected in the calendar widget.
3443 * @deprecated Event handlers for this event should be susbcribed to beforeDeselectEvent.
3445 YAHOO.widget.Calendar.prototype.onBeforeDeselect = function() { };
3448 * Event executed when a date is deselected in the calendar widget.
3449 * @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] ]
3450 * @deprecated Event handlers for this event should be susbcribed to deselectEvent.
3452 YAHOO.widget.Calendar.prototype.onDeselect = function(deselected) { };
3455 * Event executed when the user navigates to a different calendar page.
3456 * @deprecated Event handlers for this event should be susbcribed to changePageEvent.
3458 YAHOO.widget.Calendar.prototype.onChangePage = function() {
3459         this.render();
3463 * Event executed when the calendar widget is rendered.
3464 * @deprecated Event handlers for this event should be susbcribed to renderEvent.
3466 YAHOO.widget.Calendar.prototype.onRender = function() { };
3469 * Event executed when the calendar widget is reset to its original state.
3470 * @deprecated Event handlers for this event should be susbcribed to resetEvemt.
3472 YAHOO.widget.Calendar.prototype.onReset = function() { this.render(); };
3475 * Event executed when the calendar widget is completely cleared to the current month with no selections.
3476 * @deprecated Event handlers for this event should be susbcribed to clearEvent.
3478 YAHOO.widget.Calendar.prototype.onClear = function() { this.render(); };
3481 * Validates the calendar widget. This method has no default implementation
3482 * and must be extended by subclassing the widget.
3483 * @return       Should return true if the widget validates, and false if
3484 * it doesn't.
3485 * @type Boolean
3487 YAHOO.widget.Calendar.prototype.validate = function() { return true; };
3489 // END EVENT HANDLERS
3491 // BEGIN DATE PARSE METHODS
3494 * Converts a date string to a date field array
3495 * @private
3496 * @param        {String}        sDate                   Date string. Valid formats are mm/dd and mm/dd/yyyy.
3497 * @return                               A date field array representing the string passed to the method
3498 * @type Array[](Number[])
3500 YAHOO.widget.Calendar.prototype._parseDate = function(sDate) {
3501         var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER);
3502         var rArray;
3504         if (aDate.length == 2) {
3505                 rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
3506                 rArray.type = YAHOO.widget.Calendar.MONTH_DAY;
3507         } else {
3508                 rArray = [aDate[this.Locale.MDY_YEAR_POSITION-1],aDate[this.Locale.MDY_MONTH_POSITION-1],aDate[this.Locale.MDY_DAY_POSITION-1]];
3509                 rArray.type = YAHOO.widget.Calendar.DATE;
3510         }
3512         for (var i=0;i<rArray.length;i++) {
3513                 rArray[i] = parseInt(rArray[i], 10);
3514         }
3516         return rArray;
3520 * Converts a multi or single-date string to an array of date field arrays
3521 * @private
3522 * @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
3523 * @return                                                       An array of date field arrays
3524 * @type Array[](Number[])
3526 YAHOO.widget.Calendar.prototype._parseDates = function(sDates) {
3527         var aReturn = [];
3529         var aDates = sDates.split(this.Locale.DATE_DELIMITER);
3530         
3531         for (var d=0;d<aDates.length;++d) {
3532                 var sDate = aDates[d];
3534                 if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) {
3535                         // This is a range
3536                         var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER);
3538                         var dateStart = this._parseDate(aRange[0]);
3539                         var dateEnd = this._parseDate(aRange[1]);
3541                         var fullRange = this._parseRange(dateStart, dateEnd);
3542                         aReturn = aReturn.concat(fullRange);
3543                 } else {
3544                         // This is not a range
3545                         var aDate = this._parseDate(sDate);
3546                         aReturn.push(aDate);
3547                 }
3548         }
3549         return aReturn;
3553 * Converts a date range to the full list of included dates
3554 * @private
3555 * @param        {Number[]}      startDate       Date field array representing the first date in the range
3556 * @param        {Number[]}      endDate         Date field array representing the last date in the range
3557 * @return                                                       An array of date field arrays
3558 * @type Array[](Number[])
3560 YAHOO.widget.Calendar.prototype._parseRange = function(startDate, endDate) {
3561         var dStart   = new Date(startDate[0],startDate[1]-1,startDate[2]);
3562         var dCurrent = YAHOO.widget.DateMath.add(new Date(startDate[0],startDate[1]-1,startDate[2]),YAHOO.widget.DateMath.DAY,1);
3563         var dEnd     = new Date(endDate[0],  endDate[1]-1,  endDate[2]);
3565         var results = [];
3566         results.push(startDate);
3567         while (dCurrent.getTime() <= dEnd.getTime()) {
3568                 results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
3569                 dCurrent = YAHOO.widget.DateMath.add(dCurrent,YAHOO.widget.DateMath.DAY,1);
3570         }
3571         return results;
3574 // END DATE PARSE METHODS
3576 // BEGIN RENDERER METHODS
3579 * Resets the render stack of the current calendar to its original pre-render value.
3581 YAHOO.widget.Calendar.prototype.resetRenderers = function() {
3582         this.renderStack = this._renderStack.concat();
3586 * Clears the inner HTML, CSS class and style information from the specified cell.
3587 * @method clearElement
3588 * @param        {HTMLTableCellElement}  The cell to clear
3589 */ 
3590 YAHOO.widget.Calendar.prototype.clearElement = function(cell) {
3591         cell.innerHTML = "&#160;";
3592         cell.className="";
3596 * Adds a renderer to the render stack. The function reference passed to this method will be executed
3597 * when a date cell matches the conditions specified in the date string for this renderer.
3598 * @method addRenderer
3599 * @param        {String}        sDates          A date string to associate with the specified renderer. Valid formats
3600 *                                                                       include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
3601 * @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
3603 YAHOO.widget.Calendar.prototype.addRenderer = function(sDates, fnRender) {
3604         var aDates = this._parseDates(sDates);
3605         for (var i=0;i<aDates.length;++i) {
3606                 var aDate = aDates[i];
3607         
3608                 if (aDate.length == 2) { // this is either a range or a month/day combo
3609                         if (aDate[0] instanceof Array) { // this is a range
3610                                 this._addRenderer(YAHOO.widget.Calendar.RANGE,aDate,fnRender);
3611                         } else { // this is a month/day combo
3612                                 this._addRenderer(YAHOO.widget.Calendar.MONTH_DAY,aDate,fnRender);
3613                         }
3614                 } else if (aDate.length == 3) {
3615                         this._addRenderer(YAHOO.widget.Calendar.DATE,aDate,fnRender);
3616                 }
3617         }
3621 * The private method used for adding cell renderers to the local render stack.
3622 * This method is called by other methods that set the renderer type prior to the method call.
3623 * @method _addRenderer
3624 * @private
3625 * @param        {String}        type            The type string that indicates the type of date renderer being added.
3626 *                                                                       Values are YAHOO.widget.Calendar.DATE, YAHOO.widget.Calendar.MONTH_DAY, YAHOO.widget.Calendar.WEEKDAY,
3627 *                                                                       YAHOO.widget.Calendar.RANGE, YAHOO.widget.Calendar.MONTH
3628 * @param        {Array}         aDates          An array of dates used to construct the renderer. The format varies based
3629 *                                                                       on the renderer type
3630 * @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
3632 YAHOO.widget.Calendar.prototype._addRenderer = function(type, aDates, fnRender) {
3633         var add = [type,aDates,fnRender];
3634         this.renderStack.unshift(add);  
3635         this._renderStack = this.renderStack.concat();
3639 * Adds a month to the render stack. The function reference passed to this method will be executed
3640 * when a date cell matches the month passed to this method.
3641 * @method addMonthRenderer
3642 * @param        {Number}        month           The month (1-12) to associate with this renderer
3643 * @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
3645 YAHOO.widget.Calendar.prototype.addMonthRenderer = function(month, fnRender) {
3646         this._addRenderer(YAHOO.widget.Calendar.MONTH,[month],fnRender);
3650 * Adds a weekday to the render stack. The function reference passed to this method will be executed
3651 * when a date cell matches the weekday passed to this method.
3652 * @method addWeekdayRenderer
3653 * @param        {Number}        weekday         The weekday (0-6) to associate with this renderer
3654 * @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
3656 YAHOO.widget.Calendar.prototype.addWeekdayRenderer = function(weekday, fnRender) {
3657         this._addRenderer(YAHOO.widget.Calendar.WEEKDAY,[weekday],fnRender);
3660 // END RENDERER METHODS
3662 // BEGIN CSS METHODS
3665 * Removes all styles from all body cells in the current calendar table.
3666 * @method clearAllBodyCellStyles
3667 * @param        {style}         The CSS class name to remove from all calendar body cells
3669 YAHOO.widget.Calendar.prototype.clearAllBodyCellStyles = function(style) {
3670         for (var c=0;c<this.cells.length;++c) {
3671                 YAHOO.util.Dom.removeClass(this.cells[c],style);
3672         }
3675 // END CSS METHODS
3677 // BEGIN GETTER/SETTER METHODS
3679 * Sets the calendar's month explicitly
3680 * @method setMonth
3681 * @param {Number}       month           The numeric month, from 0 (January) to 11 (December)
3683 YAHOO.widget.Calendar.prototype.setMonth = function(month) {
3684         var cfgPageDate = YAHOO.widget.Calendar._DEFAULT_CONFIG.PAGEDATE.key;
3685         var current = this.cfg.getProperty(cfgPageDate);
3686         current.setMonth(parseInt(month, 10));
3687         this.cfg.setProperty(cfgPageDate, current);
3691 * Sets the calendar's year explicitly.
3692 * @method setYear
3693 * @param {Number}       year            The numeric 4-digit year
3695 YAHOO.widget.Calendar.prototype.setYear = function(year) {
3696         var cfgPageDate = YAHOO.widget.Calendar._DEFAULT_CONFIG.PAGEDATE.key;
3697         var current = this.cfg.getProperty(cfgPageDate);
3698         current.setFullYear(parseInt(year, 10));
3699         this.cfg.setProperty(cfgPageDate, current);
3703 * Gets the list of currently selected dates from the calendar.
3704 * @method getSelectedDates
3705 * @return {Date[]} An array of currently selected JavaScript Date objects.
3707 YAHOO.widget.Calendar.prototype.getSelectedDates = function() {
3708         var returnDates = [];
3709         var selected = this.cfg.getProperty(YAHOO.widget.Calendar._DEFAULT_CONFIG.SELECTED.key);
3711         for (var d=0;d<selected.length;++d) {
3712                 var dateArray = selected[d];
3714                 var date = new Date(dateArray[0],dateArray[1]-1,dateArray[2]);
3715                 returnDates.push(date);
3716         }
3718         returnDates.sort( function(a,b) { return a-b; } );
3719         return returnDates;
3722 /// END GETTER/SETTER METHODS ///
3725 * Hides the Calendar's outer container from view.
3726 * @method hide
3728 YAHOO.widget.Calendar.prototype.hide = function() {
3729         this.oDomContainer.style.display = "none";
3733 * Shows the Calendar's outer container.
3734 * @method show
3736 YAHOO.widget.Calendar.prototype.show = function() {
3737         this.oDomContainer.style.display = "block";
3741 * Returns a string representing the current browser.
3742 * @deprecated As of 2.3.0, environment information is available in YAHOO.env.ua
3743 * @see YAHOO.env.ua
3744 * @property browser
3745 * @type String
3747 YAHOO.widget.Calendar.prototype.browser = function() {
3748                         var ua = navigator.userAgent.toLowerCase();
3749                                   if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof)
3750                                          return 'opera';
3751                                   } else if (ua.indexOf('msie 7')!=-1) { // IE7
3752                                          return 'ie7';
3753                                   } else if (ua.indexOf('msie') !=-1) { // IE
3754                                          return 'ie';
3755                                   } else if (ua.indexOf('safari')!=-1) { // Safari (check before Gecko because it includes "like Gecko")
3756                                          return 'safari';
3757                                   } else if (ua.indexOf('gecko') != -1) { // Gecko
3758                                          return 'gecko';
3759                                   } else {
3760                                          return false;
3761                                   }
3762                         }();
3764 * Returns a string representation of the object.
3765 * @method toString
3766 * @return {String}      A string representation of the Calendar object.
3768 YAHOO.widget.Calendar.prototype.toString = function() {
3769         return "Calendar " + this.id;
3773 * @namespace YAHOO.widget
3774 * @class Calendar_Core
3775 * @extends YAHOO.widget.Calendar
3776 * @deprecated The old Calendar_Core class is no longer necessary.
3778 YAHOO.widget.Calendar_Core = YAHOO.widget.Calendar;
3780 YAHOO.widget.Cal_Core = YAHOO.widget.Calendar;
3783 * YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar. This class facilitates
3784 * the ability to have multi-page calendar views that share a single dataset and are
3785 * dependent on each other.
3787 * The calendar group instance will refer to each of its elements using a 0-based index.
3788 * For example, to construct the placeholder for a calendar group widget with id "cal1" and
3789 * containerId of "cal1Container", the markup would be as follows:
3790 *       <xmp>
3791 *               <div id="cal1Container_0"></div>
3792 *               <div id="cal1Container_1"></div>
3793 *       </xmp>
3794 * The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers.
3795 * @namespace YAHOO.widget
3796 * @class CalendarGroup
3797 * @constructor
3798 * @param {String}       id                      The id of the table element that will represent the calendar widget
3799 * @param {String}       containerId     The id of the container div element that will wrap the calendar table
3800 * @param {Object}       config          The configuration object containing the Calendar's arguments
3802 YAHOO.widget.CalendarGroup = function(id, containerId, config) {
3803         if (arguments.length > 0) {
3804                 this.init(id, containerId, config);
3805         }
3809 * Initializes the calendar group. All subclasses must call this method in order for the
3810 * group to be initialized properly.
3811 * @method init
3812 * @param {String}       id                      The id of the table element that will represent the calendar widget
3813 * @param {String}       containerId     The id of the container div element that will wrap the calendar table
3814 * @param {Object}       config          The configuration object containing the Calendar's arguments
3816 YAHOO.widget.CalendarGroup.prototype.init = function(id, containerId, config) {
3817         this.initEvents();
3818         this.initStyles();
3820         /**
3821         * The collection of Calendar pages contained within the CalendarGroup
3822         * @property pages
3823         * @type YAHOO.widget.Calendar[]
3824         */
3825         this.pages = [];
3826         
3827         /**
3828         * The unique id associated with the CalendarGroup
3829         * @property id
3830         * @type String
3831         */
3832         this.id = id;
3834         /**
3835         * The unique id associated with the CalendarGroup container
3836         * @property containerId
3837         * @type String
3838         */
3839         this.containerId = containerId;
3841         /**
3842         * The outer containing element for the CalendarGroup
3843         * @property oDomContainer
3844         * @type HTMLElement
3845         */
3846         this.oDomContainer = document.getElementById(containerId);
3848         YAHOO.util.Dom.addClass(this.oDomContainer, YAHOO.widget.CalendarGroup.CSS_CONTAINER);
3849         YAHOO.util.Dom.addClass(this.oDomContainer, YAHOO.widget.CalendarGroup.CSS_MULTI_UP);
3851         /**
3852         * The Config object used to hold the configuration variables for the CalendarGroup
3853         * @property cfg
3854         * @type YAHOO.util.Config
3855         */
3856         this.cfg = new YAHOO.util.Config(this);
3858         /**
3859         * The local object which contains the CalendarGroup's options
3860         * @property Options
3861         * @type Object
3862         */
3863         this.Options = {};
3865         /**
3866         * The local object which contains the CalendarGroup's locale settings
3867         * @property Locale
3868         * @type Object
3869         */
3870         this.Locale = {};
3872         this.setupConfig();
3874         if (config) {
3875                 this.cfg.applyConfig(config, true);
3876         }
3878         this.cfg.fireQueue();
3880         // OPERA HACK FOR MISWRAPPED FLOATS
3881         if (YAHOO.env.ua.opera){
3882                 this.renderEvent.subscribe(this._fixWidth, this, true);
3883         }
3887 YAHOO.widget.CalendarGroup.prototype.setupConfig = function() {
3888         
3889         var defCfg = YAHOO.widget.CalendarGroup._DEFAULT_CONFIG;
3890         
3891         /**
3892         * The number of pages to include in the CalendarGroup. This value can only be set once, in the CalendarGroup's constructor arguments.
3893         * @config pages
3894         * @type Number
3895         * @default 2
3896         */
3897         this.cfg.addProperty(defCfg.PAGES.key, { value:defCfg.PAGES.value, validator:this.cfg.checkNumber, handler:this.configPages } );
3899         /**
3900         * The month/year representing the current visible Calendar date (mm/yyyy)
3901         * @config pagedate
3902         * @type String
3903         * @default today's date
3904         */
3905         this.cfg.addProperty(defCfg.PAGEDATE.key, { value:new Date(), handler:this.configPageDate } );
3907         /**
3908         * The date or range of dates representing the current Calendar selection
3909         * @config selected
3910         * @type String
3911         * @default []
3912         */
3913         this.cfg.addProperty(defCfg.SELECTED.key, { value:[], handler:this.configSelected } );
3915         /**
3916         * The title to display above the CalendarGroup's month header
3917         * @config title
3918         * @type String
3919         * @default ""
3920         */
3921         this.cfg.addProperty(defCfg.TITLE.key, { value:defCfg.TITLE.value, handler:this.configTitle } );
3923         /**
3924         * Whether or not a close button should be displayed for this CalendarGroup
3925         * @config close
3926         * @type Boolean
3927         * @default false
3928         */
3929         this.cfg.addProperty(defCfg.CLOSE.key, { value:defCfg.CLOSE.value, handler:this.configClose } );
3931         /**
3932         * 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.
3933         * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be 
3934         * enabled if required.
3935         * 
3936         * @config iframe
3937         * @type Boolean
3938         * @default true for IE6 and below, false for all other browsers
3939         */
3940         this.cfg.addProperty(defCfg.IFRAME.key, { value:defCfg.IFRAME.value, handler:this.configIframe, validator:this.cfg.checkBoolean } );
3942         /**
3943         * The minimum selectable date in the current Calendar (mm/dd/yyyy)
3944         * @config mindate
3945         * @type String
3946         * @default null
3947         */
3948         this.cfg.addProperty(defCfg.MINDATE.key, { value:defCfg.MINDATE.value, handler:this.delegateConfig } );
3950         /**
3951         * The maximum selectable date in the current Calendar (mm/dd/yyyy)
3952         * @config maxdate
3953         * @type String
3954         * @default null
3955         */      
3956         this.cfg.addProperty(defCfg.MAXDATE.key, { value:defCfg.MAXDATE.value, handler:this.delegateConfig  } );
3958         // Options properties
3960         /**
3961         * True if the Calendar should allow multiple selections. False by default.
3962         * @config MULTI_SELECT
3963         * @type Boolean
3964         * @default false
3965         */
3966         this.cfg.addProperty(defCfg.MULTI_SELECT.key,   { value:defCfg.MULTI_SELECT.value, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
3968         /**
3969         * The weekday the week begins on. Default is 0 (Sunday).
3970         * @config START_WEEKDAY
3971         * @type number
3972         * @default 0
3973         */      
3974         this.cfg.addProperty(defCfg.START_WEEKDAY.key,  { value:defCfg.START_WEEKDAY.value, handler:this.delegateConfig, validator:this.cfg.checkNumber  } );
3975         
3976         /**
3977         * True if the Calendar should show weekday labels. True by default.
3978         * @config SHOW_WEEKDAYS
3979         * @type Boolean
3980         * @default true
3981         */      
3982         this.cfg.addProperty(defCfg.SHOW_WEEKDAYS.key,  { value:defCfg.SHOW_WEEKDAYS.value, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
3983         
3984         /**
3985         * True if the Calendar should show week row headers. False by default.
3986         * @config SHOW_WEEK_HEADER
3987         * @type Boolean
3988         * @default false
3989         */      
3990         this.cfg.addProperty(defCfg.SHOW_WEEK_HEADER.key,{ value:defCfg.SHOW_WEEK_HEADER.value, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
3991         
3992         /**
3993         * True if the Calendar should show week row footers. False by default.
3994         * @config SHOW_WEEK_FOOTER
3995         * @type Boolean
3996         * @default false
3997         */
3998         this.cfg.addProperty(defCfg.SHOW_WEEK_FOOTER.key,{ value:defCfg.SHOW_WEEK_FOOTER.value, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
3999         
4000         /**
4001         * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
4002         * @config HIDE_BLANK_WEEKS
4003         * @type Boolean
4004         * @default false
4005         */              
4006         this.cfg.addProperty(defCfg.HIDE_BLANK_WEEKS.key,{ value:defCfg.HIDE_BLANK_WEEKS.value, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
4007         
4008         /**
4009         * The image that should be used for the left navigation arrow.
4010         * @config NAV_ARROW_LEFT
4011         * @type String
4012         * @deprecated   You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
4013         * @default null
4014         */              
4015         this.cfg.addProperty(defCfg.NAV_ARROW_LEFT.key, { value:defCfg.NAV_ARROW_LEFT.value, handler:this.delegateConfig } );
4016         
4017         /**
4018         * The image that should be used for the right navigation arrow.
4019         * @config NAV_ARROW_RIGHT
4020         * @type String
4021         * @deprecated   You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
4022         * @default null
4023         */              
4024         this.cfg.addProperty(defCfg.NAV_ARROW_RIGHT.key,        { value:defCfg.NAV_ARROW_RIGHT.value, handler:this.delegateConfig } );
4026         // Locale properties
4027         
4028         /**
4029         * The short month labels for the current locale.
4030         * @config MONTHS_SHORT
4031         * @type String[]
4032         * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
4033         */
4034         this.cfg.addProperty(defCfg.MONTHS_SHORT.key,   { value:defCfg.MONTHS_SHORT.value, handler:this.delegateConfig } );
4035         
4036         /**
4037         * The long month labels for the current locale.
4038         * @config MONTHS_LONG
4039         * @type String[]
4040         * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
4041         */              
4042         this.cfg.addProperty(defCfg.MONTHS_LONG.key,            { value:defCfg.MONTHS_LONG.value, handler:this.delegateConfig } );
4043         
4044         /**
4045         * The 1-character weekday labels for the current locale.
4046         * @config WEEKDAYS_1CHAR
4047         * @type String[]
4048         * @default ["S", "M", "T", "W", "T", "F", "S"]
4049         */              
4050         this.cfg.addProperty(defCfg.WEEKDAYS_1CHAR.key, { value:defCfg.WEEKDAYS_1CHAR.value, handler:this.delegateConfig } );
4051         
4052         /**
4053         * The short weekday labels for the current locale.
4054         * @config WEEKDAYS_SHORT
4055         * @type String[]
4056         * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
4057         */              
4058         this.cfg.addProperty(defCfg.WEEKDAYS_SHORT.key, { value:defCfg.WEEKDAYS_SHORT.value, handler:this.delegateConfig } );
4059         
4060         /**
4061         * The medium weekday labels for the current locale.
4062         * @config WEEKDAYS_MEDIUM
4063         * @type String[]
4064         * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
4065         */              
4066         this.cfg.addProperty(defCfg.WEEKDAYS_MEDIUM.key,        { value:defCfg.WEEKDAYS_MEDIUM.value, handler:this.delegateConfig } );
4067         
4068         /**
4069         * The long weekday labels for the current locale.
4070         * @config WEEKDAYS_LONG
4071         * @type String[]
4072         * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
4073         */              
4074         this.cfg.addProperty(defCfg.WEEKDAYS_LONG.key,  { value:defCfg.WEEKDAYS_LONG.value, handler:this.delegateConfig } );
4076         /**
4077         * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
4078         * @config LOCALE_MONTHS
4079         * @type String
4080         * @default "long"
4081         */
4082         this.cfg.addProperty(defCfg.LOCALE_MONTHS.key,  { value:defCfg.LOCALE_MONTHS.value, handler:this.delegateConfig } );
4084         /**
4085         * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
4086         * @config LOCALE_WEEKDAYS
4087         * @type String
4088         * @default "short"
4089         */      
4090         this.cfg.addProperty(defCfg.LOCALE_WEEKDAYS.key,        { value:defCfg.LOCALE_WEEKDAYS.value, handler:this.delegateConfig } );
4092         /**
4093         * The value used to delimit individual dates in a date string passed to various Calendar functions.
4094         * @config DATE_DELIMITER
4095         * @type String
4096         * @default ","
4097         */
4098         this.cfg.addProperty(defCfg.DATE_DELIMITER.key,         { value:defCfg.DATE_DELIMITER.value, handler:this.delegateConfig } );
4100         /**
4101         * The value used to delimit date fields in a date string passed to various Calendar functions.
4102         * @config DATE_FIELD_DELIMITER
4103         * @type String
4104         * @default "/"
4105         */      
4106         this.cfg.addProperty(defCfg.DATE_FIELD_DELIMITER.key,{ value:defCfg.DATE_FIELD_DELIMITER.value, handler:this.delegateConfig } );
4108         /**
4109         * The value used to delimit date ranges in a date string passed to various Calendar functions.
4110         * @config DATE_RANGE_DELIMITER
4111         * @type String
4112         * @default "-"
4113         */
4114         this.cfg.addProperty(defCfg.DATE_RANGE_DELIMITER.key,{ value:defCfg.DATE_RANGE_DELIMITER.value, handler:this.delegateConfig } );
4116         /**
4117         * The position of the month in a month/year date string
4118         * @config MY_MONTH_POSITION
4119         * @type Number
4120         * @default 1
4121         */
4122         this.cfg.addProperty(defCfg.MY_MONTH_POSITION.key,      { value:defCfg.MY_MONTH_POSITION.value, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
4123         
4124         /**
4125         * The position of the year in a month/year date string
4126         * @config MY_YEAR_POSITION
4127         * @type Number
4128         * @default 2
4129         */      
4130         this.cfg.addProperty(defCfg.MY_YEAR_POSITION.key,       { value:defCfg.MY_YEAR_POSITION.value, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
4131         
4132         /**
4133         * The position of the month in a month/day date string
4134         * @config MD_MONTH_POSITION
4135         * @type Number
4136         * @default 1
4137         */      
4138         this.cfg.addProperty(defCfg.MD_MONTH_POSITION.key,      { value:defCfg.MD_MONTH_POSITION.value, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
4139         
4140         /**
4141         * The position of the day in a month/year date string
4142         * @config MD_DAY_POSITION
4143         * @type Number
4144         * @default 2
4145         */      
4146         this.cfg.addProperty(defCfg.MD_DAY_POSITION.key,                { value:defCfg.MD_DAY_POSITION.value, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
4147         
4148         /**
4149         * The position of the month in a month/day/year date string
4150         * @config MDY_MONTH_POSITION
4151         * @type Number
4152         * @default 1
4153         */      
4154         this.cfg.addProperty(defCfg.MDY_MONTH_POSITION.key,     { value:defCfg.MDY_MONTH_POSITION.value, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
4155         
4156         /**
4157         * The position of the day in a month/day/year date string
4158         * @config MDY_DAY_POSITION
4159         * @type Number
4160         * @default 2
4161         */      
4162         this.cfg.addProperty(defCfg.MDY_DAY_POSITION.key,       { value:defCfg.MDY_DAY_POSITION.value, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
4163         
4164         /**
4165         * The position of the year in a month/day/year date string
4166         * @config MDY_YEAR_POSITION
4167         * @type Number
4168         * @default 3
4169         */      
4170         this.cfg.addProperty(defCfg.MDY_YEAR_POSITION.key,      { value:defCfg.MDY_YEAR_POSITION.value, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
4172         /**
4173         * The position of the month in the month year label string used as the Calendar header
4174         * @config MY_LABEL_MONTH_POSITION
4175         * @type Number
4176         * @default 1
4177         */
4178         this.cfg.addProperty(defCfg.MY_LABEL_MONTH_POSITION.key,        { value:defCfg.MY_LABEL_MONTH_POSITION.value, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
4180         /**
4181         * The position of the year in the month year label string used as the Calendar header
4182         * @config MY_LABEL_YEAR_POSITION
4183         * @type Number
4184         * @default 2
4185         */
4186         this.cfg.addProperty(defCfg.MY_LABEL_YEAR_POSITION.key, { value:defCfg.MY_LABEL_YEAR_POSITION.value, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
4187         
4188         /**
4189         * The suffix used after the month when rendering the Calendar header
4190         * @config MY_LABEL_MONTH_SUFFIX
4191         * @type String
4192         * @default " "
4193         */
4194         this.cfg.addProperty(defCfg.MY_LABEL_MONTH_SUFFIX.key,  { value:defCfg.MY_LABEL_MONTH_SUFFIX.value, handler:this.delegateConfig } );
4195         
4196         /**
4197         * The suffix used after the year when rendering the Calendar header
4198         * @config MY_LABEL_YEAR_SUFFIX
4199         * @type String
4200         * @default ""
4201         */
4202         this.cfg.addProperty(defCfg.MY_LABEL_YEAR_SUFFIX.key, { value:defCfg.MY_LABEL_YEAR_SUFFIX.value, handler:this.delegateConfig } );
4206 * Initializes CalendarGroup's built-in CustomEvents
4207 * @method initEvents
4209 YAHOO.widget.CalendarGroup.prototype.initEvents = function() {
4210         var me = this;
4211         var strEvent = "Event";
4213         /**
4214         * Proxy subscriber to subscribe to the CalendarGroup's child Calendars' CustomEvents
4215         * @method sub
4216         * @private
4217         * @param {Function} fn  The function to subscribe to this CustomEvent
4218         * @param {Object}       obj     The CustomEvent's scope object
4219         * @param {Boolean}      bOverride       Whether or not to apply scope correction
4220         */
4221         var sub = function(fn, obj, bOverride) {
4222                 for (var p=0;p<me.pages.length;++p) {
4223                         var cal = me.pages[p];
4224                         cal[this.type + strEvent].subscribe(fn, obj, bOverride);
4225                 }
4226         };
4228         /**
4229         * Proxy unsubscriber to unsubscribe from the CalendarGroup's child Calendars' CustomEvents
4230         * @method unsub
4231         * @private
4232         * @param {Function} fn  The function to subscribe to this CustomEvent
4233         * @param {Object}       obj     The CustomEvent's scope object
4234         */
4235         var unsub = function(fn, obj) {
4236                 for (var p=0;p<me.pages.length;++p) {
4237                         var cal = me.pages[p];
4238                         cal[this.type + strEvent].unsubscribe(fn, obj);
4239                 }
4240         };
4241         
4242         var defEvents = YAHOO.widget.Calendar._EVENT_TYPES;
4244         /**
4245         * Fired before a selection is made
4246         * @event beforeSelectEvent
4247         */
4248         this.beforeSelectEvent = new YAHOO.util.CustomEvent(defEvents.BEFORE_SELECT);
4249         this.beforeSelectEvent.subscribe = sub; this.beforeSelectEvent.unsubscribe = unsub;
4251         /**
4252         * Fired when a selection is made
4253         * @event selectEvent
4254         * @param {Array}        Array of Date field arrays in the format [YYYY, MM, DD].
4255         */
4256         this.selectEvent = new YAHOO.util.CustomEvent(defEvents.SELECT); 
4257         this.selectEvent.subscribe = sub; this.selectEvent.unsubscribe = unsub;
4259         /**
4260         * Fired before a selection is made
4261         * @event beforeDeselectEvent
4262         */
4263         this.beforeDeselectEvent = new YAHOO.util.CustomEvent(defEvents.BEFORE_DESELECT); 
4264         this.beforeDeselectEvent.subscribe = sub; this.beforeDeselectEvent.unsubscribe = unsub;
4266         /**
4267         * Fired when a selection is made
4268         * @event deselectEvent
4269         * @param {Array}        Array of Date field arrays in the format [YYYY, MM, DD].
4270         */
4271         this.deselectEvent = new YAHOO.util.CustomEvent(defEvents.DESELECT); 
4272         this.deselectEvent.subscribe = sub; this.deselectEvent.unsubscribe = unsub;
4273         
4274         /**
4275         * Fired when the Calendar page is changed
4276         * @event changePageEvent
4277         */
4278         this.changePageEvent = new YAHOO.util.CustomEvent(defEvents.CHANGE_PAGE); 
4279         this.changePageEvent.subscribe = sub; this.changePageEvent.unsubscribe = unsub;
4281         /**
4282         * Fired before the Calendar is rendered
4283         * @event beforeRenderEvent
4284         */
4285         this.beforeRenderEvent = new YAHOO.util.CustomEvent(defEvents.BEFORE_RENDER);
4286         this.beforeRenderEvent.subscribe = sub; this.beforeRenderEvent.unsubscribe = unsub;
4288         /**
4289         * Fired when the Calendar is rendered
4290         * @event renderEvent
4291         */
4292         this.renderEvent = new YAHOO.util.CustomEvent(defEvents.RENDER);
4293         this.renderEvent.subscribe = sub; this.renderEvent.unsubscribe = unsub;
4295         /**
4296         * Fired when the Calendar is reset
4297         * @event resetEvent
4298         */
4299         this.resetEvent = new YAHOO.util.CustomEvent(defEvents.RESET); 
4300         this.resetEvent.subscribe = sub; this.resetEvent.unsubscribe = unsub;
4302         /**
4303         * Fired when the Calendar is cleared
4304         * @event clearEvent
4305         */
4306         this.clearEvent = new YAHOO.util.CustomEvent(defEvents.CLEAR);
4307         this.clearEvent.subscribe = sub; this.clearEvent.unsubscribe = unsub;
4312 * The default Config handler for the "pages" property
4313 * @method configPages
4314 * @param {String} type  The CustomEvent type (usually the property name)
4315 * @param {Object[]}     args    The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
4316 * @param {Object} obj   The scope object. For configuration handlers, this will usually equal the owner.
4318 YAHOO.widget.CalendarGroup.prototype.configPages = function(type, args, obj) {
4319         var pageCount = args[0];
4321         var cfgPageDate = YAHOO.widget.CalendarGroup._DEFAULT_CONFIG.PAGEDATE.key;
4323         // Define literals outside loop 
4324         var sep = "_";
4325         var groupCalClass = "groupcal";
4327         var firstClass = "first-of-type";
4328         var lastClass = "last-of-type";
4330         for (var p=0;p<pageCount;++p) {
4331                 var calId = this.id + sep + p;
4332                 var calContainerId = this.containerId + sep + p;
4334                 var childConfig = this.cfg.getConfig();
4335                 childConfig.close = false;
4336                 childConfig.title = false;
4338                 var cal = this.constructChild(calId, calContainerId, childConfig);
4339                 var caldate = cal.cfg.getProperty(cfgPageDate);
4340                 this._setMonthOnDate(caldate, caldate.getMonth() + p);
4341                 cal.cfg.setProperty(cfgPageDate, caldate);
4343                 YAHOO.util.Dom.removeClass(cal.oDomContainer, this.Style.CSS_SINGLE);
4344                 YAHOO.util.Dom.addClass(cal.oDomContainer, groupCalClass);
4346                 if (p===0) {
4347                         YAHOO.util.Dom.addClass(cal.oDomContainer, firstClass);
4348                 }
4350                 if (p==(pageCount-1)) {
4351                         YAHOO.util.Dom.addClass(cal.oDomContainer, lastClass);
4352                 }
4354                 cal.parent = this;
4355                 cal.index = p; 
4357                 this.pages[this.pages.length] = cal;
4358         }
4362 * The default Config handler for the "pagedate" property
4363 * @method configPageDate
4364 * @param {String} type  The CustomEvent type (usually the property name)
4365 * @param {Object[]}     args    The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
4366 * @param {Object} obj   The scope object. For configuration handlers, this will usually equal the owner.
4368 YAHOO.widget.CalendarGroup.prototype.configPageDate = function(type, args, obj) {
4369         var val = args[0];
4370         var firstPageDate;
4371         
4372         var cfgPageDate = YAHOO.widget.CalendarGroup._DEFAULT_CONFIG.PAGEDATE.key;
4373         
4374         for (var p=0;p<this.pages.length;++p) {
4375                 var cal = this.pages[p];
4376                 if (p === 0) {
4377                         firstPageDate = cal._parsePageDate(val);
4378                         cal.cfg.setProperty(cfgPageDate, firstPageDate);
4379                 } else {
4380                         var pageDate = new Date(firstPageDate);
4381                         this._setMonthOnDate(pageDate, pageDate.getMonth() + p);
4382                         cal.cfg.setProperty(cfgPageDate, pageDate);
4383                 }
4384         }
4388 * The default Config handler for the CalendarGroup "selected" property
4389 * @method configSelected
4390 * @param {String} type  The CustomEvent type (usually the property name)
4391 * @param {Object[]}     args    The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
4392 * @param {Object} obj   The scope object. For configuration handlers, this will usually equal the owner.
4394 YAHOO.widget.CalendarGroup.prototype.configSelected = function(type, args, obj) {
4395         var cfgSelected = YAHOO.widget.CalendarGroup._DEFAULT_CONFIG.SELECTED.key;
4396         this.delegateConfig(type, args, obj);
4397         var selected = (this.pages.length > 0) ? this.pages[0].cfg.getProperty(cfgSelected) : []; 
4398         this.cfg.setProperty(cfgSelected, selected, true);
4403 * Delegates a configuration property to the CustomEvents associated with the CalendarGroup's children
4404 * @method delegateConfig
4405 * @param {String} type  The CustomEvent type (usually the property name)
4406 * @param {Object[]}     args    The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
4407 * @param {Object} obj   The scope object. For configuration handlers, this will usually equal the owner.
4409 YAHOO.widget.CalendarGroup.prototype.delegateConfig = function(type, args, obj) {
4410         var val = args[0];
4411         var cal;
4413         for (var p=0;p<this.pages.length;p++) {
4414                 cal = this.pages[p];
4415                 cal.cfg.setProperty(type, val);
4416         }
4421 * Adds a function to all child Calendars within this CalendarGroup.
4422 * @method setChildFunction
4423 * @param {String}               fnName          The name of the function
4424 * @param {Function}             fn                      The function to apply to each Calendar page object
4426 YAHOO.widget.CalendarGroup.prototype.setChildFunction = function(fnName, fn) {
4427         var pageCount = this.cfg.getProperty(YAHOO.widget.CalendarGroup._DEFAULT_CONFIG.PAGES.key);
4429         for (var p=0;p<pageCount;++p) {
4430                 this.pages[p][fnName] = fn;
4431         }
4435 * Calls a function within all child Calendars within this CalendarGroup.
4436 * @method callChildFunction
4437 * @param {String}               fnName          The name of the function
4438 * @param {Array}                args            The arguments to pass to the function
4440 YAHOO.widget.CalendarGroup.prototype.callChildFunction = function(fnName, args) {
4441         var pageCount = this.cfg.getProperty(YAHOO.widget.CalendarGroup._DEFAULT_CONFIG.PAGES.key);
4443         for (var p=0;p<pageCount;++p) {
4444                 var page = this.pages[p];
4445                 if (page[fnName]) {
4446                         var fn = page[fnName];
4447                         fn.call(page, args);
4448                 }
4449         }       
4453 * Constructs a child calendar. This method can be overridden if a subclassed version of the default
4454 * calendar is to be used.
4455 * @method constructChild
4456 * @param {String}       id                      The id of the table element that will represent the calendar widget
4457 * @param {String}       containerId     The id of the container div element that will wrap the calendar table
4458 * @param {Object}       config          The configuration object containing the Calendar's arguments
4459 * @return {YAHOO.widget.Calendar}       The YAHOO.widget.Calendar instance that is constructed
4461 YAHOO.widget.CalendarGroup.prototype.constructChild = function(id,containerId,config) {
4462         var container = document.getElementById(containerId);
4463         if (! container) {
4464                 container = document.createElement("div");
4465                 container.id = containerId;
4466                 this.oDomContainer.appendChild(container);
4467         }
4468         return new YAHOO.widget.Calendar(id,containerId,config);
4473 * Sets the calendar group's month explicitly. This month will be set into the first
4474 * page of the multi-page calendar, and all other months will be iterated appropriately.
4475 * @method setMonth
4476 * @param {Number}       month           The numeric month, from 0 (January) to 11 (December)
4478 YAHOO.widget.CalendarGroup.prototype.setMonth = function(month) {
4479         month = parseInt(month, 10);
4480         var currYear;
4481         
4482         var cfgPageDate = YAHOO.widget.CalendarGroup._DEFAULT_CONFIG.PAGEDATE.key;
4483         
4484         for (var p=0; p<this.pages.length; ++p) {
4485                 var cal = this.pages[p];
4486                 var pageDate = cal.cfg.getProperty(cfgPageDate);
4487                 if (p === 0) {
4488                         currYear = pageDate.getFullYear();
4489                 } else {
4490                         pageDate.setYear(currYear);
4491                 }
4492                 this._setMonthOnDate(pageDate, month+p); 
4493                 cal.cfg.setProperty(cfgPageDate, pageDate);
4494         }
4498 * Sets the calendar group's year explicitly. This year will be set into the first
4499 * page of the multi-page calendar, and all other months will be iterated appropriately.
4500 * @method setYear
4501 * @param {Number}       year            The numeric 4-digit year
4503 YAHOO.widget.CalendarGroup.prototype.setYear = function(year) {
4505         var cfgPageDate = YAHOO.widget.CalendarGroup._DEFAULT_CONFIG.PAGEDATE.key;
4507         year = parseInt(year, 10);
4508         for (var p=0;p<this.pages.length;++p) {
4509                 var cal = this.pages[p];
4510                 var pageDate = cal.cfg.getProperty(cfgPageDate);
4512                 if ((pageDate.getMonth()+1) == 1 && p>0) {
4513                         year+=1;
4514                 }
4515                 cal.setYear(year);
4516         }
4519 * Calls the render function of all child calendars within the group.
4520 * @method render
4522 YAHOO.widget.CalendarGroup.prototype.render = function() {
4523         this.renderHeader();
4524         for (var p=0;p<this.pages.length;++p) {
4525                 var cal = this.pages[p];
4526                 cal.render();
4527         }
4528         this.renderFooter();
4532 * Selects a date or a collection of dates on the current calendar. This method, by default,
4533 * does not call the render method explicitly. Once selection has completed, render must be 
4534 * called for the changes to be reflected visually.
4535 * @method select
4536 * @param        {String/Date/Date[]}    date    The date string of dates to select in the current calendar. Valid formats are
4537 *                                                               individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
4538 *                                                               Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
4539 *                                                               This method can also take a JavaScript Date object or an array of Date objects.
4540 * @return       {Date[]}                        Array of JavaScript Date objects representing all individual dates that are currently selected.
4542 YAHOO.widget.CalendarGroup.prototype.select = function(date) {
4543         for (var p=0;p<this.pages.length;++p) {
4544                 var cal = this.pages[p];
4545                 cal.select(date);
4546         }
4547         return this.getSelectedDates();
4551 * 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.
4552 * The value of the MULTI_SELECT Configuration attribute will determine the set of dates which get selected. 
4553 * <ul>
4554 *    <li>If MULTI_SELECT is false, selectCell will select the cell at the specified index for only the last displayed Calendar page.</li>
4555 *    <li>If MULTI_SELECT is true, selectCell will select the cell at the specified index, on each displayed Calendar page.</li>
4556 * </ul>
4557 * @method selectCell
4558 * @param        {Number}        cellIndex       The index of the cell to be selected. 
4559 * @return       {Date[]}        Array of JavaScript Date objects representing all individual dates that are currently selected.
4561 YAHOO.widget.CalendarGroup.prototype.selectCell = function(cellIndex) {
4562         for (var p=0;p<this.pages.length;++p) {
4563                 var cal = this.pages[p];
4564                 cal.selectCell(cellIndex);
4565         }
4566         return this.getSelectedDates();
4570 * Deselects a date or a collection of dates on the current calendar. This method, by default,
4571 * does not call the render method explicitly. Once deselection has completed, render must be 
4572 * called for the changes to be reflected visually.
4573 * @method deselect
4574 * @param        {String/Date/Date[]}    date    The date string of dates to deselect in the current calendar. Valid formats are
4575 *                                                               individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
4576 *                                                               Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
4577 *                                                               This method can also take a JavaScript Date object or an array of Date objects. 
4578 * @return       {Date[]}                        Array of JavaScript Date objects representing all individual dates that are currently selected.
4580 YAHOO.widget.CalendarGroup.prototype.deselect = function(date) {
4581         for (var p=0;p<this.pages.length;++p) {
4582                 var cal = this.pages[p];
4583                 cal.deselect(date);
4584         }
4585         return this.getSelectedDates();
4589 * Deselects all dates on the current calendar.
4590 * @method deselectAll
4591 * @return {Date[]}              Array of JavaScript Date objects representing all individual dates that are currently selected.
4592 *                                               Assuming that this function executes properly, the return value should be an empty array.
4593 *                                               However, the empty array is returned for the sake of being able to check the selection status
4594 *                                               of the calendar.
4596 YAHOO.widget.CalendarGroup.prototype.deselectAll = function() {
4597         for (var p=0;p<this.pages.length;++p) {
4598                 var cal = this.pages[p];
4599                 cal.deselectAll();
4600         }
4601         return this.getSelectedDates();
4605 * 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.
4606 * deselectCell will deselect the cell at the specified index on each displayed Calendar page.
4608 * @method deselectCell
4609 * @param        {Number}        cellIndex       The index of the cell to deselect. 
4610 * @return       {Date[]}        Array of JavaScript Date objects representing all individual dates that are currently selected.
4612 YAHOO.widget.CalendarGroup.prototype.deselectCell = function(cellIndex) {
4613         for (var p=0;p<this.pages.length;++p) {
4614                 var cal = this.pages[p];
4615                 cal.deselectCell(cellIndex);
4616         }
4617         return this.getSelectedDates();
4621 * Resets the calendar widget to the originally selected month and year, and 
4622 * sets the calendar to the initial selection(s).
4623 * @method reset
4625 YAHOO.widget.CalendarGroup.prototype.reset = function() {
4626         for (var p=0;p<this.pages.length;++p) {
4627                 var cal = this.pages[p];
4628                 cal.reset();
4629         }
4633 * Clears the selected dates in the current calendar widget and sets the calendar
4634 * to the current month and year.
4635 * @method clear
4637 YAHOO.widget.CalendarGroup.prototype.clear = function() {
4638         for (var p=0;p<this.pages.length;++p) {
4639                 var cal = this.pages[p];
4640                 cal.clear();
4641         }
4645 * Navigates to the next month page in the calendar widget.
4646 * @method nextMonth
4648 YAHOO.widget.CalendarGroup.prototype.nextMonth = function() {
4649         for (var p=0;p<this.pages.length;++p) {
4650                 var cal = this.pages[p];
4651                 cal.nextMonth();
4652         }
4656 * Navigates to the previous month page in the calendar widget.
4657 * @method previousMonth
4659 YAHOO.widget.CalendarGroup.prototype.previousMonth = function() {
4660         for (var p=this.pages.length-1;p>=0;--p) {
4661                 var cal = this.pages[p];
4662                 cal.previousMonth();
4663         }
4667 * Navigates to the next year in the currently selected month in the calendar widget.
4668 * @method nextYear
4670 YAHOO.widget.CalendarGroup.prototype.nextYear = function() {
4671         for (var p=0;p<this.pages.length;++p) {
4672                 var cal = this.pages[p];
4673                 cal.nextYear();
4674         }
4678 * Navigates to the previous year in the currently selected month in the calendar widget.
4679 * @method previousYear
4681 YAHOO.widget.CalendarGroup.prototype.previousYear = function() {
4682         for (var p=0;p<this.pages.length;++p) {
4683                 var cal = this.pages[p];
4684                 cal.previousYear();
4685         }
4690 * Gets the list of currently selected dates from the calendar.
4691 * @return                       An array of currently selected JavaScript Date objects.
4692 * @type Date[]
4694 YAHOO.widget.CalendarGroup.prototype.getSelectedDates = function() { 
4695         var returnDates = [];
4696         var selected = this.cfg.getProperty(YAHOO.widget.CalendarGroup._DEFAULT_CONFIG.SELECTED.key);
4697         for (var d=0;d<selected.length;++d) {
4698                 var dateArray = selected[d];
4700                 var date = new Date(dateArray[0],dateArray[1]-1,dateArray[2]);
4701                 returnDates.push(date);
4702         }
4704         returnDates.sort( function(a,b) { return a-b; } );
4705         return returnDates;
4709 * Adds a renderer to the render stack. The function reference passed to this method will be executed
4710 * when a date cell matches the conditions specified in the date string for this renderer.
4711 * @method addRenderer
4712 * @param        {String}        sDates          A date string to associate with the specified renderer. Valid formats
4713 *                                                                       include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
4714 * @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
4716 YAHOO.widget.CalendarGroup.prototype.addRenderer = function(sDates, fnRender) {
4717         for (var p=0;p<this.pages.length;++p) {
4718                 var cal = this.pages[p];
4719                 cal.addRenderer(sDates, fnRender);
4720         }
4724 * Adds a month to the render stack. The function reference passed to this method will be executed
4725 * when a date cell matches the month passed to this method.
4726 * @method addMonthRenderer
4727 * @param        {Number}        month           The month (1-12) to associate with this renderer
4728 * @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
4730 YAHOO.widget.CalendarGroup.prototype.addMonthRenderer = function(month, fnRender) {
4731         for (var p=0;p<this.pages.length;++p) {
4732                 var cal = this.pages[p];
4733                 cal.addMonthRenderer(month, fnRender);
4734         }
4738 * Adds a weekday to the render stack. The function reference passed to this method will be executed
4739 * when a date cell matches the weekday passed to this method.
4740 * @method addWeekdayRenderer
4741 * @param        {Number}        weekday         The weekday (1-7) to associate with this renderer. 1=Sunday, 2=Monday etc.
4742 * @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
4744 YAHOO.widget.CalendarGroup.prototype.addWeekdayRenderer = function(weekday, fnRender) {
4745         for (var p=0;p<this.pages.length;++p) {
4746                 var cal = this.pages[p];
4747                 cal.addWeekdayRenderer(weekday, fnRender);
4748         }
4752 * Renders the header for the CalendarGroup.
4753 * @method renderHeader
4755 YAHOO.widget.CalendarGroup.prototype.renderHeader = function() {};
4758 * Renders a footer for the 2-up calendar container. By default, this method is
4759 * unimplemented.
4760 * @method renderFooter
4762 YAHOO.widget.CalendarGroup.prototype.renderFooter = function() {};
4765 * Adds the designated number of months to the current calendar month, and sets the current
4766 * calendar page date to the new month.
4767 * @method addMonths
4768 * @param {Number}       count   The number of months to add to the current calendar
4770 YAHOO.widget.CalendarGroup.prototype.addMonths = function(count) {
4771         this.callChildFunction("addMonths", count);
4776 * Subtracts the designated number of months from the current calendar month, and sets the current
4777 * calendar page date to the new month.
4778 * @method subtractMonths
4779 * @param {Number}       count   The number of months to subtract from the current calendar
4781 YAHOO.widget.CalendarGroup.prototype.subtractMonths = function(count) {
4782         this.callChildFunction("subtractMonths", count);
4786 * Adds the designated number of years to the current calendar, and sets the current
4787 * calendar page date to the new month.
4788 * @method addYears
4789 * @param {Number}       count   The number of years to add to the current calendar
4791 YAHOO.widget.CalendarGroup.prototype.addYears = function(count) {
4792         this.callChildFunction("addYears", count);
4796 * Subtcats the designated number of years from the current calendar, and sets the current
4797 * calendar page date to the new month.
4798 * @method subtractYears
4799 * @param {Number}       count   The number of years to subtract from the current calendar
4801 YAHOO.widget.CalendarGroup.prototype.subtractYears = function(count) {
4802         this.callChildFunction("subtractYears", count);
4806 * Shows the CalendarGroup's outer container.
4807 * @method show
4809 YAHOO.widget.CalendarGroup.prototype.show = function() {
4810         this.oDomContainer.style.display = "block";
4811         if (YAHOO.env.ua.opera) {
4812                 this._fixWidth();
4813         }
4817 * Sets the month on a Date object, taking into account year rollover if the month is less than 0 or greater than 11.
4818 * 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
4819 * @method       _setMonthOnDate
4820 * @private
4821 * @param        {Date}  date    The Date object on which to set the month index
4822 * @param        {Number}        iMonth  The month index to set
4824 YAHOO.widget.CalendarGroup.prototype._setMonthOnDate = function(date, iMonth) {
4825         // Bug in Safari 1.3, 2.0 (WebKit build < 420), Date.setMonth does not work consistently if iMonth is not 0-11
4826         if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420 && (iMonth < 0 || iMonth > 11)) {
4827                 var DM = YAHOO.widget.DateMath;
4828                 var newDate = DM.add(date, DM.MONTH, iMonth-date.getMonth());
4829                 date.setTime(newDate.getTime());
4830         } else {
4831                 date.setMonth(iMonth);
4832         }
4836  * Fixes the width of the CalendarGroup container element, to account for miswrapped floats
4837  * @method _fixWidth
4838  * @private
4839  */
4840 YAHOO.widget.CalendarGroup.prototype._fixWidth = function() {
4841         var startW = this.oDomContainer.offsetWidth;
4842         var w = 0;
4843         for (var p=0;p<this.pages.length;++p) {
4844                 var cal = this.pages[p];
4845                 w += cal.oDomContainer.offsetWidth;
4846         }
4847         if (w > 0) {
4848                 this.oDomContainer.style.width = w + "px";
4849         }
4854 * CSS class representing the container for the calendar
4855 * @property YAHOO.widget.CalendarGroup.CSS_CONTAINER
4856 * @static
4857 * @final
4858 * @type String
4860 YAHOO.widget.CalendarGroup.CSS_CONTAINER = "yui-calcontainer";
4863 * CSS class representing the container for the calendar
4864 * @property YAHOO.widget.CalendarGroup.CSS_MULTI_UP
4865 * @static
4866 * @final
4867 * @type String
4869 YAHOO.widget.CalendarGroup.CSS_MULTI_UP = "multi";
4872 * CSS class representing the title for the 2-up calendar
4873 * @property YAHOO.widget.CalendarGroup.CSS_2UPTITLE
4874 * @static
4875 * @final
4876 * @type String
4878 YAHOO.widget.CalendarGroup.CSS_2UPTITLE = "title";
4881 * CSS class representing the close icon for the 2-up calendar
4882 * @property YAHOO.widget.CalendarGroup.CSS_2UPCLOSE
4883 * @static
4884 * @final
4885 * @deprecated   Along with Calendar.IMG_ROOT and NAV_ARROW_LEFT, NAV_ARROW_RIGHT configuration properties.
4886 *                                       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
4887 * @type String
4889 YAHOO.widget.CalendarGroup.CSS_2UPCLOSE = "close-icon";
4891 YAHOO.lang.augmentProto(YAHOO.widget.CalendarGroup, YAHOO.widget.Calendar, "buildDayLabel",
4892                                                                                                                                  "buildMonthLabel",
4893                                                                                                                                  "renderOutOfBoundsDate",
4894                                                                                                                                  "renderRowHeader",
4895                                                                                                                                  "renderRowFooter",
4896                                                                                                                                  "renderCellDefault",
4897                                                                                                                                  "styleCellDefault",
4898                                                                                                                                  "renderCellStyleHighlight1",
4899                                                                                                                                  "renderCellStyleHighlight2",
4900                                                                                                                                  "renderCellStyleHighlight3",
4901                                                                                                                                  "renderCellStyleHighlight4",
4902                                                                                                                                  "renderCellStyleToday",
4903                                                                                                                                  "renderCellStyleSelected",
4904                                                                                                                                  "renderCellNotThisMonth",
4905                                                                                                                                  "renderBodyCellRestricted",
4906                                                                                                                                  "initStyles",
4907                                                                                                                                  "configTitle",
4908                                                                                                                                  "configClose",
4909                                                                                                                                  "configIframe",
4910                                                                                                                                  "hide",
4911                                                                                                                                  "browser");
4914 * The set of default Config property keys and values for the CalendarGroup
4915 * @property YAHOO.widget.CalendarGroup._DEFAULT_CONFIG
4916 * @final
4917 * @static
4918 * @private
4919 * @type Object
4921 YAHOO.widget.CalendarGroup._DEFAULT_CONFIG = YAHOO.widget.Calendar._DEFAULT_CONFIG;
4922 YAHOO.widget.CalendarGroup._DEFAULT_CONFIG.PAGES = {key:"pages", value:2};
4925 * Returns a string representation of the object.
4926 * @method toString
4927 * @return {String}      A string representation of the CalendarGroup object.
4929 YAHOO.widget.CalendarGroup.prototype.toString = function() {
4930         return "CalendarGroup " + this.id;
4933 YAHOO.widget.CalGrp = YAHOO.widget.CalendarGroup;
4936 * @class YAHOO.widget.Calendar2up
4937 * @extends YAHOO.widget.CalendarGroup
4938 * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
4940 YAHOO.widget.Calendar2up = function(id, containerId, config) {
4941         this.init(id, containerId, config);
4944 YAHOO.extend(YAHOO.widget.Calendar2up, YAHOO.widget.CalendarGroup);
4947 * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
4949 YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up;
4951 YAHOO.register("calendar", YAHOO.widget.Calendar, {version: "2.3.0", build: "442"});