Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('yui2-datemath', function(Y) { Y.use('yui2-calendar'); }, '3.3.0' ,{"requires": ["yui2-yahoo"]});
2
YUI.add('yui2-calendar', function(Y) {
3
    var YAHOO    = Y.YUI2;
4
    /*
5
Copyright (c) 2011, Yahoo! Inc. All rights reserved.
6
Code licensed under the BSD License:
7
http://developer.yahoo.com/yui/license.html
8
version: 2.9.0
9
*/
10
(function () {
11
 
12
    /**
13
    * Config is a utility used within an Object to allow the implementer to
14
    * maintain a list of local configuration properties and listen for changes
15
    * to those properties dynamically using CustomEvent. The initial values are
16
    * also maintained so that the configuration can be reset at any given point
17
    * to its initial state.
18
    * @namespace YAHOO.util
19
    * @class Config
20
    * @constructor
21
    * @param {Object} owner The owner Object to which this Config Object belongs
22
    */
23
    YAHOO.util.Config = function (owner) {
24
 
25
        if (owner) {
26
            this.init(owner);
27
        }
28
 
29
 
30
    };
31
 
32
 
33
    var Lang = YAHOO.lang,
34
        CustomEvent = YAHOO.util.CustomEvent,
35
        Config = YAHOO.util.Config;
36
 
37
 
38
    /**
39
     * Constant representing the CustomEvent type for the config changed event.
40
     * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT
41
     * @private
42
     * @static
43
     * @final
44
     */
45
    Config.CONFIG_CHANGED_EVENT = "configChanged";
46
 
47
    /**
48
     * Constant representing the boolean type string
49
     * @property YAHOO.util.Config.BOOLEAN_TYPE
50
     * @private
51
     * @static
52
     * @final
53
     */
54
    Config.BOOLEAN_TYPE = "boolean";
55
 
56
    Config.prototype = {
57
 
58
        /**
59
        * Object reference to the owner of this Config Object
60
        * @property owner
61
        * @type Object
62
        */
63
        owner: null,
64
 
65
        /**
66
        * Boolean flag that specifies whether a queue is currently
67
        * being executed
68
        * @property queueInProgress
69
        * @type Boolean
70
        */
71
        queueInProgress: false,
72
 
73
        /**
74
        * Maintains the local collection of configuration property objects and
75
        * their specified values
76
        * @property config
77
        * @private
78
        * @type Object
79
        */
80
        config: null,
81
 
82
        /**
83
        * Maintains the local collection of configuration property objects as
84
        * they were initially applied.
85
        * This object is used when resetting a property.
86
        * @property initialConfig
87
        * @private
88
        * @type Object
89
        */
90
        initialConfig: null,
91
 
92
        /**
93
        * Maintains the local, normalized CustomEvent queue
94
        * @property eventQueue
95
        * @private
96
        * @type Object
97
        */
98
        eventQueue: null,
99
 
100
        /**
101
        * Custom Event, notifying subscribers when Config properties are set
102
        * (setProperty is called without the silent flag
103
        * @event configChangedEvent
104
        */
105
        configChangedEvent: null,
106
 
107
        /**
108
        * Initializes the configuration Object and all of its local members.
109
        * @method init
110
        * @param {Object} owner The owner Object to which this Config
111
        * Object belongs
112
        */
113
        init: function (owner) {
114
 
115
            this.owner = owner;
116
 
117
            this.configChangedEvent =
118
                this.createEvent(Config.CONFIG_CHANGED_EVENT);
119
 
120
            this.configChangedEvent.signature = CustomEvent.LIST;
121
            this.queueInProgress = false;
122
            this.config = {};
123
            this.initialConfig = {};
124
            this.eventQueue = [];
125
 
126
        },
127
 
128
        /**
129
        * Validates that the value passed in is a Boolean.
130
        * @method checkBoolean
131
        * @param {Object} val The value to validate
132
        * @return {Boolean} true, if the value is valid
133
        */
134
        checkBoolean: function (val) {
135
            return (typeof val == Config.BOOLEAN_TYPE);
136
        },
137
 
138
        /**
139
        * Validates that the value passed in is a number.
140
        * @method checkNumber
141
        * @param {Object} val The value to validate
142
        * @return {Boolean} true, if the value is valid
143
        */
144
        checkNumber: function (val) {
145
            return (!isNaN(val));
146
        },
147
 
148
        /**
149
        * Fires a configuration property event using the specified value.
150
        * @method fireEvent
151
        * @private
152
        * @param {String} key The configuration property's name
153
        * @param {value} Object The value of the correct type for the property
154
        */
155
        fireEvent: function ( key, value ) {
156
            var property = this.config[key];
157
 
158
            if (property && property.event) {
159
                property.event.fire(value);
160
            }
161
        },
162
 
163
        /**
164
        * Adds a property to the Config Object's private config hash.
165
        * @method addProperty
166
        * @param {String} key The configuration property's name
167
        * @param {Object} propertyObject The Object containing all of this
168
        * property's arguments
169
        */
170
        addProperty: function ( key, propertyObject ) {
171
            key = key.toLowerCase();
172
 
173
            this.config[key] = propertyObject;
174
 
175
            propertyObject.event = this.createEvent(key, { scope: this.owner });
176
            propertyObject.event.signature = CustomEvent.LIST;
177
 
178
 
179
            propertyObject.key = key;
180
 
181
            if (propertyObject.handler) {
182
                propertyObject.event.subscribe(propertyObject.handler,
183
                    this.owner);
184
            }
185
 
186
            this.setProperty(key, propertyObject.value, true);
187
 
188
            if (! propertyObject.suppressEvent) {
189
                this.queueProperty(key, propertyObject.value);
190
            }
191
 
192
        },
193
 
194
        /**
195
        * Returns a key-value configuration map of the values currently set in
196
        * the Config Object.
197
        * @method getConfig
198
        * @return {Object} The current config, represented in a key-value map
199
        */
200
        getConfig: function () {
201
 
202
            var cfg = {},
203
                currCfg = this.config,
204
                prop,
205
                property;
206
 
207
            for (prop in currCfg) {
208
                if (Lang.hasOwnProperty(currCfg, prop)) {
209
                    property = currCfg[prop];
210
                    if (property && property.event) {
211
                        cfg[prop] = property.value;
212
                    }
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
            key = key.toLowerCase();
242
 
243
            var property = this.config[key];
244
 
245
            if (property && property.event) {
246
                if (key in this.initialConfig) {
247
                    this.setProperty(key, this.initialConfig[key]);
248
                    return true;
249
                }
250
            } else {
251
                return false;
252
            }
253
        },
254
 
255
        /**
256
        * Sets the value of a property. If the silent property is passed as
257
        * true, the property's event will not be fired.
258
        * @method setProperty
259
        * @param {String} key The name of the property
260
        * @param {String} value The value to set the property to
261
        * @param {Boolean} silent Whether the value should be set silently,
262
        * without firing the property event.
263
        * @return {Boolean} True, if the set was successful, false if it failed.
264
        */
265
        setProperty: function (key, value, silent) {
266
 
267
            var property;
268
 
269
            key = key.toLowerCase();
270
 
271
            if (this.queueInProgress && ! silent) {
272
                // Currently running through a queue...
273
                this.queueProperty(key,value);
274
                return true;
275
 
276
            } else {
277
                property = this.config[key];
278
                if (property && property.event) {
279
                    if (property.validator && !property.validator(value)) {
280
                        return false;
281
                    } else {
282
                        property.value = value;
283
                        if (! silent) {
284
                            this.fireEvent(key, value);
285
                            this.configChangedEvent.fire([key, value]);
286
                        }
287
                        return true;
288
                    }
289
                } else {
290
                    return false;
291
                }
292
            }
293
        },
294
 
295
        /**
296
        * Sets the value of a property and queues its event to execute. If the
297
        * event is already scheduled to execute, it is
298
        * moved from its current position to the end of the queue.
299
        * @method queueProperty
300
        * @param {String} key The name of the property
301
        * @param {String} value The value to set the property to
302
        * @return {Boolean}  true, if the set was successful, false if
303
        * it failed.
304
        */
305
        queueProperty: function (key, value) {
306
 
307
            key = key.toLowerCase();
308
 
309
            var property = this.config[key],
310
                foundDuplicate = false,
311
                iLen,
312
                queueItem,
313
                queueItemKey,
314
                queueItemValue,
315
                sLen,
316
                supercedesCheck,
317
                qLen,
318
                queueItemCheck,
319
                queueItemCheckKey,
320
                queueItemCheckValue,
321
                i,
322
                s,
323
                q;
324
 
325
            if (property && property.event) {
326
 
327
                if (!Lang.isUndefined(value) && property.validator &&
328
                    !property.validator(value)) { // validator
329
                    return false;
330
                } else {
331
 
332
                    if (!Lang.isUndefined(value)) {
333
                        property.value = value;
334
                    } else {
335
                        value = property.value;
336
                    }
337
 
338
                    foundDuplicate = false;
339
                    iLen = this.eventQueue.length;
340
 
341
                    for (i = 0; i < iLen; i++) {
342
                        queueItem = this.eventQueue[i];
343
 
344
                        if (queueItem) {
345
                            queueItemKey = queueItem[0];
346
                            queueItemValue = queueItem[1];
347
 
348
                            if (queueItemKey == key) {
349
 
350
                                /*
351
                                    found a dupe... push to end of queue, null
352
                                    current item, and break
353
                                */
354
 
355
                                this.eventQueue[i] = null;
356
 
357
                                this.eventQueue.push(
358
                                    [key, (!Lang.isUndefined(value) ?
359
                                    value : queueItemValue)]);
360
 
361
                                foundDuplicate = true;
362
                                break;
363
                            }
364
                        }
365
                    }
366
 
367
                    // this is a refire, or a new property in the queue
368
 
369
                    if (! foundDuplicate && !Lang.isUndefined(value)) {
370
                        this.eventQueue.push([key, value]);
371
                    }
372
                }
373
 
374
                if (property.supercedes) {
375
 
376
                    sLen = property.supercedes.length;
377
 
378
                    for (s = 0; s < sLen; s++) {
379
 
380
                        supercedesCheck = property.supercedes[s];
381
                        qLen = this.eventQueue.length;
382
 
383
                        for (q = 0; q < qLen; q++) {
384
                            queueItemCheck = this.eventQueue[q];
385
 
386
                            if (queueItemCheck) {
387
                                queueItemCheckKey = queueItemCheck[0];
388
                                queueItemCheckValue = queueItemCheck[1];
389
 
390
                                if (queueItemCheckKey ==
391
                                    supercedesCheck.toLowerCase() ) {
392
 
393
                                    this.eventQueue.push([queueItemCheckKey,
394
                                        queueItemCheckValue]);
395
 
396
                                    this.eventQueue[q] = null;
397
                                    break;
398
 
399
                                }
400
                            }
401
                        }
402
                    }
403
                }
404
 
405
 
406
                return true;
407
            } else {
408
                return false;
409
            }
410
        },
411
 
412
        /**
413
        * Fires the event for a property using the property's current value.
414
        * @method refireEvent
415
        * @param {String} key The name of the property
416
        */
417
        refireEvent: function (key) {
418
 
419
            key = key.toLowerCase();
420
 
421
            var property = this.config[key];
422
 
423
            if (property && property.event &&
424
 
425
                !Lang.isUndefined(property.value)) {
426
 
427
                if (this.queueInProgress) {
428
 
429
                    this.queueProperty(key);
430
 
431
                } else {
432
 
433
                    this.fireEvent(key, property.value);
434
 
435
                }
436
 
437
            }
438
        },
439
 
440
        /**
441
        * Applies a key-value Object literal to the configuration, replacing
442
        * any existing values, and queueing the property events.
443
        * Although the values will be set, fireQueue() must be called for their
444
        * associated events to execute.
445
        * @method applyConfig
446
        * @param {Object} userConfig The configuration Object literal
447
        * @param {Boolean} init  When set to true, the initialConfig will
448
        * be set to the userConfig passed in, so that calling a reset will
449
        * reset the properties to the passed values.
450
        */
451
        applyConfig: function (userConfig, init) {
452
 
453
            var sKey,
454
                oConfig;
455
 
456
            if (init) {
457
                oConfig = {};
458
                for (sKey in userConfig) {
459
                    if (Lang.hasOwnProperty(userConfig, sKey)) {
460
                        oConfig[sKey.toLowerCase()] = userConfig[sKey];
461
                    }
462
                }
463
                this.initialConfig = oConfig;
464
            }
465
 
466
            for (sKey in userConfig) {
467
                if (Lang.hasOwnProperty(userConfig, sKey)) {
468
                    this.queueProperty(sKey, userConfig[sKey]);
469
                }
470
            }
471
        },
472
 
473
        /**
474
        * Refires the events for all configuration properties using their
475
        * current values.
476
        * @method refresh
477
        */
478
        refresh: function () {
479
 
480
            var prop;
481
 
482
            for (prop in this.config) {
483
                if (Lang.hasOwnProperty(this.config, prop)) {
484
                    this.refireEvent(prop);
485
                }
486
            }
487
        },
488
 
489
        /**
490
        * Fires the normalized list of queued property change events
491
        * @method fireQueue
492
        */
493
        fireQueue: function () {
494
 
495
            var i,
496
                queueItem,
497
                key,
498
                value,
499
                property;
500
 
501
            this.queueInProgress = true;
502
            for (i = 0;i < this.eventQueue.length; i++) {
503
                queueItem = this.eventQueue[i];
504
                if (queueItem) {
505
 
506
                    key = queueItem[0];
507
                    value = queueItem[1];
508
                    property = this.config[key];
509
 
510
                    property.value = value;
511
 
512
                    // Clear out queue entry, to avoid it being
513
                    // re-added to the queue by any queueProperty/supercedes
514
                    // calls which are invoked during fireEvent
515
                    this.eventQueue[i] = null;
516
 
517
                    this.fireEvent(key,value);
518
                }
519
            }
520
 
521
            this.queueInProgress = false;
522
            this.eventQueue = [];
523
        },
524
 
525
        /**
526
        * Subscribes an external handler to the change event for any
527
        * given property.
528
        * @method subscribeToConfigEvent
529
        * @param {String} key The property name
530
        * @param {Function} handler The handler function to use subscribe to
531
        * the property's event
532
        * @param {Object} obj The Object to use for scoping the event handler
533
        * (see CustomEvent documentation)
534
        * @param {Boolean} overrideContext Optional. If true, will override
535
        * "this" within the handler to map to the scope Object passed into the
536
        * method.
537
        * @return {Boolean} True, if the subscription was successful,
538
        * otherwise false.
539
        */
540
        subscribeToConfigEvent: function (key, handler, obj, overrideContext) {
541
 
542
            var property = this.config[key.toLowerCase()];
543
 
544
            if (property && property.event) {
545
                if (!Config.alreadySubscribed(property.event, handler, obj)) {
546
                    property.event.subscribe(handler, obj, overrideContext);
547
                }
548
                return true;
549
            } else {
550
                return false;
551
            }
552
 
553
        },
554
 
555
        /**
556
        * Unsubscribes an external handler from the change event for any
557
        * given property.
558
        * @method unsubscribeFromConfigEvent
559
        * @param {String} key The property name
560
        * @param {Function} handler The handler function to use subscribe to
561
        * the property's event
562
        * @param {Object} obj The Object to use for scoping the event
563
        * handler (see CustomEvent documentation)
564
        * @return {Boolean} True, if the unsubscription was successful,
565
        * otherwise false.
566
        */
567
        unsubscribeFromConfigEvent: function (key, handler, obj) {
568
            var property = this.config[key.toLowerCase()];
569
            if (property && property.event) {
570
                return property.event.unsubscribe(handler, obj);
571
            } else {
572
                return false;
573
            }
574
        },
575
 
576
        /**
577
        * Returns a string representation of the Config object
578
        * @method toString
579
        * @return {String} The Config object in string format.
580
        */
581
        toString: function () {
582
            var output = "Config";
583
            if (this.owner) {
584
                output += " [" + this.owner.toString() + "]";
585
            }
586
            return output;
587
        },
588
 
589
        /**
590
        * Returns a string representation of the Config object's current
591
        * CustomEvent queue
592
        * @method outputEventQueue
593
        * @return {String} The string list of CustomEvents currently queued
594
        * for execution
595
        */
596
        outputEventQueue: function () {
597
 
598
            var output = "",
599
                queueItem,
600
                q,
601
                nQueue = this.eventQueue.length;
602
 
603
            for (q = 0; q < nQueue; q++) {
604
                queueItem = this.eventQueue[q];
605
                if (queueItem) {
606
                    output += queueItem[0] + "=" + queueItem[1] + ", ";
607
                }
608
            }
609
            return output;
610
        },
611
 
612
        /**
613
        * Sets all properties to null, unsubscribes all listeners from each
614
        * property's change event and all listeners from the configChangedEvent.
615
        * @method destroy
616
        */
617
        destroy: function () {
618
 
619
            var oConfig = this.config,
620
                sProperty,
621
                oProperty;
622
 
623
 
624
            for (sProperty in oConfig) {
625
 
626
                if (Lang.hasOwnProperty(oConfig, sProperty)) {
627
 
628
                    oProperty = oConfig[sProperty];
629
 
630
                    oProperty.event.unsubscribeAll();
631
                    oProperty.event = null;
632
 
633
                }
634
 
635
            }
636
 
637
            this.configChangedEvent.unsubscribeAll();
638
 
639
            this.configChangedEvent = null;
640
            this.owner = null;
641
            this.config = null;
642
            this.initialConfig = null;
643
            this.eventQueue = null;
644
 
645
        }
646
 
647
    };
648
 
649
 
650
 
651
    /**
652
    * Checks to determine if a particular function/Object pair are already
653
    * subscribed to the specified CustomEvent
654
    * @method YAHOO.util.Config.alreadySubscribed
655
    * @static
656
    * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check
657
    * the subscriptions
658
    * @param {Function} fn The function to look for in the subscribers list
659
    * @param {Object} obj The execution scope Object for the subscription
660
    * @return {Boolean} true, if the function/Object pair is already subscribed
661
    * to the CustomEvent passed in
662
    */
663
    Config.alreadySubscribed = function (evt, fn, obj) {
664
 
665
        var nSubscribers = evt.subscribers.length,
666
            subsc,
667
            i;
668
 
669
        if (nSubscribers > 0) {
670
            i = nSubscribers - 1;
671
            do {
672
                subsc = evt.subscribers[i];
673
                if (subsc && subsc.obj == obj && subsc.fn == fn) {
674
                    return true;
675
                }
676
            }
677
            while (i--);
678
        }
679
 
680
        return false;
681
 
682
    };
683
 
684
    YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider);
685
 
686
}());
687
/**
688
* The datemath module provides utility methods for basic JavaScript Date object manipulation and
689
* comparison.
690
*
691
* @module datemath
692
*/
693
 
694
/**
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
699
*/
700
YAHOO.widget.DateMath = {
701
    /**
702
    * Constant field representing Day
703
    * @property DAY
704
    * @static
705
    * @final
706
    * @type String
707
    */
708
    DAY : "D",
709
 
710
    /**
711
    * Constant field representing Week
712
    * @property WEEK
713
    * @static
714
    * @final
715
    * @type String
716
    */
717
    WEEK : "W",
718
 
719
    /**
720
    * Constant field representing Year
721
    * @property YEAR
722
    * @static
723
    * @final
724
    * @type String
725
    */
726
    YEAR : "Y",
727
 
728
    /**
729
    * Constant field representing Month
730
    * @property MONTH
731
    * @static
732
    * @final
733
    * @type String
734
    */
735
    MONTH : "M",
736
 
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,
745
 
746
    /**
747
     * Constant field representing the date in first week of January
748
     * which identifies the first week of the year.
749
     * <p>
750
     * In the U.S, Jan 1st is normally used based on a Sunday start of week.
751
     * ISO 8601, used widely throughout Europe, uses Jan 4th, based on a Monday start of week.
752
     * </p>
753
     * @property WEEK_ONE_JAN_DATE
754
     * @static
755
     * @type Number
756
     */
757
    WEEK_ONE_JAN_DATE : 1,
758
 
759
    /**
760
    * Adds the specified amount of time to the this instance.
761
    * @method add
762
    * @param {Date} date The JavaScript Date object to perform addition on
763
    * @param {String} field The field constant to be used for performing addition.
764
    * @param {Number} amount The number of units (measured in the field constant) to add to the date.
765
    * @return {Date} The resulting Date object
766
    */
767
    add : function(date, field, amount) {
768
        var d = new Date(date.getTime());
769
        switch (field) {
770
            case this.MONTH:
771
                var newMonth = date.getMonth() + amount;
772
                var years = 0;
773
 
774
                if (newMonth < 0) {
775
                    while (newMonth < 0) {
776
                        newMonth += 12;
777
                        years -= 1;
778
                    }
779
                } else if (newMonth > 11) {
780
                    while (newMonth > 11) {
781
                        newMonth -= 12;
782
                        years += 1;
783
                    }
784
                }
785
 
786
                d.setMonth(newMonth);
787
                d.setFullYear(date.getFullYear() + years);
788
                break;
789
            case this.DAY:
790
                this._addDays(d, amount);
791
                // d.setDate(date.getDate() + amount);
792
                break;
793
            case this.YEAR:
794
                d.setFullYear(date.getFullYear() + amount);
795
                break;
796
            case this.WEEK:
797
                this._addDays(d, (amount * 7));
798
                // d.setDate(date.getDate() + (amount * 7));
799
                break;
800
        }
801
        return d;
802
    },
803
 
804
    /**
805
     * Private helper method to account for bug in Safari 2 (webkit < 420)
806
     * when Date.setDate(n) is called with n less than -128 or greater than 127.
807
     * <p>
808
     * Fix approach and original findings are available here:
809
     * http://brianary.blogspot.com/2006/03/safari-date-bug.html
810
     * </p>
811
     * @method _addDays
812
     * @param {Date} d JavaScript date object
813
     * @param {Number} nDays The number of days to add to the date object (can be negative)
814
     * @private
815
     */
816
    _addDays : function(d, nDays) {
817
        if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420) {
818
            if (nDays < 0) {
819
                // Ensure we don't go below -128 (getDate() is always 1 to 31, so we won't go above 127)
820
                for(var min = -128; nDays < min; nDays -= min) {
821
                    d.setDate(d.getDate() + min);
822
                }
823
            } else {
824
                // Ensure we don't go above 96 + 31 = 127
825
                for(var max = 96; nDays > max; nDays -= max) {
826
                    d.setDate(d.getDate() + max);
827
                }
828
            }
829
            // nDays should be remainder between -128 and 96
830
        }
831
        d.setDate(d.getDate() + nDays);
832
    },
833
 
834
    /**
835
    * Subtracts the specified amount of time from the this instance.
836
    * @method subtract
837
    * @param {Date} date The JavaScript Date object to perform subtraction on
838
    * @param {Number} field The this field constant to be used for performing subtraction.
839
    * @param {Number} amount The number of units (measured in the field constant) to subtract from the date.
840
    * @return {Date} The resulting Date object
841
    */
842
    subtract : function(date, field, amount) {
843
        return this.add(date, field, (amount*-1));
844
    },
845
 
846
    /**
847
    * Determines whether a given date is before another date on the calendar.
848
    * @method before
849
    * @param {Date} date  The Date object to compare with the compare argument
850
    * @param {Date} compareTo The Date object to use for the comparison
851
    * @return {Boolean} true if the date occurs before the compared date; false if not.
852
    */
853
    before : function(date, compareTo) {
854
        var ms = compareTo.getTime();
855
        if (date.getTime() < ms) {
856
            return true;
857
        } else {
858
            return false;
859
        }
860
    },
861
 
862
    /**
863
    * Determines whether a given date is after another date on the calendar.
864
    * @method after
865
    * @param {Date} date  The Date object to compare with the compare argument
866
    * @param {Date} compareTo The Date object to use for the comparison
867
    * @return {Boolean} true if the date occurs after the compared date; false if not.
868
    */
869
    after : function(date, compareTo) {
870
        var ms = compareTo.getTime();
871
        if (date.getTime() > ms) {
872
            return true;
873
        } else {
874
            return false;
875
        }
876
    },
877
 
878
    /**
879
    * Determines whether a given date is between two other dates on the calendar.
880
    * @method between
881
    * @param {Date} date  The date to check for
882
    * @param {Date} dateBegin The start of the range
883
    * @param {Date} dateEnd  The end of the range
884
    * @return {Boolean} true if the date occurs between the compared dates; false if not.
885
    */
886
    between : function(date, dateBegin, dateEnd) {
887
        if (this.after(date, dateBegin) && this.before(date, dateEnd)) {
888
            return true;
889
        } else {
890
            return false;
891
        }
892
    },
893
 
894
    /**
895
    * Retrieves a JavaScript Date object representing January 1 of any given year.
896
    * @method getJan1
897
    * @param {Number} calendarYear  The calendar year for which to retrieve January 1
898
    * @return {Date} January 1 of the calendar year specified.
899
    */
900
    getJan1 : function(calendarYear) {
901
        return this.getDate(calendarYear,0,1);
902
    },
903
 
904
    /**
905
    * Calculates the number of days the specified date is from January 1 of the specified calendar year.
906
    * Passing January 1 to this function would return an offset value of zero.
907
    * @method getDayOffset
908
    * @param {Date} date The JavaScript date for which to find the offset
909
    * @param {Number} calendarYear The calendar year to use for determining the offset
910
    * @return {Number} The number of days since January 1 of the given year
911
    */
912
    getDayOffset : function(date, calendarYear) {
913
        var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1.
914
 
915
        // Find the number of days the passed in date is away from the calendar year start
916
        var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS);
917
        return dayOffset;
918
    },
919
 
920
    /**
921
    * Calculates the week number for the given date. Can currently support standard
922
    * U.S. week numbers, based on Jan 1st defining the 1st week of the year, and
923
    * ISO8601 week numbers, based on Jan 4th defining the 1st week of the year.
924
    *
925
    * @method getWeekNumber
926
    * @param {Date} date The JavaScript date for which to find the week number
927
    * @param {Number} firstDayOfWeek The index of the first day of the week (0 = Sun, 1 = Mon ... 6 = Sat).
928
    * Defaults to 0
929
    * @param {Number} janDate The date in the first week of January which defines week one for the year
930
    * Defaults to the value of YAHOO.widget.DateMath.WEEK_ONE_JAN_DATE, which is 1 (Jan 1st).
931
    * For the U.S, this is normally Jan 1st. ISO8601 uses Jan 4th to define the first week of the year.
932
    *
933
    * @return {Number} The number of the week containing the given date.
934
    */
935
    getWeekNumber : function(date, firstDayOfWeek, janDate) {
936
 
937
        // Setup Defaults
938
        firstDayOfWeek = firstDayOfWeek || 0;
939
        janDate = janDate || this.WEEK_ONE_JAN_DATE;
940
 
941
        var targetDate = this.clearTime(date),
942
            startOfWeek,
943
            endOfWeek;
944
 
945
        if (targetDate.getDay() === firstDayOfWeek) {
946
            startOfWeek = targetDate;
947
        } else {
948
            startOfWeek = this.getFirstDayOfWeek(targetDate, firstDayOfWeek);
949
        }
950
 
951
        var startYear = startOfWeek.getFullYear();
952
 
953
        // DST shouldn't be a problem here, math is quicker than setDate();
954
        endOfWeek = new Date(startOfWeek.getTime() + 6*this.ONE_DAY_MS);
955
 
956
        var weekNum;
957
        if (startYear !== endOfWeek.getFullYear() && endOfWeek.getDate() >= janDate) {
958
            // If years don't match, endOfWeek is in Jan. and if the
959
            // week has WEEK_ONE_JAN_DATE in it, it's week one by definition.
960
            weekNum = 1;
961
        } else {
962
            // Get the 1st day of the 1st week, and
963
            // find how many days away we are from it.
964
            var weekOne = this.clearTime(this.getDate(startYear, 0, janDate)),
965
                weekOneDayOne = this.getFirstDayOfWeek(weekOne, firstDayOfWeek);
966
 
967
            // Round days to smoothen out 1 hr DST diff
968
            var daysDiff  = Math.round((targetDate.getTime() - weekOneDayOne.getTime())/this.ONE_DAY_MS);
969
 
970
            // Calc. Full Weeks
971
            var rem = daysDiff % 7;
972
            var weeksDiff = (daysDiff - rem)/7;
973
            weekNum = weeksDiff + 1;
974
        }
975
        return weekNum;
976
    },
977
 
978
    /**
979
     * Get the first day of the week, for the give date.
980
     * @param {Date} dt The date in the week for which the first day is required.
981
     * @param {Number} startOfWeek The index for the first day of the week, 0 = Sun, 1 = Mon ... 6 = Sat (defaults to 0)
982
     * @return {Date} The first day of the week
983
     */
984
    getFirstDayOfWeek : function (dt, startOfWeek) {
985
        startOfWeek = startOfWeek || 0;
986
        var dayOfWeekIndex = dt.getDay(),
987
            dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7;
988
 
989
        return this.subtract(dt, this.DAY, dayOfWeek);
990
    },
991
 
992
    /**
993
    * Determines if a given week overlaps two different years.
994
    * @method isYearOverlapWeek
995
    * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week.
996
    * @return {Boolean} true if the date overlaps two different years.
997
    */
998
    isYearOverlapWeek : function(weekBeginDate) {
999
        var overlaps = false;
1000
        var nextWeek = this.add(weekBeginDate, this.DAY, 6);
1001
        if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) {
1002
            overlaps = true;
1003
        }
1004
        return overlaps;
1005
    },
1006
 
1007
    /**
1008
    * Determines if a given week overlaps two different months.
1009
    * @method isMonthOverlapWeek
1010
    * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week.
1011
    * @return {Boolean} true if the date overlaps two different months.
1012
    */
1013
    isMonthOverlapWeek : function(weekBeginDate) {
1014
        var overlaps = false;
1015
        var nextWeek = this.add(weekBeginDate, this.DAY, 6);
1016
        if (nextWeek.getMonth() != weekBeginDate.getMonth()) {
1017
            overlaps = true;
1018
        }
1019
        return overlaps;
1020
    },
1021
 
1022
    /**
1023
    * Gets the first day of a month containing a given date.
1024
    * @method findMonthStart
1025
    * @param {Date} date The JavaScript Date used to calculate the month start
1026
    * @return {Date}  The JavaScript Date representing the first day of the month
1027
    */
1028
    findMonthStart : function(date) {
1029
        var start = this.getDate(date.getFullYear(), date.getMonth(), 1);
1030
        return start;
1031
    },
1032
 
1033
    /**
1034
    * Gets the last day of a month containing a given date.
1035
    * @method findMonthEnd
1036
    * @param {Date} date The JavaScript Date used to calculate the month end
1037
    * @return {Date}  The JavaScript Date representing the last day of the month
1038
    */
1039
    findMonthEnd : function(date) {
1040
        var start = this.findMonthStart(date);
1041
        var nextMonth = this.add(start, this.MONTH, 1);
1042
        var end = this.subtract(nextMonth, this.DAY, 1);
1043
        return end;
1044
    },
1045
 
1046
    /**
1047
    * Clears the time fields from a given date, effectively setting the time to 12 noon.
1048
    * @method clearTime
1049
    * @param {Date} date The JavaScript Date for which the time fields will be cleared
1050
    * @return {Date}  The JavaScript Date cleared of all time fields
1051
    */
1052
    clearTime : function(date) {
1053
        date.setHours(12,0,0,0);
1054
        return date;
1055
    },
1056
 
1057
    /**
1058
     * Returns a new JavaScript Date object, representing the given year, month and date. Time fields (hr, min, sec, ms) on the new Date object
1059
     * are set to 0, except the hour which is set to 12 (noon) to avoid issues around the start of Daylight Savings Time. The method allows Date
1060
     * instances to be created with the a year less than 100. "new Date(year, month, date)" implementations set the year to 19xx if a year (xx)
1061
     * which is less than 100 is provided.
1062
     * <p>
1063
     * <em>NOTE:</em>Validation on argument values is not performed. It is the caller's responsibility to ensure
1064
     * arguments are valid as per the ECMAScript-262 Date object specification for the new Date(year, month[, date]) constructor.
1065
     * </p>
1066
     * @method getDate
1067
     * @param {Number} y Year.
1068
     * @param {Number} m Month index from 0 (Jan) to 11 (Dec).
1069
     * @param {Number} d (optional) Date from 1 to 31. If not provided, defaults to 1.
1070
     * @return {Date} The JavaScript date object with year, month, date set as provided.
1071
     */
1072
    getDate : function(y, m, d) {
1073
        var dt = null;
1074
        if (YAHOO.lang.isUndefined(d)) {
1075
            d = 1;
1076
        }
1077
        if (y >= 100) {
1078
            dt = new Date(y, m, d, 12);
1079
        } else {
1080
            dt = new Date();
1081
            dt.setFullYear(y);
1082
            dt.setMonth(m);
1083
            dt.setDate(d);
1084
            dt.setHours(12,0,0,0);
1085
        }
1086
        return dt;
1087
    }
1088
};
1089
/**
1090
* 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
1091
* multi-month interface. Calendars are generated entirely via script and can be navigated without any page refreshes.
1092
* @module    calendar
1093
* @title    Calendar
1094
* @namespace  YAHOO.widget
1095
* @requires  yahoo,dom,event
1096
*/
1097
(function(){
1098
 
1099
    var Dom = YAHOO.util.Dom,
1100
        Event = YAHOO.util.Event,
1101
        Lang = YAHOO.lang,
1102
        DateMath = YAHOO.widget.DateMath;
1103
 
1104
/**
1105
* Calendar is the base class for the Calendar widget. In its most basic
1106
* implementation, it has the ability to render a calendar widget on the page
1107
* that can be manipulated to select a single date, move back and forth between
1108
* months and years.
1109
* <p>To construct the placeholder for the calendar widget, the code is as
1110
* follows:
1111
*   <xmp>
1112
*       <div id="calContainer"></div>
1113
*   </xmp>
1114
* </p>
1115
* <p>
1116
* <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
1117
* The Calendar can be constructed by simply providing a container ID string,
1118
* or a reference to a container DIV HTMLElement (the element needs to exist
1119
* in the document).
1120
*
1121
* E.g.:
1122
*   <xmp>
1123
*       var c = new YAHOO.widget.Calendar("calContainer", configOptions);
1124
*   </xmp>
1125
* or:
1126
*   <xmp>
1127
*       var containerDiv = YAHOO.util.Dom.get("calContainer");
1128
*       var c = new YAHOO.widget.Calendar(containerDiv, configOptions);
1129
*   </xmp>
1130
* </p>
1131
* <p>
1132
* If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
1133
* For example if an ID is not provided, and the container's ID is "calContainer", the Calendar's ID will be set to "calContainer_t".
1134
* </p>
1135
*
1136
* @namespace YAHOO.widget
1137
* @class Calendar
1138
* @constructor
1139
* @param {String} id optional The id of the table element that will represent the Calendar widget. As of 2.4.0, this argument is optional.
1140
* @param {String | HTMLElement} container The id of the container div element that will wrap the Calendar table, or a reference to a DIV element which exists in the document.
1141
* @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
1142
*/
1143
function Calendar(id, containerId, config) {
1144
    this.init.apply(this, arguments);
1145
}
1146
 
1147
/**
1148
* The path to be used for images loaded for the Calendar
1149
* @property YAHOO.widget.Calendar.IMG_ROOT
1150
* @static
1151
* @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
1152
* @type String
1153
*/
1154
Calendar.IMG_ROOT = null;
1155
 
1156
/**
1157
* Type constant used for renderers to represent an individual date (M/D/Y)
1158
* @property YAHOO.widget.Calendar.DATE
1159
* @static
1160
* @final
1161
* @type String
1162
*/
1163
Calendar.DATE = "D";
1164
 
1165
/**
1166
* Type constant used for renderers to represent an individual date across any year (M/D)
1167
* @property YAHOO.widget.Calendar.MONTH_DAY
1168
* @static
1169
* @final
1170
* @type String
1171
*/
1172
Calendar.MONTH_DAY = "MD";
1173
 
1174
/**
1175
* Type constant used for renderers to represent a weekday
1176
* @property YAHOO.widget.Calendar.WEEKDAY
1177
* @static
1178
* @final
1179
* @type String
1180
*/
1181
Calendar.WEEKDAY = "WD";
1182
 
1183
/**
1184
* Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
1185
* @property YAHOO.widget.Calendar.RANGE
1186
* @static
1187
* @final
1188
* @type String
1189
*/
1190
Calendar.RANGE = "R";
1191
 
1192
/**
1193
* Type constant used for renderers to represent a month across any year
1194
* @property YAHOO.widget.Calendar.MONTH
1195
* @static
1196
* @final
1197
* @type String
1198
*/
1199
Calendar.MONTH = "M";
1200
 
1201
/**
1202
* Constant that represents the total number of date cells that are displayed in a given month
1203
* @property YAHOO.widget.Calendar.DISPLAY_DAYS
1204
* @static
1205
* @final
1206
* @type Number
1207
*/
1208
Calendar.DISPLAY_DAYS = 42;
1209
 
1210
/**
1211
* Constant used for halting the execution of the remainder of the render stack
1212
* @property YAHOO.widget.Calendar.STOP_RENDER
1213
* @static
1214
* @final
1215
* @type String
1216
*/
1217
Calendar.STOP_RENDER = "S";
1218
 
1219
/**
1220
* Constant used to represent short date field string formats (e.g. Tu or Feb)
1221
* @property YAHOO.widget.Calendar.SHORT
1222
* @static
1223
* @final
1224
* @type String
1225
*/
1226
Calendar.SHORT = "short";
1227
 
1228
/**
1229
* Constant used to represent long date field string formats (e.g. Monday or February)
1230
* @property YAHOO.widget.Calendar.LONG
1231
* @static
1232
* @final
1233
* @type String
1234
*/
1235
Calendar.LONG = "long";
1236
 
1237
/**
1238
* Constant used to represent medium date field string formats (e.g. Mon)
1239
* @property YAHOO.widget.Calendar.MEDIUM
1240
* @static
1241
* @final
1242
* @type String
1243
*/
1244
Calendar.MEDIUM = "medium";
1245
 
1246
/**
1247
* Constant used to represent single character date field string formats (e.g. M, T, W)
1248
* @property YAHOO.widget.Calendar.ONE_CHAR
1249
* @static
1250
* @final
1251
* @type String
1252
*/
1253
Calendar.ONE_CHAR = "1char";
1254
 
1255
/**
1256
* The set of default Config property keys and values for the Calendar.
1257
*
1258
* <p>
1259
* NOTE: This property is made public in order to allow users to change
1260
* the default values of configuration properties. Users should not
1261
* modify the key string, unless they are overriding the Calendar implementation
1262
* </p>
1263
*
1264
* <p>
1265
* The property is an object with key/value pairs, the key being the
1266
* uppercase configuration property name and the value being an object
1267
* literal with a key string property, and a value property, specifying the
1268
* default value of the property. To override a default value, you can set
1269
* the value property, for example, <code>YAHOO.widget.Calendar.DEFAULT_CONFIG.MULTI_SELECT.value = true;</code>
1270
*
1271
* @property YAHOO.widget.Calendar.DEFAULT_CONFIG
1272
* @static
1273
* @type Object
1274
*/
1275
 
1276
Calendar.DEFAULT_CONFIG = {
1277
    YEAR_OFFSET : {key:"year_offset", value:0, supercedes:["pagedate", "selected", "mindate","maxdate"]},
1278
    TODAY : {key:"today", value:new Date(), supercedes:["pagedate"]},
1279
    PAGEDATE : {key:"pagedate", value:null},
1280
    SELECTED : {key:"selected", value:[]},
1281
    TITLE : {key:"title", value:""},
1282
    CLOSE : {key:"close", value:false},
1283
    IFRAME : {key:"iframe", value:(YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) ? true : false},
1284
    MINDATE : {key:"mindate", value:null},
1285
    MAXDATE : {key:"maxdate", value:null},
1286
    MULTI_SELECT : {key:"multi_select", value:false},
1287
    OOM_SELECT : {key:"oom_select", value:false},
1288
    START_WEEKDAY : {key:"start_weekday", value:0},
1289
    SHOW_WEEKDAYS : {key:"show_weekdays", value:true},
1290
    SHOW_WEEK_HEADER : {key:"show_week_header", value:false},
1291
    SHOW_WEEK_FOOTER : {key:"show_week_footer", value:false},
1292
    HIDE_BLANK_WEEKS : {key:"hide_blank_weeks", value:false},
1293
    NAV_ARROW_LEFT: {key:"nav_arrow_left", value:null} ,
1294
    NAV_ARROW_RIGHT : {key:"nav_arrow_right", value:null} ,
1295
    MONTHS_SHORT : {key:"months_short", value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]},
1296
    MONTHS_LONG: {key:"months_long", value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]},
1297
    WEEKDAYS_1CHAR: {key:"weekdays_1char", value:["S", "M", "T", "W", "T", "F", "S"]},
1298
    WEEKDAYS_SHORT: {key:"weekdays_short", value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]},
1299
    WEEKDAYS_MEDIUM: {key:"weekdays_medium", value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]},
1300
    WEEKDAYS_LONG: {key:"weekdays_long", value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]},
1301
    LOCALE_MONTHS:{key:"locale_months", value:"long"},
1302
    LOCALE_WEEKDAYS:{key:"locale_weekdays", value:"short"},
1303
    DATE_DELIMITER:{key:"date_delimiter", value:","},
1304
    DATE_FIELD_DELIMITER:{key:"date_field_delimiter", value:"/"},
1305
    DATE_RANGE_DELIMITER:{key:"date_range_delimiter", value:"-"},
1306
    MY_MONTH_POSITION:{key:"my_month_position", value:1},
1307
    MY_YEAR_POSITION:{key:"my_year_position", value:2},
1308
    MD_MONTH_POSITION:{key:"md_month_position", value:1},
1309
    MD_DAY_POSITION:{key:"md_day_position", value:2},
1310
    MDY_MONTH_POSITION:{key:"mdy_month_position", value:1},
1311
    MDY_DAY_POSITION:{key:"mdy_day_position", value:2},
1312
    MDY_YEAR_POSITION:{key:"mdy_year_position", value:3},
1313
    MY_LABEL_MONTH_POSITION:{key:"my_label_month_position", value:1},
1314
    MY_LABEL_YEAR_POSITION:{key:"my_label_year_position", value:2},
1315
    MY_LABEL_MONTH_SUFFIX:{key:"my_label_month_suffix", value:" "},
1316
    MY_LABEL_YEAR_SUFFIX:{key:"my_label_year_suffix", value:""},
1317
    NAV: {key:"navigator", value: null},
1318
    STRINGS : {
1319
        key:"strings",
1320
        value: {
1321
            previousMonth : "Previous Month",
1322
            nextMonth : "Next Month",
1323
            close: "Close"
1324
        },
1325
        supercedes : ["close", "title"]
1326
    }
1327
};
1328
 
1329
/**
1330
* The set of default Config property keys and values for the Calendar
1331
* @property YAHOO.widget.Calendar._DEFAULT_CONFIG
1332
* @deprecated Made public. See the public DEFAULT_CONFIG property for details
1333
* @final
1334
* @static
1335
* @private
1336
* @type Object
1337
*/
1338
Calendar._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG;
1339
 
1340
var DEF_CFG = Calendar.DEFAULT_CONFIG;
1341
 
1342
/**
1343
* The set of Custom Event types supported by the Calendar
1344
* @property YAHOO.widget.Calendar._EVENT_TYPES
1345
* @final
1346
* @static
1347
* @private
1348
* @type Object
1349
*/
1350
Calendar._EVENT_TYPES = {
1351
    BEFORE_SELECT : "beforeSelect",
1352
    SELECT : "select",
1353
    BEFORE_DESELECT : "beforeDeselect",
1354
    DESELECT : "deselect",
1355
    CHANGE_PAGE : "changePage",
1356
    BEFORE_RENDER : "beforeRender",
1357
    RENDER : "render",
1358
    BEFORE_DESTROY : "beforeDestroy",
1359
    DESTROY : "destroy",
1360
    RESET : "reset",
1361
    CLEAR : "clear",
1362
    BEFORE_HIDE : "beforeHide",
1363
    HIDE : "hide",
1364
    BEFORE_SHOW : "beforeShow",
1365
    SHOW : "show",
1366
    BEFORE_HIDE_NAV : "beforeHideNav",
1367
    HIDE_NAV : "hideNav",
1368
    BEFORE_SHOW_NAV : "beforeShowNav",
1369
    SHOW_NAV : "showNav",
1370
    BEFORE_RENDER_NAV : "beforeRenderNav",
1371
    RENDER_NAV : "renderNav"
1372
};
1373
 
1374
/**
1375
* The set of default style constants for the Calendar
1376
* @property YAHOO.widget.Calendar.STYLES
1377
* @static
1378
* @type Object An object with name/value pairs for the class name identifier/value.
1379
*/
1380
Calendar.STYLES = {
1381
    CSS_ROW_HEADER: "calrowhead",
1382
    CSS_ROW_FOOTER: "calrowfoot",
1383
    CSS_CELL : "calcell",
1384
    CSS_CELL_SELECTOR : "selector",
1385
    CSS_CELL_SELECTED : "selected",
1386
    CSS_CELL_SELECTABLE : "selectable",
1387
    CSS_CELL_RESTRICTED : "restricted",
1388
    CSS_CELL_TODAY : "today",
1389
    CSS_CELL_OOM : "oom",
1390
    CSS_CELL_OOB : "previous",
1391
    CSS_HEADER : "calheader",
1392
    CSS_HEADER_TEXT : "calhead",
1393
    CSS_BODY : "calbody",
1394
    CSS_WEEKDAY_CELL : "calweekdaycell",
1395
    CSS_WEEKDAY_ROW : "calweekdayrow",
1396
    CSS_FOOTER : "calfoot",
1397
    CSS_CALENDAR : "yui-calendar",
1398
    CSS_SINGLE : "single",
1399
    CSS_CONTAINER : "yui-calcontainer",
1400
    CSS_NAV_LEFT : "calnavleft",
1401
    CSS_NAV_RIGHT : "calnavright",
1402
    CSS_NAV : "calnav",
1403
    CSS_CLOSE : "calclose",
1404
    CSS_CELL_TOP : "calcelltop",
1405
    CSS_CELL_LEFT : "calcellleft",
1406
    CSS_CELL_RIGHT : "calcellright",
1407
    CSS_CELL_BOTTOM : "calcellbottom",
1408
    CSS_CELL_HOVER : "calcellhover",
1409
    CSS_CELL_HIGHLIGHT1 : "highlight1",
1410
    CSS_CELL_HIGHLIGHT2 : "highlight2",
1411
    CSS_CELL_HIGHLIGHT3 : "highlight3",
1412
    CSS_CELL_HIGHLIGHT4 : "highlight4",
1413
    CSS_WITH_TITLE: "withtitle",
1414
    CSS_FIXED_SIZE: "fixedsize",
1415
    CSS_LINK_CLOSE: "link-close"
1416
};
1417
 
1418
/**
1419
* The set of default style constants for the Calendar
1420
* @property YAHOO.widget.Calendar._STYLES
1421
* @deprecated Made public. See the public STYLES property for details
1422
* @final
1423
* @static
1424
* @private
1425
* @type Object
1426
*/
1427
Calendar._STYLES = Calendar.STYLES;
1428
 
1429
Calendar.prototype = {
1430
 
1431
    /**
1432
    * The configuration object used to set up the calendars various locale and style options.
1433
    * @property Config
1434
    * @private
1435
    * @deprecated Configuration properties should be set by calling Calendar.cfg.setProperty.
1436
    * @type Object
1437
    */
1438
    Config : null,
1439
 
1440
    /**
1441
    * The parent CalendarGroup, only to be set explicitly by the parent group
1442
    * @property parent
1443
    * @type CalendarGroup
1444
    */
1445
    parent : null,
1446
 
1447
    /**
1448
    * The index of this item in the parent group
1449
    * @property index
1450
    * @type Number
1451
    */
1452
    index : -1,
1453
 
1454
    /**
1455
    * The collection of calendar table cells
1456
    * @property cells
1457
    * @type HTMLTableCellElement[]
1458
    */
1459
    cells : null,
1460
 
1461
    /**
1462
    * 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].
1463
    * @property cellDates
1464
    * @type Array[](Number[])
1465
    */
1466
    cellDates : null,
1467
 
1468
    /**
1469
    * The id that uniquely identifies this Calendar.
1470
    * @property id
1471
    * @type String
1472
    */
1473
    id : null,
1474
 
1475
    /**
1476
    * The unique id associated with the Calendar's container
1477
    * @property containerId
1478
    * @type String
1479
    */
1480
    containerId: null,
1481
 
1482
    /**
1483
    * 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.
1484
    * @property oDomContainer
1485
    * @type HTMLElement
1486
    */
1487
    oDomContainer : null,
1488
 
1489
    /**
1490
    * A Date object representing today's date.
1491
    * @deprecated Use the "today" configuration property
1492
    * @property today
1493
    * @type Date
1494
    */
1495
    today : null,
1496
 
1497
    /**
1498
    * The list of render functions, along with required parameters, used to render cells.
1499
    * @property renderStack
1500
    * @type Array[]
1501
    */
1502
    renderStack : null,
1503
 
1504
    /**
1505
    * A copy of the initial render functions created before rendering.
1506
    * @property _renderStack
1507
    * @private
1508
    * @type Array
1509
    */
1510
    _renderStack : null,
1511
 
1512
    /**
1513
    * A reference to the CalendarNavigator instance created for this Calendar.
1514
    * Will be null if the "navigator" configuration property has not been set
1515
    * @property oNavigator
1516
    * @type CalendarNavigator
1517
    */
1518
    oNavigator : null,
1519
 
1520
    /**
1521
    * The private list of initially selected dates.
1522
    * @property _selectedDates
1523
    * @private
1524
    * @type Array
1525
    */
1526
    _selectedDates : null,
1527
 
1528
    /**
1529
    * A map of DOM event handlers to attach to cells associated with specific CSS class names
1530
    * @property domEventMap
1531
    * @type Object
1532
    */
1533
    domEventMap : null,
1534
 
1535
    /**
1536
     * Protected helper used to parse Calendar constructor/init arguments.
1537
     *
1538
     * As of 2.4.0, Calendar supports a simpler constructor
1539
     * signature. This method reconciles arguments
1540
     * received in the pre 2.4.0 and 2.4.0 formats.
1541
     *
1542
     * @protected
1543
     * @method _parseArgs
1544
     * @param {Array} Function "arguments" array
1545
     * @return {Object} Object with id, container, config properties containing
1546
     * the reconciled argument values.
1547
     **/
1548
    _parseArgs : function(args) {
1549
        /*
1550
           2.4.0 Constructors signatures
1551
 
1552
           new Calendar(String)
1553
           new Calendar(HTMLElement)
1554
           new Calendar(String, ConfigObject)
1555
           new Calendar(HTMLElement, ConfigObject)
1556
 
1557
           Pre 2.4.0 Constructor signatures
1558
 
1559
           new Calendar(String, String)
1560
           new Calendar(String, HTMLElement)
1561
           new Calendar(String, String, ConfigObject)
1562
           new Calendar(String, HTMLElement, ConfigObject)
1563
         */
1564
        var nArgs = {id:null, container:null, config:null};
1565
 
1566
        if (args && args.length && args.length > 0) {
1567
            switch (args.length) {
1568
                case 1:
1569
                    nArgs.id = null;
1570
                    nArgs.container = args[0];
1571
                    nArgs.config = null;
1572
                    break;
1573
                case 2:
1574
                    if (Lang.isObject(args[1]) && !args[1].tagName && !(args[1] instanceof String)) {
1575
                        nArgs.id = null;
1576
                        nArgs.container = args[0];
1577
                        nArgs.config = args[1];
1578
                    } else {
1579
                        nArgs.id = args[0];
1580
                        nArgs.container = args[1];
1581
                        nArgs.config = null;
1582
                    }
1583
                    break;
1584
                default: // 3+
1585
                    nArgs.id = args[0];
1586
                    nArgs.container = args[1];
1587
                    nArgs.config = args[2];
1588
                    break;
1589
            }
1590
        } else {
1591
        }
1592
        return nArgs;
1593
    },
1594
 
1595
    /**
1596
    * Initializes the Calendar widget.
1597
    * @method init
1598
    *
1599
    * @param {String} id optional The id of the table element that will represent the Calendar widget. As of 2.4.0, this argument is optional.
1600
    * @param {String | HTMLElement} container The id of the container div element that will wrap the Calendar table, or a reference to a DIV element which exists in the document.
1601
    * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
1602
    */
1603
    init : function(id, container, config) {
1604
        // Normalize 2.4.0, pre 2.4.0 args
1605
        var nArgs = this._parseArgs(arguments);
1606
 
1607
        id = nArgs.id;
1608
        container = nArgs.container;
1609
        config = nArgs.config;
1610
 
1611
        this.oDomContainer = Dom.get(container);
1612
 
1613
        this._oDoc = this.oDomContainer.ownerDocument;
1614
 
1615
        if (!this.oDomContainer.id) {
1616
            this.oDomContainer.id = Dom.generateId();
1617
        }
1618
 
1619
        if (!id) {
1620
            id = this.oDomContainer.id + "_t";
1621
        }
1622
 
1623
        this.id = id;
1624
        this.containerId = this.oDomContainer.id;
1625
 
1626
        this.initEvents();
1627
 
1628
        /**
1629
        * The Config object used to hold the configuration variables for the Calendar
1630
        * @property cfg
1631
        * @type YAHOO.util.Config
1632
        */
1633
        this.cfg = new YAHOO.util.Config(this);
1634
 
1635
        /**
1636
        * The local object which contains the Calendar's options
1637
        * @property Options
1638
        * @type Object
1639
        */
1640
        this.Options = {};
1641
 
1642
        /**
1643
        * The local object which contains the Calendar's locale settings
1644
        * @property Locale
1645
        * @type Object
1646
        */
1647
        this.Locale = {};
1648
 
1649
        this.initStyles();
1650
 
1651
        Dom.addClass(this.oDomContainer, this.Style.CSS_CONTAINER);
1652
        Dom.addClass(this.oDomContainer, this.Style.CSS_SINGLE);
1653
 
1654
        this.cellDates = [];
1655
        this.cells = [];
1656
        this.renderStack = [];
1657
        this._renderStack = [];
1658
 
1659
        this.setupConfig();
1660
 
1661
        if (config) {
1662
            this.cfg.applyConfig(config, true);
1663
        }
1664
 
1665
        this.cfg.fireQueue();
1666
 
1667
        this.today = this.cfg.getProperty("today");
1668
    },
1669
 
1670
    /**
1671
    * Default Config listener for the iframe property. If the iframe config property is set to true,
1672
    * renders the built-in IFRAME shim if the container is relatively or absolutely positioned.
1673
    *
1674
    * @method configIframe
1675
    */
1676
    configIframe : function(type, args, obj) {
1677
        var useIframe = args[0];
1678
 
1679
        if (!this.parent) {
1680
            if (Dom.inDocument(this.oDomContainer)) {
1681
                if (useIframe) {
1682
                    var pos = Dom.getStyle(this.oDomContainer, "position");
1683
 
1684
                    if (pos == "absolute" || pos == "relative") {
1685
 
1686
                        if (!Dom.inDocument(this.iframe)) {
1687
                            this.iframe = document.createElement("iframe");
1688
                            this.iframe.src = "javascript:false;";
1689
 
1690
                            Dom.setStyle(this.iframe, "opacity", "0");
1691
 
1692
                            if (YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) {
1693
                                Dom.addClass(this.iframe, this.Style.CSS_FIXED_SIZE);
1694
                            }
1695
 
1696
                            this.oDomContainer.insertBefore(this.iframe, this.oDomContainer.firstChild);
1697
                        }
1698
                    }
1699
                } else {
1700
                    if (this.iframe) {
1701
                        if (this.iframe.parentNode) {
1702
                            this.iframe.parentNode.removeChild(this.iframe);
1703
                        }
1704
                        this.iframe = null;
1705
                    }
1706
                }
1707
            }
1708
        }
1709
    },
1710
 
1711
    /**
1712
    * Default handler for the "title" property
1713
    * @method configTitle
1714
    */
1715
    configTitle : function(type, args, obj) {
1716
        var title = args[0];
1717
 
1718
        // "" disables title bar
1719
        if (title) {
1720
            this.createTitleBar(title);
1721
        } else {
1722
            var close = this.cfg.getProperty(DEF_CFG.CLOSE.key);
1723
            if (!close) {
1724
                this.removeTitleBar();
1725
            } else {
1726
                this.createTitleBar("&#160;");
1727
            }
1728
        }
1729
    },
1730
 
1731
    /**
1732
    * Default handler for the "close" property
1733
    * @method configClose
1734
    */
1735
    configClose : function(type, args, obj) {
1736
        var close = args[0],
1737
            title = this.cfg.getProperty(DEF_CFG.TITLE.key);
1738
 
1739
        if (close) {
1740
            if (!title) {
1741
                this.createTitleBar("&#160;");
1742
            }
1743
            this.createCloseButton();
1744
        } else {
1745
            this.removeCloseButton();
1746
            if (!title) {
1747
                this.removeTitleBar();
1748
            }
1749
        }
1750
    },
1751
 
1752
    /**
1753
    * Initializes Calendar's built-in CustomEvents
1754
    * @method initEvents
1755
    */
1756
    initEvents : function() {
1757
 
1758
        var defEvents = Calendar._EVENT_TYPES,
1759
            CE = YAHOO.util.CustomEvent,
1760
            cal = this; // To help with minification
1761
 
1762
        /**
1763
        * Fired before a date selection is made
1764
        * @event beforeSelectEvent
1765
        */
1766
        cal.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
1767
 
1768
        /**
1769
        * Fired when a date selection is made
1770
        * @event selectEvent
1771
        * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
1772
        */
1773
        cal.selectEvent = new CE(defEvents.SELECT);
1774
 
1775
        /**
1776
        * Fired before a date or set of dates is deselected
1777
        * @event beforeDeselectEvent
1778
        */
1779
        cal.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
1780
 
1781
        /**
1782
        * Fired when a date or set of dates is deselected
1783
        * @event deselectEvent
1784
        * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
1785
        */
1786
        cal.deselectEvent = new CE(defEvents.DESELECT);
1787
 
1788
        /**
1789
        * Fired when the Calendar page is changed
1790
        * @event changePageEvent
1791
        * @param {Date} prevDate The date before the page was changed
1792
        * @param {Date} newDate The date after the page was changed
1793
        */
1794
        cal.changePageEvent = new CE(defEvents.CHANGE_PAGE);
1795
 
1796
        /**
1797
        * Fired before the Calendar is rendered
1798
        * @event beforeRenderEvent
1799
        */
1800
        cal.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
1801
 
1802
        /**
1803
        * Fired when the Calendar is rendered
1804
        * @event renderEvent
1805
        */
1806
        cal.renderEvent = new CE(defEvents.RENDER);
1807
 
1808
        /**
1809
        * Fired just before the Calendar is to be destroyed
1810
        * @event beforeDestroyEvent
1811
        */
1812
        cal.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
1813
 
1814
        /**
1815
        * Fired after the Calendar is destroyed. This event should be used
1816
        * for notification only. When this event is fired, important Calendar instance
1817
        * properties, dom references and event listeners have already been
1818
        * removed/dereferenced, and hence the Calendar instance is not in a usable
1819
        * state.
1820
        *
1821
        * @event destroyEvent
1822
        */
1823
        cal.destroyEvent = new CE(defEvents.DESTROY);
1824
 
1825
        /**
1826
        * Fired when the Calendar is reset
1827
        * @event resetEvent
1828
        */
1829
        cal.resetEvent = new CE(defEvents.RESET);
1830
 
1831
        /**
1832
        * Fired when the Calendar is cleared
1833
        * @event clearEvent
1834
        */
1835
        cal.clearEvent = new CE(defEvents.CLEAR);
1836
 
1837
        /**
1838
        * Fired just before the Calendar is to be shown
1839
        * @event beforeShowEvent
1840
        */
1841
        cal.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
1842
 
1843
        /**
1844
        * Fired after the Calendar is shown
1845
        * @event showEvent
1846
        */
1847
        cal.showEvent = new CE(defEvents.SHOW);
1848
 
1849
        /**
1850
        * Fired just before the Calendar is to be hidden
1851
        * @event beforeHideEvent
1852
        */
1853
        cal.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
1854
 
1855
        /**
1856
        * Fired after the Calendar is hidden
1857
        * @event hideEvent
1858
        */
1859
        cal.hideEvent = new CE(defEvents.HIDE);
1860
 
1861
        /**
1862
        * Fired just before the CalendarNavigator is to be shown
1863
        * @event beforeShowNavEvent
1864
        */
1865
        cal.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
1866
 
1867
        /**
1868
        * Fired after the CalendarNavigator is shown
1869
        * @event showNavEvent
1870
        */
1871
        cal.showNavEvent = new CE(defEvents.SHOW_NAV);
1872
 
1873
        /**
1874
        * Fired just before the CalendarNavigator is to be hidden
1875
        * @event beforeHideNavEvent
1876
        */
1877
        cal.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
1878
 
1879
        /**
1880
        * Fired after the CalendarNavigator is hidden
1881
        * @event hideNavEvent
1882
        */
1883
        cal.hideNavEvent = new CE(defEvents.HIDE_NAV);
1884
 
1885
        /**
1886
        * Fired just before the CalendarNavigator is to be rendered
1887
        * @event beforeRenderNavEvent
1888
        */
1889
        cal.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
1890
 
1891
        /**
1892
        * Fired after the CalendarNavigator is rendered
1893
        * @event renderNavEvent
1894
        */
1895
        cal.renderNavEvent = new CE(defEvents.RENDER_NAV);
1896
 
1897
        cal.beforeSelectEvent.subscribe(cal.onBeforeSelect, this, true);
1898
        cal.selectEvent.subscribe(cal.onSelect, this, true);
1899
        cal.beforeDeselectEvent.subscribe(cal.onBeforeDeselect, this, true);
1900
        cal.deselectEvent.subscribe(cal.onDeselect, this, true);
1901
        cal.changePageEvent.subscribe(cal.onChangePage, this, true);
1902
        cal.renderEvent.subscribe(cal.onRender, this, true);
1903
        cal.resetEvent.subscribe(cal.onReset, this, true);
1904
        cal.clearEvent.subscribe(cal.onClear, this, true);
1905
    },
1906
 
1907
    /**
1908
    * The default event handler for clicks on the "Previous Month" navigation UI
1909
    *
1910
    * @method doPreviousMonthNav
1911
    * @param {DOMEvent} e The DOM event
1912
    * @param {Calendar} cal A reference to the calendar
1913
    */
1914
    doPreviousMonthNav : function(e, cal) {
1915
        Event.preventDefault(e);
1916
        // previousMonth invoked in a timeout, to allow
1917
        // event to bubble up, with correct target. Calling
1918
        // previousMonth, will call render which will remove
1919
        // HTML which generated the event, resulting in an
1920
        // invalid event target in certain browsers.
1921
        setTimeout(function() {
1922
            cal.previousMonth();
1923
            var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_LEFT, "a", cal.oDomContainer);
1924
            if (navs && navs[0]) {
1925
                try {
1926
                    navs[0].focus();
1927
                } catch (ex) {
1928
                    // ignore
1929
                }
1930
            }
1931
        }, 0);
1932
    },
1933
 
1934
    /**
1935
     * The default event handler for clicks on the "Next Month" navigation UI
1936
     *
1937
     * @method doNextMonthNav
1938
     * @param {DOMEvent} e The DOM event
1939
     * @param {Calendar} cal A reference to the calendar
1940
     */
1941
    doNextMonthNav : function(e, cal) {
1942
        Event.preventDefault(e);
1943
        setTimeout(function() {
1944
            cal.nextMonth();
1945
            var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_RIGHT, "a", cal.oDomContainer);
1946
            if (navs && navs[0]) {
1947
                try {
1948
                    navs[0].focus();
1949
                } catch (ex) {
1950
                    // ignore
1951
                }
1952
            }
1953
        }, 0);
1954
    },
1955
 
1956
    /**
1957
    * The default event handler for date cell selection. Currently attached to
1958
    * the Calendar's bounding box, referenced by it's <a href="#property_oDomContainer">oDomContainer</a> property.
1959
    *
1960
    * @method doSelectCell
1961
    * @param {DOMEvent} e The DOM event
1962
    * @param {Calendar} cal A reference to the calendar
1963
    */
1964
    doSelectCell : function(e, cal) {
1965
        var cell, d, date, index;
1966
 
1967
        var target = Event.getTarget(e),
1968
            tagName = target.tagName.toLowerCase(),
1969
            defSelector = false;
1970
 
1971
        while (tagName != "td" && !Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1972
 
1973
            if (!defSelector && tagName == "a" && Dom.hasClass(target, cal.Style.CSS_CELL_SELECTOR)) {
1974
                defSelector = true;
1975
            }
1976
 
1977
            target = target.parentNode;
1978
            tagName = target.tagName.toLowerCase();
1979
 
1980
            if (target == this.oDomContainer || tagName == "html") {
1981
                return;
1982
            }
1983
        }
1984
 
1985
        if (defSelector) {
1986
            // Stop link href navigation for default renderer
1987
            Event.preventDefault(e);
1988
        }
1989
 
1990
        cell = target;
1991
 
1992
        if (Dom.hasClass(cell, cal.Style.CSS_CELL_SELECTABLE)) {
1993
            index = cal.getIndexFromId(cell.id);
1994
            if (index > -1) {
1995
                d = cal.cellDates[index];
1996
                if (d) {
1997
                    date = DateMath.getDate(d[0],d[1]-1,d[2]);
1998
 
1999
                    var link;
2000
 
2001
                    if (cal.Options.MULTI_SELECT) {
2002
                        link = cell.getElementsByTagName("a")[0];
2003
                        if (link) {
2004
                            link.blur();
2005
                        }
2006
 
2007
                        var cellDate = cal.cellDates[index];
2008
                        var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
2009
 
2010
                        if (cellDateIndex > -1) {
2011
                            cal.deselectCell(index);
2012
                        } else {
2013
                            cal.selectCell(index);
2014
                        }
2015
 
2016
                    } else {
2017
                        link = cell.getElementsByTagName("a")[0];
2018
                        if (link) {
2019
                            link.blur();
2020
                        }
2021
                        cal.selectCell(index);
2022
                    }
2023
                }
2024
            }
2025
        }
2026
    },
2027
 
2028
    /**
2029
    * The event that is executed when the user hovers over a cell
2030
    * @method doCellMouseOver
2031
    * @param {DOMEvent} e The event
2032
    * @param {Calendar} cal A reference to the calendar passed by the Event utility
2033
    */
2034
    doCellMouseOver : function(e, cal) {
2035
        var target;
2036
        if (e) {
2037
            target = Event.getTarget(e);
2038
        } else {
2039
            target = this;
2040
        }
2041
 
2042
        while (target.tagName && target.tagName.toLowerCase() != "td") {
2043
            target = target.parentNode;
2044
            if (!target.tagName || target.tagName.toLowerCase() == "html") {
2045
                return;
2046
            }
2047
        }
2048
 
2049
        if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
2050
            Dom.addClass(target, cal.Style.CSS_CELL_HOVER);
2051
        }
2052
    },
2053
 
2054
    /**
2055
    * The event that is executed when the user moves the mouse out of a cell
2056
    * @method doCellMouseOut
2057
    * @param {DOMEvent} e The event
2058
    * @param {Calendar} cal A reference to the calendar passed by the Event utility
2059
    */
2060
    doCellMouseOut : function(e, cal) {
2061
        var target;
2062
        if (e) {
2063
            target = Event.getTarget(e);
2064
        } else {
2065
            target = this;
2066
        }
2067
 
2068
        while (target.tagName && target.tagName.toLowerCase() != "td") {
2069
            target = target.parentNode;
2070
            if (!target.tagName || target.tagName.toLowerCase() == "html") {
2071
                return;
2072
            }
2073
        }
2074
 
2075
        if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
2076
            Dom.removeClass(target, cal.Style.CSS_CELL_HOVER);
2077
        }
2078
    },
2079
 
2080
    setupConfig : function() {
2081
 
2082
        var cfg = this.cfg;
2083
 
2084
        /**
2085
        * The date to use to represent "Today".
2086
        *
2087
        * @config today
2088
        * @type Date
2089
        * @default The client side date (new Date()) when the Calendar is instantiated.
2090
        */
2091
        cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler:this.configToday, suppressEvent:true } );
2092
 
2093
        /**
2094
        * The month/year representing the current visible Calendar date (mm/yyyy)
2095
        * @config pagedate
2096
        * @type String | Date
2097
        * @default Today's date
2098
        */
2099
        cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } );
2100
 
2101
        /**
2102
        * The date or range of dates representing the current Calendar selection
2103
        * @config selected
2104
        * @type String
2105
        * @default []
2106
        */
2107
        cfg.addProperty(DEF_CFG.SELECTED.key, { value:DEF_CFG.SELECTED.value.concat(), handler:this.configSelected } );
2108
 
2109
        /**
2110
        * The title to display above the Calendar's month header. The title is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2111
        * @config title
2112
        * @type HTML
2113
        * @default ""
2114
        */
2115
        cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
2116
 
2117
        /**
2118
        * Whether or not a close button should be displayed for this Calendar
2119
        * @config close
2120
        * @type Boolean
2121
        * @default false
2122
        */
2123
        cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
2124
 
2125
        /**
2126
        * 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.
2127
        * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be
2128
        * enabled if required.
2129
        *
2130
        * @config iframe
2131
        * @type Boolean
2132
        * @default true for IE6 and below, false for all other browsers
2133
        */
2134
        cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
2135
 
2136
        /**
2137
        * The minimum selectable date in the current Calendar (mm/dd/yyyy)
2138
        * @config mindate
2139
        * @type String | Date
2140
        * @default null
2141
        */
2142
        cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.configMinDate } );
2143
 
2144
        /**
2145
        * The maximum selectable date in the current Calendar (mm/dd/yyyy)
2146
        * @config maxdate
2147
        * @type String | Date
2148
        * @default null
2149
        */
2150
        cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.configMaxDate } );
2151
 
2152
        // Options properties
2153
 
2154
        /**
2155
        * True if the Calendar should allow multiple selections. False by default.
2156
        * @config MULTI_SELECT
2157
        * @type Boolean
2158
        * @default false
2159
        */
2160
        cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2161
 
2162
        /**
2163
        * True if the Calendar should allow selection of out-of-month dates. False by default.
2164
        * @config OOM_SELECT
2165
        * @type Boolean
2166
        * @default false
2167
        */
2168
        cfg.addProperty(DEF_CFG.OOM_SELECT.key, { value:DEF_CFG.OOM_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2169
 
2170
        /**
2171
        * The weekday the week begins on. Default is 0 (Sunday = 0, Monday = 1 ... Saturday = 6).
2172
        * @config START_WEEKDAY
2173
        * @type number
2174
        * @default 0
2175
        */
2176
        cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.configOptions, validator:cfg.checkNumber  } );
2177
 
2178
        /**
2179
        * True if the Calendar should show weekday labels. True by default.
2180
        * @config SHOW_WEEKDAYS
2181
        * @type Boolean
2182
        * @default true
2183
        */
2184
        cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.configOptions, validator:cfg.checkBoolean  } );
2185
 
2186
        /**
2187
        * True if the Calendar should show week row headers. False by default.
2188
        * @config SHOW_WEEK_HEADER
2189
        * @type Boolean
2190
        * @default false
2191
        */
2192
        cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key, { value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2193
 
2194
        /**
2195
        * True if the Calendar should show week row footers. False by default.
2196
        * @config SHOW_WEEK_FOOTER
2197
        * @type Boolean
2198
        * @default false
2199
        */
2200
        cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2201
 
2202
        /**
2203
        * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
2204
        * @config HIDE_BLANK_WEEKS
2205
        * @type Boolean
2206
        * @default false
2207
        */
2208
        cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key, { value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2209
 
2210
        /**
2211
        * The image URL that should be used for the left navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2212
        * @config NAV_ARROW_LEFT
2213
        * @type String
2214
        * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
2215
        * @default null
2216
        */
2217
        cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.configOptions } );
2218
 
2219
        /**
2220
        * The image URL that should be used for the right navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2221
        * @config NAV_ARROW_RIGHT
2222
        * @type String
2223
        * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
2224
        * @default null
2225
        */
2226
        cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.configOptions } );
2227
 
2228
        // Locale properties
2229
 
2230
        /**
2231
        * The short month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2232
        * @config MONTHS_SHORT
2233
        * @type HTML[]
2234
        * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
2235
        */
2236
        cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.configLocale } );
2237
 
2238
        /**
2239
        * The long month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2240
        * @config MONTHS_LONG
2241
        * @type HTML[]
2242
        * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
2243
        */
2244
        cfg.addProperty(DEF_CFG.MONTHS_LONG.key,  { value:DEF_CFG.MONTHS_LONG.value, handler:this.configLocale } );
2245
 
2246
        /**
2247
        * The 1-character weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2248
        * @config WEEKDAYS_1CHAR
2249
        * @type HTML[]
2250
        * @default ["S", "M", "T", "W", "T", "F", "S"]
2251
        */
2252
        cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.configLocale } );
2253
 
2254
        /**
2255
        * The short weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2256
        * @config WEEKDAYS_SHORT
2257
        * @type HTML[]
2258
        * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
2259
        */
2260
        cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.configLocale } );
2261
 
2262
        /**
2263
        * The medium weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2264
        * @config WEEKDAYS_MEDIUM
2265
        * @type HTML[]
2266
        * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
2267
        */
2268
        cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.configLocale } );
2269
 
2270
        /**
2271
        * The long weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2272
        * @config WEEKDAYS_LONG
2273
        * @type HTML[]
2274
        * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
2275
        */
2276
        cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.configLocale } );
2277
 
2278
        /**
2279
        * Refreshes the locale values used to build the Calendar.
2280
        * @method refreshLocale
2281
        * @private
2282
        */
2283
        var refreshLocale = function() {
2284
            cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
2285
            cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
2286
        };
2287
 
2288
        cfg.subscribeToConfigEvent(DEF_CFG.START_WEEKDAY.key, refreshLocale, this, true);
2289
        cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_SHORT.key, refreshLocale, this, true);
2290
        cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_LONG.key, refreshLocale, this, true);
2291
        cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_1CHAR.key, refreshLocale, this, true);
2292
        cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_SHORT.key, refreshLocale, this, true);
2293
        cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_MEDIUM.key, refreshLocale, this, true);
2294
        cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_LONG.key, refreshLocale, this, true);
2295
 
2296
        /**
2297
        * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
2298
        * @config LOCALE_MONTHS
2299
        * @type String
2300
        * @default "long"
2301
        */
2302
        cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.configLocaleValues } );
2303
 
2304
        /**
2305
        * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
2306
        * @config LOCALE_WEEKDAYS
2307
        * @type String
2308
        * @default "short"
2309
        */
2310
        cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.configLocaleValues } );
2311
 
2312
        /**
2313
        * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to
2314
        * be used when displaying and parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by
2315
        * methods will always represent the Gregorian year, in order to maintain date/month/week values.
2316
        *
2317
        * @config YEAR_OFFSET
2318
        * @type Number
2319
        * @default 0
2320
        */
2321
        cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, handler:this.configLocale  } );
2322
 
2323
        /**
2324
        * The value used to delimit individual dates in a date string passed to various Calendar functions.
2325
        * @config DATE_DELIMITER
2326
        * @type String
2327
        * @default ","
2328
        */
2329
        cfg.addProperty(DEF_CFG.DATE_DELIMITER.key,  { value:DEF_CFG.DATE_DELIMITER.value, handler:this.configLocale } );
2330
 
2331
        /**
2332
        * The value used to delimit date fields in a date string passed to various Calendar functions.
2333
        * @config DATE_FIELD_DELIMITER
2334
        * @type String
2335
        * @default "/"
2336
        */
2337
        cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key, { value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.configLocale } );
2338
 
2339
        /**
2340
        * The value used to delimit date ranges in a date string passed to various Calendar functions.
2341
        * @config DATE_RANGE_DELIMITER
2342
        * @type String
2343
        * @default "-"
2344
        */
2345
        cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key, { value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.configLocale } );
2346
 
2347
        /**
2348
        * The position of the month in a month/year date string
2349
        * @config MY_MONTH_POSITION
2350
        * @type Number
2351
        * @default 1
2352
        */
2353
        cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2354
 
2355
        /**
2356
        * The position of the year in a month/year date string
2357
        * @config MY_YEAR_POSITION
2358
        * @type Number
2359
        * @default 2
2360
        */
2361
        cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2362
 
2363
        /**
2364
        * The position of the month in a month/day date string
2365
        * @config MD_MONTH_POSITION
2366
        * @type Number
2367
        * @default 1
2368
        */
2369
        cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2370
 
2371
        /**
2372
        * The position of the day in a month/year date string
2373
        * @config MD_DAY_POSITION
2374
        * @type Number
2375
        * @default 2
2376
        */
2377
        cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key,  { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2378
 
2379
        /**
2380
        * The position of the month in a month/day/year date string
2381
        * @config MDY_MONTH_POSITION
2382
        * @type Number
2383
        * @default 1
2384
        */
2385
        cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2386
 
2387
        /**
2388
        * The position of the day in a month/day/year date string
2389
        * @config MDY_DAY_POSITION
2390
        * @type Number
2391
        * @default 2
2392
        */
2393
        cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2394
 
2395
        /**
2396
        * The position of the year in a month/day/year date string
2397
        * @config MDY_YEAR_POSITION
2398
        * @type Number
2399
        * @default 3
2400
        */
2401
        cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2402
 
2403
        /**
2404
        * The position of the month in the month year label string used as the Calendar header
2405
        * @config MY_LABEL_MONTH_POSITION
2406
        * @type Number
2407
        * @default 1
2408
        */
2409
        cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2410
 
2411
        /**
2412
        * The position of the year in the month year label string used as the Calendar header
2413
        * @config MY_LABEL_YEAR_POSITION
2414
        * @type Number
2415
        * @default 2
2416
        */
2417
        cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2418
 
2419
        /**
2420
        * The suffix used after the month when rendering the Calendar header. The suffix is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2421
        * @config MY_LABEL_MONTH_SUFFIX
2422
        * @type HTML
2423
        * @default " "
2424
        */
2425
        cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.configLocale } );
2426
 
2427
        /**
2428
        * The suffix used after the year when rendering the Calendar header. The suffix is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2429
        * @config MY_LABEL_YEAR_SUFFIX
2430
        * @type HTML
2431
        * @default ""
2432
        */
2433
        cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.configLocale } );
2434
 
2435
        /**
2436
        * Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a
2437
        * specific Month/Year without having to scroll sequentially through months.
2438
        * <p>
2439
        * Setting this property to null (default value) or false, will disable the CalendarNavigator UI.
2440
        * </p>
2441
        * <p>
2442
        * Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values.
2443
        * </p>
2444
        * <p>
2445
        * This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI.
2446
        * The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object.
2447
        * Any properties which are not provided will use the default values (defined in the CalendarNavigator class).
2448
        * </p>
2449
        * <dl>
2450
        * <dt>strings</dt>
2451
        * <dd><em>Object</em> :  An object with the properties shown below, defining the string labels to use in the Navigator's UI. The strings are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2452
        *     <dl>
2453
        *         <dt>month</dt><dd><em>HTML</em> : The markup to use for the month label. Defaults to "Month".</dd>
2454
        *         <dt>year</dt><dd><em>HTML</em> : The markup to use for the year label. Defaults to "Year".</dd>
2455
        *         <dt>submit</dt><dd><em>HTML</em> : The markup to use for the submit button label. Defaults to "Okay".</dd>
2456
        *         <dt>cancel</dt><dd><em>HTML</em> : The markup to use for the cancel button label. Defaults to "Cancel".</dd>
2457
        *         <dt>invalidYear</dt><dd><em>HTML</em> : The markup to use for invalid year values. Defaults to "Year needs to be a number".</dd>
2458
        *     </dl>
2459
        * </dd>
2460
        * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd>
2461
        * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
2462
        * </dl>
2463
        * <p>E.g.</p>
2464
        * <pre>
2465
        * var navConfig = {
2466
        *   strings: {
2467
        *    month:"Calendar Month",
2468
        *    year:"Calendar Year",
2469
        *    submit: "Submit",
2470
        *    cancel: "Cancel",
2471
        *    invalidYear: "Please enter a valid year"
2472
        *   },
2473
        *   monthFormat: YAHOO.widget.Calendar.SHORT,
2474
        *   initialFocus: "month"
2475
        * }
2476
        * </pre>
2477
        * @config navigator
2478
        * @type {Object|Boolean}
2479
        * @default null
2480
        */
2481
        cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
2482
 
2483
        /**
2484
         * The map of UI strings which the Calendar UI uses.
2485
         *
2486
         * @config strings
2487
         * @type {Object}
2488
         * @default An object with the properties shown below:
2489
         *     <dl>
2490
         *         <dt>previousMonth</dt><dd><em>HTML</em> : The markup to use for the "Previous Month" navigation label. Defaults to "Previous Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
2491
         *         <dt>nextMonth</dt><dd><em>HTML</em> : The markup to use for the "Next Month" navigation UI. Defaults to "Next Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
2492
         *         <dt>close</dt><dd><em>HTML</em> : The markup to use for the close button label. Defaults to "Close". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
2493
         *     </dl>
2494
         */
2495
        cfg.addProperty(DEF_CFG.STRINGS.key, {
2496
            value:DEF_CFG.STRINGS.value,
2497
            handler:this.configStrings,
2498
            validator: function(val) {
2499
                return Lang.isObject(val);
2500
            },
2501
            supercedes:DEF_CFG.STRINGS.supercedes
2502
        });
2503
    },
2504
 
2505
    /**
2506
    * The default handler for the "strings" property
2507
    * @method configStrings
2508
    */
2509
    configStrings : function(type, args, obj) {
2510
        var val = Lang.merge(DEF_CFG.STRINGS.value, args[0]);
2511
        this.cfg.setProperty(DEF_CFG.STRINGS.key, val, true);
2512
    },
2513
 
2514
    /**
2515
    * The default handler for the "pagedate" property
2516
    * @method configPageDate
2517
    */
2518
    configPageDate : function(type, args, obj) {
2519
        this.cfg.setProperty(DEF_CFG.PAGEDATE.key, this._parsePageDate(args[0]), true);
2520
    },
2521
 
2522
    /**
2523
    * The default handler for the "mindate" property
2524
    * @method configMinDate
2525
    */
2526
    configMinDate : function(type, args, obj) {
2527
        var val = args[0];
2528
        if (Lang.isString(val)) {
2529
            val = this._parseDate(val);
2530
            this.cfg.setProperty(DEF_CFG.MINDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
2531
        }
2532
    },
2533
 
2534
    /**
2535
    * The default handler for the "maxdate" property
2536
    * @method configMaxDate
2537
    */
2538
    configMaxDate : function(type, args, obj) {
2539
        var val = args[0];
2540
        if (Lang.isString(val)) {
2541
            val = this._parseDate(val);
2542
            this.cfg.setProperty(DEF_CFG.MAXDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
2543
        }
2544
    },
2545
 
2546
    /**
2547
    * The default handler for the "today" property
2548
    * @method configToday
2549
    */
2550
    configToday : function(type, args, obj) {
2551
        // Only do this for initial set. Changing the today property after the initial
2552
        // set, doesn't affect pagedate
2553
        var val = args[0];
2554
        if (Lang.isString(val)) {
2555
            val = this._parseDate(val);
2556
        }
2557
        var today = DateMath.clearTime(val);
2558
        if (!this.cfg.initialConfig[DEF_CFG.PAGEDATE.key]) {
2559
            this.cfg.setProperty(DEF_CFG.PAGEDATE.key, today);
2560
        }
2561
        this.today = today;
2562
        this.cfg.setProperty(DEF_CFG.TODAY.key, today, true);
2563
    },
2564
 
2565
    /**
2566
    * The default handler for the "selected" property
2567
    * @method configSelected
2568
    */
2569
    configSelected : function(type, args, obj) {
2570
        var selected = args[0],
2571
            cfgSelected = DEF_CFG.SELECTED.key;
2572
 
2573
        if (selected) {
2574
            if (Lang.isString(selected)) {
2575
                this.cfg.setProperty(cfgSelected, this._parseDates(selected), true);
2576
            }
2577
        }
2578
        if (! this._selectedDates) {
2579
            this._selectedDates = this.cfg.getProperty(cfgSelected);
2580
        }
2581
    },
2582
 
2583
    /**
2584
    * The default handler for all configuration options properties
2585
    * @method configOptions
2586
    */
2587
    configOptions : function(type, args, obj) {
2588
        this.Options[type.toUpperCase()] = args[0];
2589
    },
2590
 
2591
    /**
2592
    * The default handler for all configuration locale properties
2593
    * @method configLocale
2594
    */
2595
    configLocale : function(type, args, obj) {
2596
        this.Locale[type.toUpperCase()] = args[0];
2597
 
2598
        this.cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
2599
        this.cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
2600
    },
2601
 
2602
    /**
2603
    * The default handler for all configuration locale field length properties
2604
    * @method configLocaleValues
2605
    */
2606
    configLocaleValues : function(type, args, obj) {
2607
 
2608
        type = type.toLowerCase();
2609
 
2610
        var val = args[0],
2611
            cfg = this.cfg,
2612
            Locale = this.Locale;
2613
 
2614
        switch (type) {
2615
            case DEF_CFG.LOCALE_MONTHS.key:
2616
                switch (val) {
2617
                    case Calendar.SHORT:
2618
                        Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_SHORT.key).concat();
2619
                        break;
2620
                    case Calendar.LONG:
2621
                        Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_LONG.key).concat();
2622
                        break;
2623
                }
2624
                break;
2625
            case DEF_CFG.LOCALE_WEEKDAYS.key:
2626
                switch (val) {
2627
                    case Calendar.ONE_CHAR:
2628
                        Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_1CHAR.key).concat();
2629
                        break;
2630
                    case Calendar.SHORT:
2631
                        Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_SHORT.key).concat();
2632
                        break;
2633
                    case Calendar.MEDIUM:
2634
                        Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_MEDIUM.key).concat();
2635
                        break;
2636
                    case Calendar.LONG:
2637
                        Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_LONG.key).concat();
2638
                        break;
2639
                }
2640
 
2641
                var START_WEEKDAY = cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
2642
 
2643
                if (START_WEEKDAY > 0) {
2644
                    for (var w=0; w < START_WEEKDAY; ++w) {
2645
                        Locale.LOCALE_WEEKDAYS.push(Locale.LOCALE_WEEKDAYS.shift());
2646
                    }
2647
                }
2648
                break;
2649
        }
2650
    },
2651
 
2652
    /**
2653
     * The default handler for the "navigator" property
2654
     * @method configNavigator
2655
     */
2656
    configNavigator : function(type, args, obj) {
2657
        var val = args[0];
2658
        if (YAHOO.widget.CalendarNavigator && (val === true || Lang.isObject(val))) {
2659
            if (!this.oNavigator) {
2660
                this.oNavigator = new YAHOO.widget.CalendarNavigator(this);
2661
                // Cleanup DOM Refs/Events before innerHTML is removed.
2662
                this.beforeRenderEvent.subscribe(function () {
2663
                    if (!this.pages) {
2664
                        this.oNavigator.erase();
2665
                    }
2666
                }, this, true);
2667
            }
2668
        } else {
2669
            if (this.oNavigator) {
2670
                this.oNavigator.destroy();
2671
                this.oNavigator = null;
2672
            }
2673
        }
2674
    },
2675
 
2676
    /**
2677
    * Defines the class names used by Calendar when rendering to DOM. NOTE: The class names are added to the DOM as HTML and should be escaped by the implementor if coming from an external source.
2678
    * @method initStyles
2679
    */
2680
    initStyles : function() {
2681
 
2682
        var defStyle = Calendar.STYLES;
2683
 
2684
        this.Style = {
2685
            /**
2686
            * @property Style.CSS_ROW_HEADER
2687
            */
2688
            CSS_ROW_HEADER: defStyle.CSS_ROW_HEADER,
2689
            /**
2690
            * @property Style.CSS_ROW_FOOTER
2691
            */
2692
            CSS_ROW_FOOTER: defStyle.CSS_ROW_FOOTER,
2693
            /**
2694
            * @property Style.CSS_CELL
2695
            */
2696
            CSS_CELL : defStyle.CSS_CELL,
2697
            /**
2698
            * @property Style.CSS_CELL_SELECTOR
2699
            */
2700
            CSS_CELL_SELECTOR : defStyle.CSS_CELL_SELECTOR,
2701
            /**
2702
            * @property Style.CSS_CELL_SELECTED
2703
            */
2704
            CSS_CELL_SELECTED : defStyle.CSS_CELL_SELECTED,
2705
            /**
2706
            * @property Style.CSS_CELL_SELECTABLE
2707
            */
2708
            CSS_CELL_SELECTABLE : defStyle.CSS_CELL_SELECTABLE,
2709
            /**
2710
            * @property Style.CSS_CELL_RESTRICTED
2711
            */
2712
            CSS_CELL_RESTRICTED : defStyle.CSS_CELL_RESTRICTED,
2713
            /**
2714
            * @property Style.CSS_CELL_TODAY
2715
            */
2716
            CSS_CELL_TODAY : defStyle.CSS_CELL_TODAY,
2717
            /**
2718
            * @property Style.CSS_CELL_OOM
2719
            */
2720
            CSS_CELL_OOM : defStyle.CSS_CELL_OOM,
2721
            /**
2722
            * @property Style.CSS_CELL_OOB
2723
            */
2724
            CSS_CELL_OOB : defStyle.CSS_CELL_OOB,
2725
            /**
2726
            * @property Style.CSS_HEADER
2727
            */
2728
            CSS_HEADER : defStyle.CSS_HEADER,
2729
            /**
2730
            * @property Style.CSS_HEADER_TEXT
2731
            */
2732
            CSS_HEADER_TEXT : defStyle.CSS_HEADER_TEXT,
2733
            /**
2734
            * @property Style.CSS_BODY
2735
            */
2736
            CSS_BODY : defStyle.CSS_BODY,
2737
            /**
2738
            * @property Style.CSS_WEEKDAY_CELL
2739
            */
2740
            CSS_WEEKDAY_CELL : defStyle.CSS_WEEKDAY_CELL,
2741
            /**
2742
            * @property Style.CSS_WEEKDAY_ROW
2743
            */
2744
            CSS_WEEKDAY_ROW : defStyle.CSS_WEEKDAY_ROW,
2745
            /**
2746
            * @property Style.CSS_FOOTER
2747
            */
2748
            CSS_FOOTER : defStyle.CSS_FOOTER,
2749
            /**
2750
            * @property Style.CSS_CALENDAR
2751
            */
2752
            CSS_CALENDAR : defStyle.CSS_CALENDAR,
2753
            /**
2754
            * @property Style.CSS_SINGLE
2755
            */
2756
            CSS_SINGLE : defStyle.CSS_SINGLE,
2757
            /**
2758
            * @property Style.CSS_CONTAINER
2759
            */
2760
            CSS_CONTAINER : defStyle.CSS_CONTAINER,
2761
            /**
2762
            * @property Style.CSS_NAV_LEFT
2763
            */
2764
            CSS_NAV_LEFT : defStyle.CSS_NAV_LEFT,
2765
            /**
2766
            * @property Style.CSS_NAV_RIGHT
2767
            */
2768
            CSS_NAV_RIGHT : defStyle.CSS_NAV_RIGHT,
2769
            /**
2770
            * @property Style.CSS_NAV
2771
            */
2772
            CSS_NAV : defStyle.CSS_NAV,
2773
            /**
2774
            * @property Style.CSS_CLOSE
2775
            */
2776
            CSS_CLOSE : defStyle.CSS_CLOSE,
2777
            /**
2778
            * @property Style.CSS_CELL_TOP
2779
            */
2780
            CSS_CELL_TOP : defStyle.CSS_CELL_TOP,
2781
            /**
2782
            * @property Style.CSS_CELL_LEFT
2783
            */
2784
            CSS_CELL_LEFT : defStyle.CSS_CELL_LEFT,
2785
            /**
2786
            * @property Style.CSS_CELL_RIGHT
2787
            */
2788
            CSS_CELL_RIGHT : defStyle.CSS_CELL_RIGHT,
2789
            /**
2790
            * @property Style.CSS_CELL_BOTTOM
2791
            */
2792
            CSS_CELL_BOTTOM : defStyle.CSS_CELL_BOTTOM,
2793
            /**
2794
            * @property Style.CSS_CELL_HOVER
2795
            */
2796
            CSS_CELL_HOVER : defStyle.CSS_CELL_HOVER,
2797
            /**
2798
            * @property Style.CSS_CELL_HIGHLIGHT1
2799
            */
2800
            CSS_CELL_HIGHLIGHT1 : defStyle.CSS_CELL_HIGHLIGHT1,
2801
            /**
2802
            * @property Style.CSS_CELL_HIGHLIGHT2
2803
            */
2804
            CSS_CELL_HIGHLIGHT2 : defStyle.CSS_CELL_HIGHLIGHT2,
2805
            /**
2806
            * @property Style.CSS_CELL_HIGHLIGHT3
2807
            */
2808
            CSS_CELL_HIGHLIGHT3 : defStyle.CSS_CELL_HIGHLIGHT3,
2809
            /**
2810
            * @property Style.CSS_CELL_HIGHLIGHT4
2811
            */
2812
            CSS_CELL_HIGHLIGHT4 : defStyle.CSS_CELL_HIGHLIGHT4,
2813
            /**
2814
             * @property Style.CSS_WITH_TITLE
2815
             */
2816
            CSS_WITH_TITLE : defStyle.CSS_WITH_TITLE,
2817
             /**
2818
             * @property Style.CSS_FIXED_SIZE
2819
             */
2820
            CSS_FIXED_SIZE : defStyle.CSS_FIXED_SIZE,
2821
             /**
2822
             * @property Style.CSS_LINK_CLOSE
2823
             */
2824
            CSS_LINK_CLOSE : defStyle.CSS_LINK_CLOSE
2825
        };
2826
    },
2827
 
2828
    /**
2829
    * Builds the date label that will be displayed in the calendar header or
2830
    * footer, depending on configuration.
2831
    * @method buildMonthLabel
2832
    * @return {HTML} The formatted calendar month label
2833
    */
2834
    buildMonthLabel : function() {
2835
        return this._buildMonthLabel(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
2836
    },
2837
 
2838
    /**
2839
     * Helper method, to format a Month Year string, given a JavaScript Date, based on the
2840
     * Calendar localization settings
2841
     *
2842
     * @method _buildMonthLabel
2843
     * @private
2844
     * @param {Date} date
2845
     * @return {HTML} Formated month, year string
2846
     */
2847
    _buildMonthLabel : function(date) {
2848
        var monthLabel  = this.Locale.LOCALE_MONTHS[date.getMonth()] + this.Locale.MY_LABEL_MONTH_SUFFIX,
2849
            yearLabel = (date.getFullYear() + this.Locale.YEAR_OFFSET) + this.Locale.MY_LABEL_YEAR_SUFFIX;
2850
 
2851
        if (this.Locale.MY_LABEL_MONTH_POSITION == 2 || this.Locale.MY_LABEL_YEAR_POSITION == 1) {
2852
            return yearLabel + monthLabel;
2853
        } else {
2854
            return monthLabel + yearLabel;
2855
        }
2856
    },
2857
 
2858
    /**
2859
    * Builds the date digit that will be displayed in calendar cells
2860
    * @method buildDayLabel
2861
    * @param {Date} workingDate The current working date
2862
    * @return {Number} The day
2863
    */
2864
    buildDayLabel : function(workingDate) {
2865
        return workingDate.getDate();
2866
    },
2867
 
2868
    /**
2869
     * Creates the title bar element and adds it to Calendar container DIV. NOTE: The title parameter passed into this method is added to the DOM as HTML and should be escaped by the implementor if coming from an external source.
2870
     *
2871
     * @method createTitleBar
2872
     * @param {HTML} strTitle The title to display in the title bar
2873
     * @return The title bar element
2874
     */
2875
    createTitleBar : function(strTitle) {
2876
        var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || document.createElement("div");
2877
        tDiv.className = YAHOO.widget.CalendarGroup.CSS_2UPTITLE;
2878
        tDiv.innerHTML = strTitle;
2879
        this.oDomContainer.insertBefore(tDiv, this.oDomContainer.firstChild);
2880
 
2881
        Dom.addClass(this.oDomContainer, this.Style.CSS_WITH_TITLE);
2882
 
2883
        return tDiv;
2884
    },
2885
 
2886
    /**
2887
     * Removes the title bar element from the DOM
2888
     *
2889
     * @method removeTitleBar
2890
     */
2891
    removeTitleBar : function() {
2892
        var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || null;
2893
        if (tDiv) {
2894
            Event.purgeElement(tDiv);
2895
            this.oDomContainer.removeChild(tDiv);
2896
        }
2897
        Dom.removeClass(this.oDomContainer, this.Style.CSS_WITH_TITLE);
2898
    },
2899
 
2900
    /**
2901
     * Creates the close button HTML element and adds it to Calendar container DIV
2902
     *
2903
     * @method createCloseButton
2904
     * @return {HTMLElement} The close HTML element created
2905
     */
2906
    createCloseButton : function() {
2907
        var cssClose = YAHOO.widget.CalendarGroup.CSS_2UPCLOSE,
2908
            cssLinkClose = this.Style.CSS_LINK_CLOSE,
2909
            DEPR_CLOSE_PATH = "us/my/bn/x_d.gif",
2910
 
2911
            lnk = Dom.getElementsByClassName(cssLinkClose, "a", this.oDomContainer)[0],
2912
            strings = this.cfg.getProperty(DEF_CFG.STRINGS.key),
2913
            closeStr = (strings && strings.close) ? strings.close : "";
2914
 
2915
        if (!lnk) {
2916
            lnk = document.createElement("a");
2917
            Event.addListener(lnk, "click", function(e, cal) {
2918
                cal.hide();
2919
                Event.preventDefault(e);
2920
            }, this);
2921
        }
2922
 
2923
        lnk.href = "#";
2924
        lnk.className = cssLinkClose;
2925
 
2926
        if (Calendar.IMG_ROOT !== null) {
2927
            var img = Dom.getElementsByClassName(cssClose, "img", lnk)[0] || document.createElement("img");
2928
            img.src = Calendar.IMG_ROOT + DEPR_CLOSE_PATH;
2929
            img.className = cssClose;
2930
            lnk.appendChild(img);
2931
        } else {
2932
            lnk.innerHTML = '<span class="' + cssClose + ' ' + this.Style.CSS_CLOSE + '">' + closeStr + '</span>';
2933
        }
2934
        this.oDomContainer.appendChild(lnk);
2935
 
2936
        return lnk;
2937
    },
2938
 
2939
    /**
2940
     * Removes the close button HTML element from the DOM
2941
     *
2942
     * @method removeCloseButton
2943
     */
2944
    removeCloseButton : function() {
2945
        var btn = Dom.getElementsByClassName(this.Style.CSS_LINK_CLOSE, "a", this.oDomContainer)[0] || null;
2946
        if (btn) {
2947
            Event.purgeElement(btn);
2948
            this.oDomContainer.removeChild(btn);
2949
        }
2950
    },
2951
 
2952
    /**
2953
    * Renders the calendar header. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2954
    * @method renderHeader
2955
    * @param {HTML[]} html The current working HTML array
2956
    * @return {HTML[]} The current working HTML array
2957
    */
2958
    renderHeader : function(html) {
2959
 
2960
 
2961
        var colSpan = 7,
2962
            DEPR_NAV_LEFT = "us/tr/callt.gif",
2963
            DEPR_NAV_RIGHT = "us/tr/calrt.gif",
2964
            cfg = this.cfg,
2965
            pageDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
2966
            strings= cfg.getProperty(DEF_CFG.STRINGS.key),
2967
            prevStr = (strings && strings.previousMonth) ?  strings.previousMonth : "",
2968
            nextStr = (strings && strings.nextMonth) ? strings.nextMonth : "",
2969
            monthLabel;
2970
 
2971
        if (cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
2972
            colSpan += 1;
2973
        }
2974
 
2975
        if (cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
2976
            colSpan += 1;
2977
        }
2978
 
2979
        html[html.length] = "<thead>";
2980
        html[html.length] =  "<tr>";
2981
        html[html.length] =   '<th colspan="' + colSpan + '" class="' + this.Style.CSS_HEADER_TEXT + '">';
2982
        html[html.length] =    '<div class="' + this.Style.CSS_HEADER + '">';
2983
 
2984
        var renderLeft, renderRight = false;
2985
 
2986
        if (this.parent) {
2987
            if (this.index === 0) {
2988
                renderLeft = true;
2989
            }
2990
            if (this.index == (this.parent.cfg.getProperty("pages") -1)) {
2991
                renderRight = true;
2992
            }
2993
        } else {
2994
            renderLeft = true;
2995
            renderRight = true;
2996
        }
2997
 
2998
        if (renderLeft) {
2999
            monthLabel  = this._buildMonthLabel(DateMath.subtract(pageDate, DateMath.MONTH, 1));
3000
 
3001
            var leftArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_LEFT.key);
3002
            // 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
3003
            if (leftArrow === null && Calendar.IMG_ROOT !== null) {
3004
                leftArrow = Calendar.IMG_ROOT + DEPR_NAV_LEFT;
3005
            }
3006
            var leftStyle = (leftArrow === null) ? "" : ' style="background-image:url(' + leftArrow + ')"';
3007
            html[html.length] = '<a class="' + this.Style.CSS_NAV_LEFT + '"' + leftStyle + ' href="#">' + prevStr + ' (' + monthLabel + ')' + '</a>';
3008
        }
3009
 
3010
        var lbl = this.buildMonthLabel();
3011
        var cal = this.parent || this;
3012
        if (cal.cfg.getProperty("navigator")) {
3013
            lbl = "<a class=\"" + this.Style.CSS_NAV + "\" href=\"#\">" + lbl + "</a>";
3014
        }
3015
        html[html.length] = lbl;
3016
 
3017
        if (renderRight) {
3018
            monthLabel  = this._buildMonthLabel(DateMath.add(pageDate, DateMath.MONTH, 1));
3019
 
3020
            var rightArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_RIGHT.key);
3021
            if (rightArrow === null && Calendar.IMG_ROOT !== null) {
3022
                rightArrow = Calendar.IMG_ROOT + DEPR_NAV_RIGHT;
3023
            }
3024
            var rightStyle = (rightArrow === null) ? "" : ' style="background-image:url(' + rightArrow + ')"';
3025
            html[html.length] = '<a class="' + this.Style.CSS_NAV_RIGHT + '"' + rightStyle + ' href="#">' + nextStr + ' (' + monthLabel + ')' + '</a>';
3026
        }
3027
 
3028
        html[html.length] = '</div>\n</th>\n</tr>';
3029
 
3030
        if (cfg.getProperty(DEF_CFG.SHOW_WEEKDAYS.key)) {
3031
            html = this.buildWeekdays(html);
3032
        }
3033
 
3034
        html[html.length] = '</thead>';
3035
 
3036
        return html;
3037
    },
3038
 
3039
    /**
3040
    * Renders the Calendar's weekday headers. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
3041
    * @method buildWeekdays
3042
    * @param {HTML[]} html The current working HTML array
3043
    * @return {HTML[]} The current working HTML array
3044
    */
3045
    buildWeekdays : function(html) {
3046
 
3047
        html[html.length] = '<tr class="' + this.Style.CSS_WEEKDAY_ROW + '">';
3048
 
3049
        if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
3050
            html[html.length] = '<th>&#160;</th>';
3051
        }
3052
 
3053
        for(var i=0;i < this.Locale.LOCALE_WEEKDAYS.length; ++i) {
3054
            html[html.length] = '<th class="' + this.Style.CSS_WEEKDAY_CELL + '">' + this.Locale.LOCALE_WEEKDAYS[i] + '</th>';
3055
        }
3056
 
3057
        if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
3058
            html[html.length] = '<th>&#160;</th>';
3059
        }
3060
 
3061
        html[html.length] = '</tr>';
3062
 
3063
        return html;
3064
    },
3065
 
3066
    /**
3067
    * Renders the calendar body. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
3068
    * @method renderBody
3069
    * @param {Date} workingDate The current working Date being used for the render process
3070
    * @param {HTML[]} html The current working HTML array
3071
    * @return {HTML[]} The current working HTML array
3072
    */
3073
    renderBody : function(workingDate, html) {
3074
 
3075
        var startDay = this.cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
3076
 
3077
        this.preMonthDays = workingDate.getDay();
3078
        if (startDay > 0) {
3079
            this.preMonthDays -= startDay;
3080
        }
3081
        if (this.preMonthDays < 0) {
3082
            this.preMonthDays += 7;
3083
        }
3084
 
3085
        this.monthDays = DateMath.findMonthEnd(workingDate).getDate();
3086
        this.postMonthDays = Calendar.DISPLAY_DAYS-this.preMonthDays-this.monthDays;
3087
 
3088
 
3089
        workingDate = DateMath.subtract(workingDate, DateMath.DAY, this.preMonthDays);
3090
 
3091
        var weekNum,
3092
            weekClass,
3093
            weekPrefix = "w",
3094
            cellPrefix = "_cell",
3095
            workingDayPrefix = "wd",
3096
            dayPrefix = "d",
3097
            cellRenderers,
3098
            renderer,
3099
            t = this.today,
3100
            cfg = this.cfg,
3101
            oom,
3102
            todayYear = t.getFullYear(),
3103
            todayMonth = t.getMonth(),
3104
            todayDate = t.getDate(),
3105
            useDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
3106
            hideBlankWeeks = cfg.getProperty(DEF_CFG.HIDE_BLANK_WEEKS.key),
3107
            showWeekFooter = cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key),
3108
            showWeekHeader = cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key),
3109
            oomSelect = cfg.getProperty(DEF_CFG.OOM_SELECT.key),
3110
            mindate = cfg.getProperty(DEF_CFG.MINDATE.key),
3111
            maxdate = cfg.getProperty(DEF_CFG.MAXDATE.key),
3112
            yearOffset = this.Locale.YEAR_OFFSET;
3113
 
3114
        if (mindate) {
3115
            mindate = DateMath.clearTime(mindate);
3116
        }
3117
        if (maxdate) {
3118
            maxdate = DateMath.clearTime(maxdate);
3119
        }
3120
 
3121
        html[html.length] = '<tbody class="m' + (useDate.getMonth()+1) + ' ' + this.Style.CSS_BODY + '">';
3122
 
3123
        var i = 0,
3124
            tempDiv = document.createElement("div"),
3125
            cell = document.createElement("td");
3126
 
3127
        tempDiv.appendChild(cell);
3128
 
3129
        var cal = this.parent || this;
3130
 
3131
        for (var r = 0; r < 6; r++) {
3132
            weekNum = DateMath.getWeekNumber(workingDate, startDay);
3133
            weekClass = weekPrefix + weekNum;
3134
 
3135
            // Local OOM check for performance, since we already have pagedate
3136
            if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth()) {
3137
                break;
3138
            } else {
3139
                html[html.length] = '<tr class="' + weekClass + '">';
3140
 
3141
                if (showWeekHeader) { html = this.renderRowHeader(weekNum, html); }
3142
 
3143
                for (var d=0; d < 7; d++){ // Render actual days
3144
 
3145
                    cellRenderers = [];
3146
 
3147
                    this.clearElement(cell);
3148
                    cell.className = this.Style.CSS_CELL;
3149
                    cell.id = this.id + cellPrefix + i;
3150
 
3151
                    if (workingDate.getDate()  == todayDate &&
3152
                        workingDate.getMonth()  == todayMonth &&
3153
                        workingDate.getFullYear() == todayYear) {
3154
                        cellRenderers[cellRenderers.length]=cal.renderCellStyleToday;
3155
                    }
3156
 
3157
                    var workingArray = [workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()];
3158
                    this.cellDates[this.cellDates.length] = workingArray; // Add this date to cellDates
3159
 
3160
                    // Local OOM check for performance, since we already have pagedate
3161
                    oom = workingDate.getMonth() != useDate.getMonth();
3162
                    if (oom && !oomSelect) {
3163
                        cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth;
3164
                    } else {
3165
                        Dom.addClass(cell, workingDayPrefix + workingDate.getDay());
3166
                        Dom.addClass(cell, dayPrefix + workingDate.getDate());
3167
 
3168
                        // Concat, so that we're not splicing from an array
3169
                        // which we're also iterating
3170
                        var rs = this.renderStack.concat();
3171
 
3172
                        for (var s=0, l = rs.length; s < l; ++s) {
3173
 
3174
                            renderer = null;
3175
 
3176
                            var rArray = rs[s],
3177
                                type = rArray[0],
3178
                                month,
3179
                                day,
3180
                                year;
3181
 
3182
                            switch (type) {
3183
                                case Calendar.DATE:
3184
                                    month = rArray[1][1];
3185
                                    day = rArray[1][2];
3186
                                    year = rArray[1][0];
3187
 
3188
                                    if (workingDate.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == year) {
3189
                                        renderer = rArray[2];
3190
                                        this.renderStack.splice(s,1);
3191
                                    }
3192
 
3193
                                    break;
3194
                                case Calendar.MONTH_DAY:
3195
                                    month = rArray[1][0];
3196
                                    day = rArray[1][1];
3197
 
3198
                                    if (workingDate.getMonth()+1 == month && workingDate.getDate() == day) {
3199
                                        renderer = rArray[2];
3200
                                        this.renderStack.splice(s,1);
3201
                                    }
3202
                                    break;
3203
                                case Calendar.RANGE:
3204
                                    var date1 = rArray[1][0],
3205
                                        date2 = rArray[1][1],
3206
                                        d1month = date1[1],
3207
                                        d1day = date1[2],
3208
                                        d1year = date1[0],
3209
                                        d1 = DateMath.getDate(d1year, d1month-1, d1day),
3210
                                        d2month = date2[1],
3211
                                        d2day = date2[2],
3212
                                        d2year = date2[0],
3213
                                        d2 = DateMath.getDate(d2year, d2month-1, d2day);
3214
 
3215
                                    if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime()) {
3216
                                        renderer = rArray[2];
3217
 
3218
                                        if (workingDate.getTime()==d2.getTime()) {
3219
                                            this.renderStack.splice(s,1);
3220
                                        }
3221
                                    }
3222
                                    break;
3223
                                case Calendar.WEEKDAY:
3224
                                    var weekday = rArray[1][0];
3225
                                    if (workingDate.getDay()+1 == weekday) {
3226
                                        renderer = rArray[2];
3227
                                    }
3228
                                    break;
3229
                                case Calendar.MONTH:
3230
                                    month = rArray[1][0];
3231
                                    if (workingDate.getMonth()+1 == month) {
3232
                                        renderer = rArray[2];
3233
                                    }
3234
                                    break;
3235
                            }
3236
 
3237
                            if (renderer) {
3238
                                cellRenderers[cellRenderers.length]=renderer;
3239
                            }
3240
                        }
3241
 
3242
                    }
3243
 
3244
                    if (this._indexOfSelectedFieldArray(workingArray) > -1) {
3245
                        cellRenderers[cellRenderers.length]=cal.renderCellStyleSelected;
3246
                    }
3247
 
3248
                    if (oom) {
3249
                        cellRenderers[cellRenderers.length] = cal.styleCellNotThisMonth;
3250
                    }
3251
 
3252
                    if ((mindate && (workingDate.getTime() < mindate.getTime())) || (maxdate && (workingDate.getTime() > maxdate.getTime()))) {
3253
                        cellRenderers[cellRenderers.length] = cal.renderOutOfBoundsDate;
3254
                    } else {
3255
                        cellRenderers[cellRenderers.length] = cal.styleCellDefault;
3256
                        cellRenderers[cellRenderers.length] = cal.renderCellDefault;
3257
                    }
3258
 
3259
                    for (var x=0; x < cellRenderers.length; ++x) {
3260
                        if (cellRenderers[x].call(cal, workingDate, cell) == Calendar.STOP_RENDER) {
3261
                            break;
3262
                        }
3263
                    }
3264
 
3265
                    workingDate.setTime(workingDate.getTime() + DateMath.ONE_DAY_MS);
3266
                    // Just in case we crossed DST/Summertime boundaries
3267
                    workingDate = DateMath.clearTime(workingDate);
3268
 
3269
                    if (i >= 0 && i <= 6) {
3270
                        Dom.addClass(cell, this.Style.CSS_CELL_TOP);
3271
                    }
3272
                    if ((i % 7) === 0) {
3273
                        Dom.addClass(cell, this.Style.CSS_CELL_LEFT);
3274
                    }
3275
                    if (((i+1) % 7) === 0) {
3276
                        Dom.addClass(cell, this.Style.CSS_CELL_RIGHT);
3277
                    }
3278
 
3279
                    var postDays = this.postMonthDays;
3280
                    if (hideBlankWeeks && postDays >= 7) {
3281
                        var blankWeeks = Math.floor(postDays/7);
3282
                        for (var p=0;p<blankWeeks;++p) {
3283
                            postDays -= 7;
3284
                        }
3285
                    }
3286
 
3287
                    if (i >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
3288
                        Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM);
3289
                    }
3290
 
3291
                    html[html.length] = tempDiv.innerHTML;
3292
                    i++;
3293
                }
3294
 
3295
                if (showWeekFooter) { html = this.renderRowFooter(weekNum, html); }
3296
 
3297
                html[html.length] = '</tr>';
3298
            }
3299
        }
3300
 
3301
        html[html.length] = '</tbody>';
3302
 
3303
        return html;
3304
    },
3305
 
3306
    /**
3307
    * Renders the calendar footer. In the default implementation, there is no footer. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
3308
    * @method renderFooter
3309
    * @param {HTML[]} html The current working HTML array
3310
    * @return {HTML[]} The current working HTML array
3311
    */
3312
    renderFooter : function(html) { return html; },
3313
 
3314
    /**
3315
    * Renders the calendar after it has been configured. The render() method has a specific call chain that will execute
3316
    * when the method is called: renderHeader, renderBody, renderFooter.
3317
    * Refer to the documentation for those methods for information on individual render tasks.
3318
    * @method render
3319
    */
3320
    render : function() {
3321
        this.beforeRenderEvent.fire();
3322
 
3323
        // Find starting day of the current month
3324
        var workingDate = DateMath.findMonthStart(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
3325
 
3326
        this.resetRenderers();
3327
        this.cellDates.length = 0;
3328
 
3329
        Event.purgeElement(this.oDomContainer, true);
3330
 
3331
        var html = [],
3332
            table;
3333
 
3334
        html[html.length] = '<table cellSpacing="0" class="' + this.Style.CSS_CALENDAR + ' y' + (workingDate.getFullYear() + this.Locale.YEAR_OFFSET) +'" id="' + this.id + '">';
3335
        html = this.renderHeader(html);
3336
        html = this.renderBody(workingDate, html);
3337
        html = this.renderFooter(html);
3338
        html[html.length] = '</table>';
3339
 
3340
        this.oDomContainer.innerHTML = html.join("\n");
3341
 
3342
        this.applyListeners();
3343
 
3344
        // Using oDomContainer.ownerDocument, to allow for cross-frame rendering
3345
        table = ((this._oDoc) && this._oDoc.getElementById(this.id)) || (this.id);
3346
 
3347
        this.cells = Dom.getElementsByClassName(this.Style.CSS_CELL, "td", table);
3348
 
3349
        this.cfg.refireEvent(DEF_CFG.TITLE.key);
3350
        this.cfg.refireEvent(DEF_CFG.CLOSE.key);
3351
        this.cfg.refireEvent(DEF_CFG.IFRAME.key);
3352
 
3353
        this.renderEvent.fire();
3354
    },
3355
 
3356
    /**
3357
    * Applies the Calendar's DOM listeners to applicable elements.
3358
    * @method applyListeners
3359
    */
3360
    applyListeners : function() {
3361
        var root = this.oDomContainer,
3362
            cal = this.parent || this,
3363
            anchor = "a",
3364
            click = "click";
3365
 
3366
        var linkLeft = Dom.getElementsByClassName(this.Style.CSS_NAV_LEFT, anchor, root),
3367
            linkRight = Dom.getElementsByClassName(this.Style.CSS_NAV_RIGHT, anchor, root);
3368
 
3369
        if (linkLeft && linkLeft.length > 0) {
3370
            this.linkLeft = linkLeft[0];
3371
            Event.addListener(this.linkLeft, click, this.doPreviousMonthNav, cal, true);
3372
        }
3373
 
3374
        if (linkRight && linkRight.length > 0) {
3375
            this.linkRight = linkRight[0];
3376
            Event.addListener(this.linkRight, click, this.doNextMonthNav, cal, true);
3377
        }
3378
 
3379
        if (cal.cfg.getProperty("navigator") !== null) {
3380
            this.applyNavListeners();
3381
        }
3382
 
3383
        if (this.domEventMap) {
3384
            var el,elements;
3385
            for (var cls in this.domEventMap) {
3386
                if (Lang.hasOwnProperty(this.domEventMap, cls)) {
3387
                    var items = this.domEventMap[cls];
3388
 
3389
                    if (! (items instanceof Array)) {
3390
                        items = [items];
3391
                    }
3392
 
3393
                    for (var i=0;i<items.length;i++) {
3394
                        var item = items[i];
3395
                        elements = Dom.getElementsByClassName(cls, item.tag, this.oDomContainer);
3396
 
3397
                        for (var c=0;c<elements.length;c++) {
3398
                            el = elements[c];
3399
                             Event.addListener(el, item.event, item.handler, item.scope, item.correct );
3400
                        }
3401
                    }
3402
                }
3403
            }
3404
        }
3405
 
3406
        Event.addListener(this.oDomContainer, "click", this.doSelectCell, this);
3407
        Event.addListener(this.oDomContainer, "mouseover", this.doCellMouseOver, this);
3408
        Event.addListener(this.oDomContainer, "mouseout", this.doCellMouseOut, this);
3409
    },
3410
 
3411
    /**
3412
    * Applies the DOM listeners to activate the Calendar Navigator.
3413
    * @method applyNavListeners
3414
    */
3415
    applyNavListeners : function() {
3416
        var calParent = this.parent || this,
3417
            cal = this,
3418
            navBtns = Dom.getElementsByClassName(this.Style.CSS_NAV, "a", this.oDomContainer);
3419
 
3420
        if (navBtns.length > 0) {
3421
 
3422
            Event.addListener(navBtns, "click", function (e, obj) {
3423
                var target = Event.getTarget(e);
3424
                // this == navBtn
3425
                if (this === target || Dom.isAncestor(this, target)) {
3426
                    Event.preventDefault(e);
3427
                }
3428
                var navigator = calParent.oNavigator;
3429
                if (navigator) {
3430
                    var pgdate = cal.cfg.getProperty("pagedate");
3431
                    navigator.setYear(pgdate.getFullYear() + cal.Locale.YEAR_OFFSET);
3432
                    navigator.setMonth(pgdate.getMonth());
3433
                    navigator.show();
3434
                }
3435
            });
3436
        }
3437
    },
3438
 
3439
    /**
3440
    * Retrieves the Date object for the specified Calendar cell
3441
    * @method getDateByCellId
3442
    * @param {String} id The id of the cell
3443
    * @return {Date} The Date object for the specified Calendar cell
3444
    */
3445
    getDateByCellId : function(id) {
3446
        var date = this.getDateFieldsByCellId(id);
3447
        return (date) ? DateMath.getDate(date[0],date[1]-1,date[2]) : null;
3448
    },
3449
 
3450
    /**
3451
    * Retrieves the Date object for the specified Calendar cell
3452
    * @method getDateFieldsByCellId
3453
    * @param {String} id The id of the cell
3454
    * @return {Array} The array of Date fields for the specified Calendar cell
3455
    */
3456
    getDateFieldsByCellId : function(id) {
3457
        id = this.getIndexFromId(id);
3458
        return (id > -1) ? this.cellDates[id] : null;
3459
    },
3460
 
3461
    /**
3462
     * Find the Calendar's cell index for a given date.
3463
     * If the date is not found, the method returns -1.
3464
     * <p>
3465
     * The returned index can be used to lookup the cell HTMLElement
3466
     * using the Calendar's cells array or passed to selectCell to select
3467
     * cells by index.
3468
     * </p>
3469
     *
3470
     * See <a href="#cells">cells</a>, <a href="#selectCell">selectCell</a>.
3471
     *
3472
     * @method getCellIndex
3473
     * @param {Date} date JavaScript Date object, for which to find a cell index.
3474
     * @return {Number} The index of the date in Calendars cellDates/cells arrays, or -1 if the date
3475
     * is not on the curently rendered Calendar page.
3476
     */
3477
    getCellIndex : function(date) {
3478
        var idx = -1;
3479
        if (date) {
3480
            var m = date.getMonth(),
3481
                y = date.getFullYear(),
3482
                d = date.getDate(),
3483
                dates = this.cellDates;
3484
 
3485
            for (var i = 0; i < dates.length; ++i) {
3486
                var cellDate = dates[i];
3487
                if (cellDate[0] === y && cellDate[1] === m+1 && cellDate[2] === d) {
3488
                    idx = i;
3489
                    break;
3490
                }
3491
            }
3492
        }
3493
        return idx;
3494
    },
3495
 
3496
    /**
3497
     * Given the id used to mark each Calendar cell, this method
3498
     * extracts the index number from the id.
3499
     *
3500
     * @param {String} strId The cell id
3501
     * @return {Number} The index of the cell, or -1 if id does not contain an index number
3502
     */
3503
    getIndexFromId : function(strId) {
3504
        var idx = -1,
3505
            li = strId.lastIndexOf("_cell");
3506
 
3507
        if (li > -1) {
3508
            idx = parseInt(strId.substring(li + 5), 10);
3509
        }
3510
 
3511
        return idx;
3512
    },
3513
 
3514
    // BEGIN BUILT-IN TABLE CELL RENDERERS
3515
 
3516
    /**
3517
    * Renders a cell that falls before the minimum date or after the maximum date.
3518
    * @method renderOutOfBoundsDate
3519
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3520
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3521
    * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3522
    *   should not be terminated
3523
    */
3524
    renderOutOfBoundsDate : function(workingDate, cell) {
3525
        Dom.addClass(cell, this.Style.CSS_CELL_OOB);
3526
        cell.innerHTML = workingDate.getDate();
3527
        return Calendar.STOP_RENDER;
3528
    },
3529
 
3530
    /**
3531
    * Renders the row header HTML for a week.
3532
    *
3533
    * @method renderRowHeader
3534
    * @param {Number} weekNum The week number of the current row
3535
    * @param {HTML[]} cell The current working HTML array
3536
    */
3537
    renderRowHeader : function(weekNum, html) {
3538
        html[html.length] = '<th class="' + this.Style.CSS_ROW_HEADER + '">' + weekNum + '</th>';
3539
        return html;
3540
    },
3541
 
3542
    /**
3543
    * Renders the row footer HTML for a week.
3544
    *
3545
    * @method renderRowFooter
3546
    * @param {Number} weekNum The week number of the current row
3547
    * @param {HTML[]} cell The current working HTML array
3548
    */
3549
    renderRowFooter : function(weekNum, html) {
3550
        html[html.length] = '<th class="' + this.Style.CSS_ROW_FOOTER + '">' + weekNum + '</th>';
3551
        return html;
3552
    },
3553
 
3554
    /**
3555
    * Renders a single standard calendar cell in the calendar widget table.
3556
    *
3557
    * All logic for determining how a standard default cell will be rendered is
3558
    * encapsulated in this method, and must be accounted for when extending the
3559
    * widget class.
3560
    *
3561
    * @method renderCellDefault
3562
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3563
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3564
    */
3565
    renderCellDefault : function(workingDate, cell) {
3566
        cell.innerHTML = '<a href="#" class="' + this.Style.CSS_CELL_SELECTOR + '">' + this.buildDayLabel(workingDate) + "</a>";
3567
    },
3568
 
3569
    /**
3570
    * Styles a selectable cell.
3571
    * @method styleCellDefault
3572
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3573
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3574
    */
3575
    styleCellDefault : function(workingDate, cell) {
3576
        Dom.addClass(cell, this.Style.CSS_CELL_SELECTABLE);
3577
    },
3578
 
3579
 
3580
    /**
3581
    * Renders a single standard calendar cell using the CSS hightlight1 style
3582
    * @method renderCellStyleHighlight1
3583
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3584
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3585
    */
3586
    renderCellStyleHighlight1 : function(workingDate, cell) {
3587
        Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
3588
    },
3589
 
3590
    /**
3591
    * Renders a single standard calendar cell using the CSS hightlight2 style
3592
    * @method renderCellStyleHighlight2
3593
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3594
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3595
    */
3596
    renderCellStyleHighlight2 : function(workingDate, cell) {
3597
        Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
3598
    },
3599
 
3600
    /**
3601
    * Renders a single standard calendar cell using the CSS hightlight3 style
3602
    * @method renderCellStyleHighlight3
3603
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3604
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3605
    */
3606
    renderCellStyleHighlight3 : function(workingDate, cell) {
3607
        Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
3608
    },
3609
 
3610
    /**
3611
    * Renders a single standard calendar cell using the CSS hightlight4 style
3612
    * @method renderCellStyleHighlight4
3613
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3614
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3615
    */
3616
    renderCellStyleHighlight4 : function(workingDate, cell) {
3617
        Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
3618
    },
3619
 
3620
    /**
3621
    * Applies the default style used for rendering today's date to the current calendar cell
3622
    * @method renderCellStyleToday
3623
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3624
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3625
    */
3626
    renderCellStyleToday : function(workingDate, cell) {
3627
        Dom.addClass(cell, this.Style.CSS_CELL_TODAY);
3628
    },
3629
 
3630
    /**
3631
    * Applies the default style used for rendering selected dates to the current calendar cell
3632
    * @method renderCellStyleSelected
3633
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3634
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3635
    * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3636
    *   should not be terminated
3637
    */
3638
    renderCellStyleSelected : function(workingDate, cell) {
3639
        Dom.addClass(cell, this.Style.CSS_CELL_SELECTED);
3640
    },
3641
 
3642
    /**
3643
    * Applies the default style used for rendering dates that are not a part of the current
3644
    * month (preceding or trailing the cells for the current month)
3645
    *
3646
    * @method renderCellNotThisMonth
3647
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3648
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3649
    * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3650
    *   should not be terminated
3651
    */
3652
    renderCellNotThisMonth : function(workingDate, cell) {
3653
        this.styleCellNotThisMonth(workingDate, cell);
3654
        cell.innerHTML=workingDate.getDate();
3655
        return Calendar.STOP_RENDER;
3656
    },
3657
 
3658
    /** Applies the style used for rendering out-of-month dates to the current calendar cell
3659
    * @method styleCellNotThisMonth
3660
    * @param {Date}                 workingDate     The current working Date object being used to generate the calendar
3661
    * @param {HTMLTableCellElement} cell            The current working cell in the calendar
3662
    */
3663
    styleCellNotThisMonth : function(workingDate, cell) {
3664
        YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM);
3665
    },
3666
 
3667
    /**
3668
    * Renders the current calendar cell as a non-selectable "black-out" date using the default
3669
    * restricted style.
3670
    * @method renderBodyCellRestricted
3671
    * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3672
    * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3673
    * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3674
    *   should not be terminated
3675
    */
3676
    renderBodyCellRestricted : function(workingDate, cell) {
3677
        Dom.addClass(cell, this.Style.CSS_CELL);
3678
        Dom.addClass(cell, this.Style.CSS_CELL_RESTRICTED);
3679
        cell.innerHTML=workingDate.getDate();
3680
        return Calendar.STOP_RENDER;
3681
    },
3682
 
3683
    // END BUILT-IN TABLE CELL RENDERERS
3684
 
3685
    // BEGIN MONTH NAVIGATION METHODS
3686
 
3687
    /**
3688
    * Adds the designated number of months to the current calendar month, and sets the current
3689
    * calendar page date to the new month.
3690
    * @method addMonths
3691
    * @param {Number} count The number of months to add to the current calendar
3692
    */
3693
    addMonths : function(count) {
3694
        var cfgPageDate = DEF_CFG.PAGEDATE.key,
3695
 
3696
        prevDate = this.cfg.getProperty(cfgPageDate),
3697
        newDate = DateMath.add(prevDate, DateMath.MONTH, count);
3698
 
3699
        this.cfg.setProperty(cfgPageDate, newDate);
3700
        this.resetRenderers();
3701
        this.changePageEvent.fire(prevDate, newDate);
3702
    },
3703
 
3704
    /**
3705
    * Subtracts the designated number of months from the current calendar month, and sets the current
3706
    * calendar page date to the new month.
3707
    * @method subtractMonths
3708
    * @param {Number} count The number of months to subtract from the current calendar
3709
    */
3710
    subtractMonths : function(count) {
3711
        this.addMonths(-1*count);
3712
    },
3713
 
3714
    /**
3715
    * Adds the designated number of years to the current calendar, and sets the current
3716
    * calendar page date to the new month.
3717
    * @method addYears
3718
    * @param {Number} count The number of years to add to the current calendar
3719
    */
3720
    addYears : function(count) {
3721
        var cfgPageDate = DEF_CFG.PAGEDATE.key,
3722
 
3723
        prevDate = this.cfg.getProperty(cfgPageDate),
3724
        newDate = DateMath.add(prevDate, DateMath.YEAR, count);
3725
 
3726
        this.cfg.setProperty(cfgPageDate, newDate);
3727
        this.resetRenderers();
3728
        this.changePageEvent.fire(prevDate, newDate);
3729
    },
3730
 
3731
    /**
3732
    * Subtcats the designated number of years from the current calendar, and sets the current
3733
    * calendar page date to the new month.
3734
    * @method subtractYears
3735
    * @param {Number} count The number of years to subtract from the current calendar
3736
    */
3737
    subtractYears : function(count) {
3738
        this.addYears(-1*count);
3739
    },
3740
 
3741
    /**
3742
    * Navigates to the next month page in the calendar widget.
3743
    * @method nextMonth
3744
    */
3745
    nextMonth : function() {
3746
        this.addMonths(1);
3747
    },
3748
 
3749
    /**
3750
    * Navigates to the previous month page in the calendar widget.
3751
    * @method previousMonth
3752
    */
3753
    previousMonth : function() {
3754
        this.addMonths(-1);
3755
    },
3756
 
3757
    /**
3758
    * Navigates to the next year in the currently selected month in the calendar widget.
3759
    * @method nextYear
3760
    */
3761
    nextYear : function() {
3762
        this.addYears(1);
3763
    },
3764
 
3765
    /**
3766
    * Navigates to the previous year in the currently selected month in the calendar widget.
3767
    * @method previousYear
3768
    */
3769
    previousYear : function() {
3770
        this.addYears(-1);
3771
    },
3772
 
3773
    // END MONTH NAVIGATION METHODS
3774
 
3775
    // BEGIN SELECTION METHODS
3776
 
3777
    /**
3778
    * Resets the calendar widget to the originally selected month and year, and
3779
    * sets the calendar to the initial selection(s).
3780
    * @method reset
3781
    */
3782
    reset : function() {
3783
        this.cfg.resetProperty(DEF_CFG.SELECTED.key);
3784
        this.cfg.resetProperty(DEF_CFG.PAGEDATE.key);
3785
        this.resetEvent.fire();
3786
    },
3787
 
3788
    /**
3789
    * Clears the selected dates in the current calendar widget and sets the calendar
3790
    * to the current month and year.
3791
    * @method clear
3792
    */
3793
    clear : function() {
3794
        this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
3795
        this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.today.getTime()));
3796
        this.clearEvent.fire();
3797
    },
3798
 
3799
    /**
3800
    * Selects a date or a collection of dates on the current calendar. This method, by default,
3801
    * does not call the render method explicitly. Once selection has completed, render must be
3802
    * called for the changes to be reflected visually.
3803
    *
3804
    * Any dates which are OOB (out of bounds, not selectable) will not be selected and the array of
3805
    * selected dates passed to the selectEvent will not contain OOB dates.
3806
    *
3807
    * If all dates are OOB, the no state change will occur; beforeSelect and select events will not be fired.
3808
    *
3809
    * @method select
3810
    * @param {String/Date/Date[]} date The date string of dates to select in the current calendar. Valid formats are
3811
    *        individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3812
    *        Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3813
    *        This method can also take a JavaScript Date object or an array of Date objects.
3814
    * @return {Date[]}   Array of JavaScript Date objects representing all individual dates that are currently selected.
3815
    */
3816
    select : function(date) {
3817
 
3818
        var aToBeSelected = this._toFieldArray(date),
3819
            validDates = [],
3820
            selected = [],
3821
            cfgSelected = DEF_CFG.SELECTED.key;
3822
 
3823
 
3824
        for (var a=0; a < aToBeSelected.length; ++a) {
3825
            var toSelect = aToBeSelected[a];
3826
 
3827
            if (!this.isDateOOB(this._toDate(toSelect))) {
3828
 
3829
                if (validDates.length === 0) {
3830
                    this.beforeSelectEvent.fire();
3831
                    selected = this.cfg.getProperty(cfgSelected);
3832
                }
3833
                validDates.push(toSelect);
3834
 
3835
                if (this._indexOfSelectedFieldArray(toSelect) == -1) {
3836
                    selected[selected.length] = toSelect;
3837
                }
3838
            }
3839
        }
3840
 
3841
 
3842
        if (validDates.length > 0) {
3843
            if (this.parent) {
3844
                this.parent.cfg.setProperty(cfgSelected, selected);
3845
            } else {
3846
                this.cfg.setProperty(cfgSelected, selected);
3847
            }
3848
            this.selectEvent.fire(validDates);
3849
        }
3850
 
3851
        return this.getSelectedDates();
3852
    },
3853
 
3854
    /**
3855
    * Selects a date on the current calendar by referencing the index of the cell that should be selected.
3856
    * This method is used to easily select a single cell (usually with a mouse click) without having to do
3857
    * a full render. The selected style is applied to the cell directly.
3858
    *
3859
    * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month
3860
    * or out of bounds cells), it will not be selected and in such a case beforeSelect and select events will not be fired.
3861
    *
3862
    * @method selectCell
3863
    * @param {Number} cellIndex The index of the cell to select in the current calendar.
3864
    * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3865
    */
3866
    selectCell : function(cellIndex) {
3867
 
3868
        var cell = this.cells[cellIndex],
3869
            cellDate = this.cellDates[cellIndex],
3870
            dCellDate = this._toDate(cellDate),
3871
            selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3872
 
3873
 
3874
        if (selectable) {
3875
 
3876
            this.beforeSelectEvent.fire();
3877
 
3878
            var cfgSelected = DEF_CFG.SELECTED.key;
3879
            var selected = this.cfg.getProperty(cfgSelected);
3880
 
3881
            var selectDate = cellDate.concat();
3882
 
3883
            if (this._indexOfSelectedFieldArray(selectDate) == -1) {
3884
                selected[selected.length] = selectDate;
3885
            }
3886
            if (this.parent) {
3887
                this.parent.cfg.setProperty(cfgSelected, selected);
3888
            } else {
3889
                this.cfg.setProperty(cfgSelected, selected);
3890
            }
3891
            this.renderCellStyleSelected(dCellDate,cell);
3892
            this.selectEvent.fire([selectDate]);
3893
 
3894
            this.doCellMouseOut.call(cell, null, this);
3895
        }
3896
 
3897
        return this.getSelectedDates();
3898
    },
3899
 
3900
    /**
3901
    * Deselects a date or a collection of dates on the current calendar. This method, by default,
3902
    * does not call the render method explicitly. Once deselection has completed, render must be
3903
    * called for the changes to be reflected visually.
3904
    *
3905
    * The method will not attempt to deselect any dates which are OOB (out of bounds, and hence not selectable)
3906
    * and the array of deselected dates passed to the deselectEvent will not contain any OOB dates.
3907
    *
3908
    * If all dates are OOB, beforeDeselect and deselect events will not be fired.
3909
    *
3910
    * @method deselect
3911
    * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
3912
    *        individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3913
    *        Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3914
    *        This method can also take a JavaScript Date object or an array of Date objects.
3915
    * @return {Date[]}   Array of JavaScript Date objects representing all individual dates that are currently selected.
3916
    */
3917
    deselect : function(date) {
3918
 
3919
        var aToBeDeselected = this._toFieldArray(date),
3920
            validDates = [],
3921
            selected = [],
3922
            cfgSelected = DEF_CFG.SELECTED.key;
3923
 
3924
 
3925
        for (var a=0; a < aToBeDeselected.length; ++a) {
3926
            var toDeselect = aToBeDeselected[a];
3927
 
3928
            if (!this.isDateOOB(this._toDate(toDeselect))) {
3929
 
3930
                if (validDates.length === 0) {
3931
                    this.beforeDeselectEvent.fire();
3932
                    selected = this.cfg.getProperty(cfgSelected);
3933
                }
3934
 
3935
                validDates.push(toDeselect);
3936
 
3937
                var index = this._indexOfSelectedFieldArray(toDeselect);
3938
                if (index != -1) {
3939
                    selected.splice(index,1);
3940
                }
3941
            }
3942
        }
3943
 
3944
 
3945
        if (validDates.length > 0) {
3946
            if (this.parent) {
3947
                this.parent.cfg.setProperty(cfgSelected, selected);
3948
            } else {
3949
                this.cfg.setProperty(cfgSelected, selected);
3950
            }
3951
            this.deselectEvent.fire(validDates);
3952
        }
3953
 
3954
        return this.getSelectedDates();
3955
    },
3956
 
3957
    /**
3958
    * Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
3959
    * This method is used to easily deselect a single cell (usually with a mouse click) without having to do
3960
    * a full render. The selected style is removed from the cell directly.
3961
    *
3962
    * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month
3963
    * or out of bounds cells), the method will not attempt to deselect it and in such a case, beforeDeselect and
3964
    * deselect events will not be fired.
3965
    *
3966
    * @method deselectCell
3967
    * @param {Number} cellIndex The index of the cell to deselect in the current calendar.
3968
    * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3969
    */
3970
    deselectCell : function(cellIndex) {
3971
        var cell = this.cells[cellIndex],
3972
            cellDate = this.cellDates[cellIndex],
3973
            cellDateIndex = this._indexOfSelectedFieldArray(cellDate);
3974
 
3975
        var selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3976
 
3977
        if (selectable) {
3978
 
3979
            this.beforeDeselectEvent.fire();
3980
 
3981
            var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key),
3982
                dCellDate = this._toDate(cellDate),
3983
                selectDate = cellDate.concat();
3984
 
3985
            if (cellDateIndex > -1) {
3986
                if ((this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth() == dCellDate.getMonth() &&
3987
                    this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getFullYear() == dCellDate.getFullYear()) || this.cfg.getProperty(DEF_CFG.OOM_SELECT.key)) {
3988
                    Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
3989
                }
3990
                selected.splice(cellDateIndex, 1);
3991
            }
3992
 
3993
            if (this.parent) {
3994
                this.parent.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
3995
            } else {
3996
                this.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
3997
            }
3998
 
3999
            this.deselectEvent.fire([selectDate]);
4000
        }
4001
 
4002
        return this.getSelectedDates();
4003
    },
4004
 
4005
    /**
4006
    * Deselects all dates on the current calendar.
4007
    * @method deselectAll
4008
    * @return {Date[]}  Array of JavaScript Date objects representing all individual dates that are currently selected.
4009
    *      Assuming that this function executes properly, the return value should be an empty array.
4010
    *      However, the empty array is returned for the sake of being able to check the selection status
4011
    *      of the calendar.
4012
    */
4013
    deselectAll : function() {
4014
        this.beforeDeselectEvent.fire();
4015
 
4016
        var cfgSelected = DEF_CFG.SELECTED.key,
4017
            selected = this.cfg.getProperty(cfgSelected),
4018
            count = selected.length,
4019
            sel = selected.concat();
4020
 
4021
        if (this.parent) {
4022
            this.parent.cfg.setProperty(cfgSelected, []);
4023
        } else {
4024
            this.cfg.setProperty(cfgSelected, []);
4025
        }
4026
 
4027
        if (count > 0) {
4028
            this.deselectEvent.fire(sel);
4029
        }
4030
 
4031
        return this.getSelectedDates();
4032
    },
4033
 
4034
    // END SELECTION METHODS
4035
 
4036
    // BEGIN TYPE CONVERSION METHODS
4037
 
4038
    /**
4039
    * Converts a date (either a JavaScript Date object, or a date string) to the internal data structure
4040
    * used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]].
4041
    * @method _toFieldArray
4042
    * @private
4043
    * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
4044
    *        individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
4045
    *        Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
4046
    *        This method can also take a JavaScript Date object or an array of Date objects.
4047
    * @return {Array[](Number[])} Array of date field arrays
4048
    */
4049
    _toFieldArray : function(date) {
4050
        var returnDate = [];
4051
 
4052
        if (date instanceof Date) {
4053
            returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]];
4054
        } else if (Lang.isString(date)) {
4055
            returnDate = this._parseDates(date);
4056
        } else if (Lang.isArray(date)) {
4057
            for (var i=0;i<date.length;++i) {
4058
                var d = date[i];
4059
                returnDate[returnDate.length] = [d.getFullYear(),d.getMonth()+1,d.getDate()];
4060
            }
4061
        }
4062
 
4063
        return returnDate;
4064
    },
4065
 
4066
    /**
4067
    * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object. The date field array
4068
    * is the format in which dates are as provided as arguments to selectEvent and deselectEvent listeners.
4069
    *
4070
    * @method toDate
4071
    * @param {Number[]} dateFieldArray The date field array to convert to a JavaScript Date.
4072
    * @return {Date} JavaScript Date object representing the date field array.
4073
    */
4074
    toDate : function(dateFieldArray) {
4075
        return this._toDate(dateFieldArray);
4076
    },
4077
 
4078
    /**
4079
    * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
4080
    * @method _toDate
4081
    * @private
4082
    * @deprecated Made public, toDate
4083
    * @param {Number[]}  dateFieldArray The date field array to convert to a JavaScript Date.
4084
    * @return {Date} JavaScript Date object representing the date field array
4085
    */
4086
    _toDate : function(dateFieldArray) {
4087
        if (dateFieldArray instanceof Date) {
4088
            return dateFieldArray;
4089
        } else {
4090
            return DateMath.getDate(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]);
4091
        }
4092
    },
4093
 
4094
    // END TYPE CONVERSION METHODS
4095
 
4096
    // BEGIN UTILITY METHODS
4097
 
4098
    /**
4099
    * Determines if 2 field arrays are equal.
4100
    * @method _fieldArraysAreEqual
4101
    * @private
4102
    * @param {Number[]} array1 The first date field array to compare
4103
    * @param {Number[]} array2 The first date field array to compare
4104
    * @return {Boolean} The boolean that represents the equality of the two arrays
4105
    */
4106
    _fieldArraysAreEqual : function(array1, array2) {
4107
        var match = false;
4108
 
4109
        if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2]) {
4110
            match=true;
4111
        }
4112
 
4113
        return match;
4114
    },
4115
 
4116
    /**
4117
    * Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates.
4118
    * @method _indexOfSelectedFieldArray
4119
    * @private
4120
    * @param {Number[]}  find The date field array to search for
4121
    * @return {Number}   The index of the date field array within the collection of selected dates.
4122
    *        -1 will be returned if the date is not found.
4123
    */
4124
    _indexOfSelectedFieldArray : function(find) {
4125
        var selected = -1,
4126
            seldates = this.cfg.getProperty(DEF_CFG.SELECTED.key);
4127
 
4128
        for (var s=0;s<seldates.length;++s) {
4129
            var sArray = seldates[s];
4130
            if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2]) {
4131
                selected = s;
4132
                break;
4133
            }
4134
        }
4135
 
4136
        return selected;
4137
    },
4138
 
4139
    /**
4140
    * Determines whether a given date is OOM (out of month).
4141
    * @method isDateOOM
4142
    * @param {Date} date The JavaScript Date object for which to check the OOM status
4143
    * @return {Boolean} true if the date is OOM
4144
    */
4145
    isDateOOM : function(date) {
4146
        return (date.getMonth() != this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth());
4147
    },
4148
 
4149
    /**
4150
    * Determines whether a given date is OOB (out of bounds - less than the mindate or more than the maxdate).
4151
    *
4152
    * @method isDateOOB
4153
    * @param {Date} date The JavaScript Date object for which to check the OOB status
4154
    * @return {Boolean} true if the date is OOB
4155
    */
4156
    isDateOOB : function(date) {
4157
        var minDate = this.cfg.getProperty(DEF_CFG.MINDATE.key),
4158
            maxDate = this.cfg.getProperty(DEF_CFG.MAXDATE.key),
4159
            dm = DateMath;
4160
 
4161
        if (minDate) {
4162
            minDate = dm.clearTime(minDate);
4163
        }
4164
        if (maxDate) {
4165
            maxDate = dm.clearTime(maxDate);
4166
        }
4167
 
4168
        var clearedDate = new Date(date.getTime());
4169
        clearedDate = dm.clearTime(clearedDate);
4170
 
4171
        return ((minDate && clearedDate.getTime() < minDate.getTime()) || (maxDate && clearedDate.getTime() > maxDate.getTime()));
4172
    },
4173
 
4174
    /**
4175
     * Parses a pagedate configuration property value. The value can either be specified as a string of form "mm/yyyy" or a Date object
4176
     * 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
4177
     * @method _parsePageDate
4178
     * @private
4179
     * @param {Date|String} date Pagedate value which needs to be parsed
4180
     * @return {Date} The Date object representing the pagedate
4181
     */
4182
    _parsePageDate : function(date) {
4183
        var parsedDate;
4184
 
4185
        if (date) {
4186
            if (date instanceof Date) {
4187
                parsedDate = DateMath.findMonthStart(date);
4188
            } else {
4189
                var month, year, aMonthYear;
4190
                aMonthYear = date.split(this.cfg.getProperty(DEF_CFG.DATE_FIELD_DELIMITER.key));
4191
                month = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_MONTH_POSITION.key)-1], 10)-1;
4192
                year = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_YEAR_POSITION.key)-1], 10) - this.Locale.YEAR_OFFSET;
4193
 
4194
                parsedDate = DateMath.getDate(year, month, 1);
4195
            }
4196
        } else {
4197
            parsedDate = DateMath.getDate(this.today.getFullYear(), this.today.getMonth(), 1);
4198
        }
4199
        return parsedDate;
4200
    },
4201
 
4202
    // END UTILITY METHODS
4203
 
4204
    // BEGIN EVENT HANDLERS
4205
 
4206
    /**
4207
    * Event executed before a date is selected in the calendar widget.
4208
    * @deprecated Event handlers for this event should be susbcribed to beforeSelectEvent.
4209
    */
4210
    onBeforeSelect : function() {
4211
        if (this.cfg.getProperty(DEF_CFG.MULTI_SELECT.key) === false) {
4212
            if (this.parent) {
4213
                this.parent.callChildFunction("clearAllBodyCellStyles", this.Style.CSS_CELL_SELECTED);
4214
                this.parent.deselectAll();
4215
            } else {
4216
                this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED);
4217
                this.deselectAll();
4218
            }
4219
        }
4220
    },
4221
 
4222
    /**
4223
    * Event executed when a date is selected in the calendar widget.
4224
    * @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] ]
4225
    * @deprecated Event handlers for this event should be susbcribed to selectEvent.
4226
    */
4227
    onSelect : function(selected) { },
4228
 
4229
    /**
4230
    * Event executed before a date is deselected in the calendar widget.
4231
    * @deprecated Event handlers for this event should be susbcribed to beforeDeselectEvent.
4232
    */
4233
    onBeforeDeselect : function() { },
4234
 
4235
    /**
4236
    * Event executed when a date is deselected in the calendar widget.
4237
    * @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] ]
4238
    * @deprecated Event handlers for this event should be susbcribed to deselectEvent.
4239
    */
4240
    onDeselect : function(deselected) { },
4241
 
4242
    /**
4243
    * Event executed when the user navigates to a different calendar page.
4244
    * @deprecated Event handlers for this event should be susbcribed to changePageEvent.
4245
    */
4246
    onChangePage : function() {
4247
        this.render();
4248
    },
4249
 
4250
    /**
4251
    * Event executed when the calendar widget is rendered.
4252
    * @deprecated Event handlers for this event should be susbcribed to renderEvent.
4253
    */
4254
    onRender : function() { },
4255
 
4256
    /**
4257
    * Event executed when the calendar widget is reset to its original state.
4258
    * @deprecated Event handlers for this event should be susbcribed to resetEvemt.
4259
    */
4260
    onReset : function() { this.render(); },
4261
 
4262
    /**
4263
    * Event executed when the calendar widget is completely cleared to the current month with no selections.
4264
    * @deprecated Event handlers for this event should be susbcribed to clearEvent.
4265
    */
4266
    onClear : function() { this.render(); },
4267
 
4268
    /**
4269
    * Validates the calendar widget. This method has no default implementation
4270
    * and must be extended by subclassing the widget.
4271
    * @return Should return true if the widget validates, and false if
4272
    * it doesn't.
4273
    * @type Boolean
4274
    */
4275
    validate : function() { return true; },
4276
 
4277
    // END EVENT HANDLERS
4278
 
4279
    // BEGIN DATE PARSE METHODS
4280
 
4281
    /**
4282
    * Converts a date string to a date field array
4283
    * @private
4284
    * @param {String} sDate   Date string. Valid formats are mm/dd and mm/dd/yyyy.
4285
    * @return    A date field array representing the string passed to the method
4286
    * @type Array[](Number[])
4287
    */
4288
    _parseDate : function(sDate) {
4289
        var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER),
4290
            rArray;
4291
 
4292
        if (aDate.length == 2) {
4293
            rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
4294
            rArray.type = Calendar.MONTH_DAY;
4295
        } else {
4296
            rArray = [aDate[this.Locale.MDY_YEAR_POSITION-1] - this.Locale.YEAR_OFFSET, aDate[this.Locale.MDY_MONTH_POSITION-1],aDate[this.Locale.MDY_DAY_POSITION-1]];
4297
            rArray.type = Calendar.DATE;
4298
        }
4299
 
4300
        for (var i=0;i<rArray.length;i++) {
4301
            rArray[i] = parseInt(rArray[i], 10);
4302
        }
4303
 
4304
        return rArray;
4305
    },
4306
 
4307
    /**
4308
    * Converts a multi or single-date string to an array of date field arrays
4309
    * @private
4310
    * @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
4311
    * @return       An array of date field arrays
4312
    * @type Array[](Number[])
4313
    */
4314
    _parseDates : function(sDates) {
4315
        var aReturn = [],
4316
            aDates = sDates.split(this.Locale.DATE_DELIMITER);
4317
 
4318
        for (var d=0;d<aDates.length;++d) {
4319
            var sDate = aDates[d];
4320
 
4321
            if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) {
4322
                // This is a range
4323
                var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER),
4324
                    dateStart = this._parseDate(aRange[0]),
4325
                    dateEnd = this._parseDate(aRange[1]),
4326
                    fullRange = this._parseRange(dateStart, dateEnd);
4327
 
4328
                aReturn = aReturn.concat(fullRange);
4329
            } else {
4330
                // This is not a range
4331
                var aDate = this._parseDate(sDate);
4332
                aReturn.push(aDate);
4333
            }
4334
        }
4335
        return aReturn;
4336
    },
4337
 
4338
    /**
4339
    * Converts a date range to the full list of included dates
4340
    * @private
4341
    * @param {Number[]} startDate Date field array representing the first date in the range
4342
    * @param {Number[]} endDate  Date field array representing the last date in the range
4343
    * @return       An array of date field arrays
4344
    * @type Array[](Number[])
4345
    */
4346
    _parseRange : function(startDate, endDate) {
4347
        var dCurrent = DateMath.add(DateMath.getDate(startDate[0],startDate[1]-1,startDate[2]),DateMath.DAY,1),
4348
            dEnd     = DateMath.getDate(endDate[0],  endDate[1]-1,  endDate[2]),
4349
            results = [];
4350
 
4351
        results.push(startDate);
4352
        while (dCurrent.getTime() <= dEnd.getTime()) {
4353
            results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
4354
            dCurrent = DateMath.add(dCurrent,DateMath.DAY,1);
4355
        }
4356
        return results;
4357
    },
4358
 
4359
    // END DATE PARSE METHODS
4360
 
4361
    // BEGIN RENDERER METHODS
4362
 
4363
    /**
4364
    * Resets the render stack of the current calendar to its original pre-render value.
4365
    */
4366
    resetRenderers : function() {
4367
        this.renderStack = this._renderStack.concat();
4368
    },
4369
 
4370
    /**
4371
     * Removes all custom renderers added to the Calendar through the addRenderer, addMonthRenderer and
4372
     * addWeekdayRenderer methods. Calendar's render method needs to be called after removing renderers
4373
     * to re-render the Calendar without custom renderers applied.
4374
     */
4375
    removeRenderers : function() {
4376
        this._renderStack = [];
4377
        this.renderStack = [];
4378
    },
4379
 
4380
    /**
4381
    * Clears the inner HTML, CSS class and style information from the specified cell.
4382
    * @method clearElement
4383
    * @param {HTMLTableCellElement} cell The cell to clear
4384
    */
4385
    clearElement : function(cell) {
4386
        cell.innerHTML = "&#160;";
4387
        cell.className="";
4388
    },
4389
 
4390
    /**
4391
    * Adds a renderer to the render stack. The function reference passed to this method will be executed
4392
    * when a date cell matches the conditions specified in the date string for this renderer.
4393
    *
4394
    * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should
4395
    * escape markup used to set the cell contents, if coming from an external source.<p>
4396
    * @method addRenderer
4397
    * @param {String} sDates  A date string to associate with the specified renderer. Valid formats
4398
    *         include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
4399
    * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4400
    */
4401
    addRenderer : function(sDates, fnRender) {
4402
        var aDates = this._parseDates(sDates);
4403
        for (var i=0;i<aDates.length;++i) {
4404
            var aDate = aDates[i];
4405
 
4406
            if (aDate.length == 2) { // this is either a range or a month/day combo
4407
                if (aDate[0] instanceof Array) { // this is a range
4408
                    this._addRenderer(Calendar.RANGE,aDate,fnRender);
4409
                } else { // this is a month/day combo
4410
                    this._addRenderer(Calendar.MONTH_DAY,aDate,fnRender);
4411
                }
4412
            } else if (aDate.length == 3) {
4413
                this._addRenderer(Calendar.DATE,aDate,fnRender);
4414
            }
4415
        }
4416
    },
4417
 
4418
    /**
4419
    * The private method used for adding cell renderers to the local render stack.
4420
    * This method is called by other methods that set the renderer type prior to the method call.
4421
    * @method _addRenderer
4422
    * @private
4423
    * @param {String} type  The type string that indicates the type of date renderer being added.
4424
    *         Values are YAHOO.widget.Calendar.DATE, YAHOO.widget.Calendar.MONTH_DAY, YAHOO.widget.Calendar.WEEKDAY,
4425
    *         YAHOO.widget.Calendar.RANGE, YAHOO.widget.Calendar.MONTH
4426
    * @param {Array}  aDates  An array of dates used to construct the renderer. The format varies based
4427
    *         on the renderer type
4428
    * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4429
    */
4430
    _addRenderer : function(type, aDates, fnRender) {
4431
        var add = [type,aDates,fnRender];
4432
        this.renderStack.unshift(add);
4433
        this._renderStack = this.renderStack.concat();
4434
    },
4435
 
4436
    /**
4437
    * Adds a month renderer to the render stack. The function reference passed to this method will be executed
4438
    * when a date cell matches the month passed to this method
4439
    *
4440
    * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should
4441
    * escape markup used to set the cell contents, if coming from an external source.<p>
4442
    * @method addMonthRenderer
4443
    * @param {Number} month  The month (1-12) to associate with this renderer
4444
    * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4445
    */
4446
    addMonthRenderer : function(month, fnRender) {
4447
        this._addRenderer(Calendar.MONTH,[month],fnRender);
4448
    },
4449
 
4450
    /**
4451
    * Adds a weekday renderer to the render stack. The function reference passed to this method will be executed
4452
    * when a date cell matches the weekday passed to this method.
4453
    *
4454
    * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should
4455
    * escape HTML used to set the cell contents, if coming from an external source.<p>
4456
    *
4457
    * @method addWeekdayRenderer
4458
    * @param {Number} weekday  The weekday (Sunday = 1, Monday = 2 ... Saturday = 7) to associate with this renderer
4459
    * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4460
    */
4461
    addWeekdayRenderer : function(weekday, fnRender) {
4462
        this._addRenderer(Calendar.WEEKDAY,[weekday],fnRender);
4463
    },
4464
 
4465
    // END RENDERER METHODS
4466
 
4467
    // BEGIN CSS METHODS
4468
 
4469
    /**
4470
    * Removes all styles from all body cells in the current calendar table.
4471
    * @method clearAllBodyCellStyles
4472
    * @param {style} style The CSS class name to remove from all calendar body cells
4473
    */
4474
    clearAllBodyCellStyles : function(style) {
4475
        for (var c=0;c<this.cells.length;++c) {
4476
            Dom.removeClass(this.cells[c],style);
4477
        }
4478
    },
4479
 
4480
    // END CSS METHODS
4481
 
4482
    // BEGIN GETTER/SETTER METHODS
4483
    /**
4484
    * Sets the calendar's month explicitly
4485
    * @method setMonth
4486
    * @param {Number} month  The numeric month, from 0 (January) to 11 (December)
4487
    */
4488
    setMonth : function(month) {
4489
        var cfgPageDate = DEF_CFG.PAGEDATE.key,
4490
            current = this.cfg.getProperty(cfgPageDate);
4491
        current.setMonth(parseInt(month, 10));
4492
        this.cfg.setProperty(cfgPageDate, current);
4493
    },
4494
 
4495
    /**
4496
    * Sets the calendar's year explicitly.
4497
    * @method setYear
4498
    * @param {Number} year  The numeric 4-digit year
4499
    */
4500
    setYear : function(year) {
4501
        var cfgPageDate = DEF_CFG.PAGEDATE.key,
4502
            current = this.cfg.getProperty(cfgPageDate);
4503
 
4504
        current.setFullYear(parseInt(year, 10) - this.Locale.YEAR_OFFSET);
4505
        this.cfg.setProperty(cfgPageDate, current);
4506
    },
4507
 
4508
    /**
4509
    * Gets the list of currently selected dates from the calendar.
4510
    * @method getSelectedDates
4511
    * @return {Date[]} An array of currently selected JavaScript Date objects.
4512
    */
4513
    getSelectedDates : function() {
4514
        var returnDates = [],
4515
            selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
4516
 
4517
        for (var d=0;d<selected.length;++d) {
4518
            var dateArray = selected[d];
4519
 
4520
            var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
4521
            returnDates.push(date);
4522
        }
4523
 
4524
        returnDates.sort( function(a,b) { return a-b; } );
4525
        return returnDates;
4526
    },
4527
 
4528
    /// END GETTER/SETTER METHODS ///
4529
 
4530
    /**
4531
    * Hides the Calendar's outer container from view.
4532
    * @method hide
4533
    */
4534
    hide : function() {
4535
        if (this.beforeHideEvent.fire()) {
4536
            this.oDomContainer.style.display = "none";
4537
            this.hideEvent.fire();
4538
        }
4539
    },
4540
 
4541
    /**
4542
    * Shows the Calendar's outer container.
4543
    * @method show
4544
    */
4545
    show : function() {
4546
        if (this.beforeShowEvent.fire()) {
4547
            this.oDomContainer.style.display = "block";
4548
            this.showEvent.fire();
4549
        }
4550
    },
4551
 
4552
    /**
4553
    * Returns a string representing the current browser.
4554
    * @deprecated As of 2.3.0, environment information is available in YAHOO.env.ua
4555
    * @see YAHOO.env.ua
4556
    * @property browser
4557
    * @type String
4558
    */
4559
    browser : (function() {
4560
                var ua = navigator.userAgent.toLowerCase();
4561
                      if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof)
4562
                         return 'opera';
4563
                      } else if (ua.indexOf('msie 7')!=-1) { // IE7
4564
                         return 'ie7';
4565
                      } else if (ua.indexOf('msie') !=-1) { // IE
4566
                         return 'ie';
4567
                      } else if (ua.indexOf('safari')!=-1) { // Safari (check before Gecko because it includes "like Gecko")
4568
                         return 'safari';
4569
                      } else if (ua.indexOf('gecko') != -1) { // Gecko
4570
                         return 'gecko';
4571
                      } else {
4572
                         return false;
4573
                      }
4574
                })(),
4575
    /**
4576
    * Returns a string representation of the object.
4577
    * @method toString
4578
    * @return {String} A string representation of the Calendar object.
4579
    */
4580
    toString : function() {
4581
        return "Calendar " + this.id;
4582
    },
4583
 
4584
    /**
4585
     * Destroys the Calendar instance. The method will remove references
4586
     * to HTML elements, remove any event listeners added by the Calendar,
4587
     * and destroy the Config and CalendarNavigator instances it has created.
4588
     *
4589
     * @method destroy
4590
     */
4591
    destroy : function() {
4592
 
4593
        if (this.beforeDestroyEvent.fire()) {
4594
            var cal = this;
4595
 
4596
            // Child objects
4597
            if (cal.navigator) {
4598
                cal.navigator.destroy();
4599
            }
4600
 
4601
            if (cal.cfg) {
4602
                cal.cfg.destroy();
4603
            }
4604
 
4605
            // DOM event listeners
4606
            Event.purgeElement(cal.oDomContainer, true);
4607
 
4608
            // Generated markup/DOM - Not removing the container DIV since we didn't create it.
4609
            Dom.removeClass(cal.oDomContainer, cal.Style.CSS_WITH_TITLE);
4610
            Dom.removeClass(cal.oDomContainer, cal.Style.CSS_CONTAINER);
4611
            Dom.removeClass(cal.oDomContainer, cal.Style.CSS_SINGLE);
4612
            cal.oDomContainer.innerHTML = "";
4613
 
4614
            // JS-to-DOM references
4615
            cal.oDomContainer = null;
4616
            cal.cells = null;
4617
 
4618
            this.destroyEvent.fire();
4619
        }
4620
    }
4621
};
4622
 
4623
YAHOO.widget.Calendar = Calendar;
4624
 
4625
/**
4626
* @namespace YAHOO.widget
4627
* @class Calendar_Core
4628
* @extends YAHOO.widget.Calendar
4629
* @deprecated The old Calendar_Core class is no longer necessary.
4630
*/
4631
YAHOO.widget.Calendar_Core = YAHOO.widget.Calendar;
4632
 
4633
YAHOO.widget.Cal_Core = YAHOO.widget.Calendar;
4634
 
4635
})();
4636
(function() {
4637
 
4638
    var Dom = YAHOO.util.Dom,
4639
        DateMath = YAHOO.widget.DateMath,
4640
        Event = YAHOO.util.Event,
4641
        Lang = YAHOO.lang,
4642
        Calendar = YAHOO.widget.Calendar;
4643
 
4644
/**
4645
* YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar. This class facilitates
4646
* the ability to have multi-page calendar views that share a single dataset and are
4647
* dependent on each other.
4648
*
4649
* The calendar group instance will refer to each of its elements using a 0-based index.
4650
* For example, to construct the placeholder for a calendar group widget with id "cal1" and
4651
* containerId of "cal1Container", the markup would be as follows:
4652
*   <xmp>
4653
*       <div id="cal1Container_0"></div>
4654
*       <div id="cal1Container_1"></div>
4655
*   </xmp>
4656
* The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers.
4657
*
4658
* <p>
4659
* <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
4660
* The CalendarGroup can be constructed by simply providing a container ID string,
4661
* or a reference to a container DIV HTMLElement (the element needs to exist
4662
* in the document).
4663
*
4664
* E.g.:
4665
*   <xmp>
4666
*       var c = new YAHOO.widget.CalendarGroup("calContainer", configOptions);
4667
*   </xmp>
4668
* or:
4669
*   <xmp>
4670
*       var containerDiv = YAHOO.util.Dom.get("calContainer");
4671
*       var c = new YAHOO.widget.CalendarGroup(containerDiv, configOptions);
4672
*   </xmp>
4673
* </p>
4674
* <p>
4675
* If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
4676
* For example if an ID is not provided, and the container's ID is "calContainer", the CalendarGroup's ID will be set to "calContainer_t".
4677
* </p>
4678
*
4679
* @namespace YAHOO.widget
4680
* @class CalendarGroup
4681
* @constructor
4682
* @param {String} id optional The id of the table element that will represent the CalendarGroup widget. As of 2.4.0, this argument is optional.
4683
* @param {String | HTMLElement} container The id of the container div element that will wrap the CalendarGroup table, or a reference to a DIV element which exists in the document.
4684
* @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
4685
*/
4686
function CalendarGroup(id, containerId, config) {
4687
    if (arguments.length > 0) {
4688
        this.init.apply(this, arguments);
4689
    }
4690
}
4691
 
4692
/**
4693
* The set of default Config property keys and values for the CalendarGroup.
4694
*
4695
* <p>
4696
* NOTE: This property is made public in order to allow users to change
4697
* the default values of configuration properties. Users should not
4698
* modify the key string, unless they are overriding the Calendar implementation
4699
* </p>
4700
*
4701
* @property YAHOO.widget.CalendarGroup.DEFAULT_CONFIG
4702
* @static
4703
* @type Object An object with key/value pairs, the key being the
4704
* uppercase configuration property name and the value being an objec
4705
* literal with a key string property, and a value property, specifying the
4706
* default value of the property
4707
*/
4708
 
4709
/**
4710
* The set of default Config property keys and values for the CalendarGroup
4711
* @property YAHOO.widget.CalendarGroup._DEFAULT_CONFIG
4712
* @deprecated Made public. See the public DEFAULT_CONFIG property for details
4713
* @private
4714
* @static
4715
* @type Object
4716
*/
4717
CalendarGroup.DEFAULT_CONFIG = CalendarGroup._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG;
4718
CalendarGroup.DEFAULT_CONFIG.PAGES = {key:"pages", value:2};
4719
 
4720
var DEF_CFG = CalendarGroup.DEFAULT_CONFIG;
4721
 
4722
CalendarGroup.prototype = {
4723
 
4724
    /**
4725
    * Initializes the calendar group. All subclasses must call this method in order for the
4726
    * group to be initialized properly.
4727
    * @method init
4728
    * @param {String} id optional The id of the table element that will represent the CalendarGroup widget. As of 2.4.0, this argument is optional.
4729
    * @param {String | HTMLElement} container The id of the container div element that will wrap the CalendarGroup table, or a reference to a DIV element which exists in the document.
4730
    * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
4731
    */
4732
    init : function(id, container, config) {
4733
 
4734
        // Normalize 2.4.0, pre 2.4.0 args
4735
        var nArgs = this._parseArgs(arguments);
4736
 
4737
        id = nArgs.id;
4738
        container = nArgs.container;
4739
        config = nArgs.config;
4740
 
4741
        this.oDomContainer = Dom.get(container);
4742
 
4743
        if (!this.oDomContainer.id) {
4744
            this.oDomContainer.id = Dom.generateId();
4745
        }
4746
        if (!id) {
4747
            id = this.oDomContainer.id + "_t";
4748
        }
4749
 
4750
        /**
4751
        * The unique id associated with the CalendarGroup
4752
        * @property id
4753
        * @type String
4754
        */
4755
        this.id = id;
4756
 
4757
        /**
4758
        * The unique id associated with the CalendarGroup container
4759
        * @property containerId
4760
        * @type String
4761
        */
4762
        this.containerId = this.oDomContainer.id;
4763
 
4764
        this.initEvents();
4765
        this.initStyles();
4766
 
4767
        /**
4768
        * The collection of Calendar pages contained within the CalendarGroup
4769
        * @property pages
4770
        * @type YAHOO.widget.Calendar[]
4771
        */
4772
        this.pages = [];
4773
 
4774
        Dom.addClass(this.oDomContainer, CalendarGroup.CSS_CONTAINER);
4775
        Dom.addClass(this.oDomContainer, CalendarGroup.CSS_MULTI_UP);
4776
 
4777
        /**
4778
        * The Config object used to hold the configuration variables for the CalendarGroup
4779
        * @property cfg
4780
        * @type YAHOO.util.Config
4781
        */
4782
        this.cfg = new YAHOO.util.Config(this);
4783
 
4784
        /**
4785
        * The local object which contains the CalendarGroup's options
4786
        * @property Options
4787
        * @type Object
4788
        */
4789
        this.Options = {};
4790
 
4791
        /**
4792
        * The local object which contains the CalendarGroup's locale settings
4793
        * @property Locale
4794
        * @type Object
4795
        */
4796
        this.Locale = {};
4797
 
4798
        this.setupConfig();
4799
 
4800
        if (config) {
4801
            this.cfg.applyConfig(config, true);
4802
        }
4803
 
4804
        this.cfg.fireQueue();
4805
 
4806
    },
4807
 
4808
    setupConfig : function() {
4809
 
4810
        var cfg = this.cfg;
4811
 
4812
        /**
4813
        * The number of pages to include in the CalendarGroup. This value can only be set once, in the CalendarGroup's constructor arguments.
4814
        * @config pages
4815
        * @type Number
4816
        * @default 2
4817
        */
4818
        cfg.addProperty(DEF_CFG.PAGES.key, { value:DEF_CFG.PAGES.value, validator:cfg.checkNumber, handler:this.configPages } );
4819
 
4820
        /**
4821
        * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to
4822
        * be used when displaying or parsing dates.  NOTE: All JS Date objects returned by methods, or expected as input by
4823
        * methods will always represent the Gregorian year, in order to maintain date/month/week values.
4824
        *
4825
        * @config year_offset
4826
        * @type Number
4827
        * @default 0
4828
        */
4829
        cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, handler: this.delegateConfig, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, suppressEvent:true } );
4830
 
4831
        /**
4832
        * The date to use to represent "Today".
4833
        *
4834
        * @config today
4835
        * @type Date
4836
        * @default Today's date
4837
        */
4838
        cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler: this.configToday, suppressEvent:false } );
4839
 
4840
        /**
4841
        * The month/year representing the current visible Calendar date (mm/yyyy)
4842
        * @config pagedate
4843
        * @type String | Date
4844
        * @default Today's date
4845
        */
4846
        cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } );
4847
 
4848
        /**
4849
        * The date or range of dates representing the current Calendar selection
4850
        *
4851
        * @config selected
4852
        * @type String
4853
        * @default []
4854
        */
4855
        cfg.addProperty(DEF_CFG.SELECTED.key, { value:[], handler:this.configSelected } );
4856
 
4857
        /**
4858
        * The title to display above the CalendarGroup's month header. The title is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4859
        * @config title
4860
        * @type HTML
4861
        * @default ""
4862
        */
4863
        cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
4864
 
4865
        /**
4866
        * Whether or not a close button should be displayed for this CalendarGroup
4867
        * @config close
4868
        * @type Boolean
4869
        * @default false
4870
        */
4871
        cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
4872
 
4873
        /**
4874
        * 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.
4875
        * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be
4876
        * enabled if required.
4877
        *
4878
        * @config iframe
4879
        * @type Boolean
4880
        * @default true for IE6 and below, false for all other browsers
4881
        */
4882
        cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
4883
 
4884
        /**
4885
        * The minimum selectable date in the current Calendar (mm/dd/yyyy)
4886
        * @config mindate
4887
        * @type String | Date
4888
        * @default null
4889
        */
4890
        cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.delegateConfig } );
4891
 
4892
        /**
4893
        * The maximum selectable date in the current Calendar (mm/dd/yyyy)
4894
        * @config maxdate
4895
        * @type String | Date
4896
        * @default null
4897
        */
4898
        cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.delegateConfig  } );
4899
 
4900
        /**
4901
        * True if the Calendar should allow multiple selections. False by default.
4902
        * @config MULTI_SELECT
4903
        * @type Boolean
4904
        * @default false
4905
        */
4906
        cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4907
 
4908
        /**
4909
        * True if the Calendar should allow selection of out-of-month dates. False by default.
4910
        * @config OOM_SELECT
4911
        * @type Boolean
4912
        * @default false
4913
        */
4914
        cfg.addProperty(DEF_CFG.OOM_SELECT.key, { value:DEF_CFG.OOM_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4915
 
4916
        /**
4917
        * The weekday the week begins on. Default is 0 (Sunday).
4918
        * @config START_WEEKDAY
4919
        * @type number
4920
        * @default 0
4921
        */
4922
        cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.delegateConfig, validator:cfg.checkNumber  } );
4923
 
4924
        /**
4925
        * True if the Calendar should show weekday labels. True by default.
4926
        * @config SHOW_WEEKDAYS
4927
        * @type Boolean
4928
        * @default true
4929
        */
4930
        cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4931
 
4932
        /**
4933
        * True if the Calendar should show week row headers. False by default.
4934
        * @config SHOW_WEEK_HEADER
4935
        * @type Boolean
4936
        * @default false
4937
        */
4938
        cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key,{ value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4939
 
4940
        /**
4941
        * True if the Calendar should show week row footers. False by default.
4942
        * @config SHOW_WEEK_FOOTER
4943
        * @type Boolean
4944
        * @default false
4945
        */
4946
        cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4947
 
4948
        /**
4949
        * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
4950
        * @config HIDE_BLANK_WEEKS
4951
        * @type Boolean
4952
        * @default false
4953
        */
4954
        cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key,{ value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4955
 
4956
        /**
4957
        * The image URL that should be used for the left navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4958
        * @config NAV_ARROW_LEFT
4959
        * @type String
4960
        * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
4961
        * @default null
4962
        */
4963
        cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.delegateConfig } );
4964
 
4965
        /**
4966
        * The image URL that should be used for the right navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4967
        * @config NAV_ARROW_RIGHT
4968
        * @type String
4969
        * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
4970
        * @default null
4971
        */
4972
        cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.delegateConfig } );
4973
 
4974
        // Locale properties
4975
 
4976
        /**
4977
        * The short month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4978
        * @config MONTHS_SHORT
4979
        * @type HTML[]
4980
        * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
4981
        */
4982
        cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.delegateConfig } );
4983
 
4984
        /**
4985
        * The long month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4986
        * @config MONTHS_LONG
4987
        * @type HTML[]
4988
        * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
4989
        */
4990
        cfg.addProperty(DEF_CFG.MONTHS_LONG.key,  { value:DEF_CFG.MONTHS_LONG.value, handler:this.delegateConfig } );
4991
 
4992
        /**
4993
        * The 1-character weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4994
        * @config WEEKDAYS_1CHAR
4995
        * @type HTML[]
4996
        * @default ["S", "M", "T", "W", "T", "F", "S"]
4997
        */
4998
        cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.delegateConfig } );
4999
 
5000
        /**
5001
        * The short weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
5002
        * @config WEEKDAYS_SHORT
5003
        * @type HTML[]
5004
        * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
5005
        */
5006
        cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.delegateConfig } );
5007
 
5008
        /**
5009
        * The medium weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
5010
        * @config WEEKDAYS_MEDIUM
5011
        * @type HTML[]
5012
        * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
5013
        */
5014
        cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.delegateConfig } );
5015
 
5016
        /**
5017
        * The long weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
5018
        * @config WEEKDAYS_LONG
5019
        * @type HTML[]
5020
        * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
5021
        */
5022
        cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.delegateConfig } );
5023
 
5024
        /**
5025
        * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
5026
        * @config LOCALE_MONTHS
5027
        * @type String
5028
        * @default "long"
5029
        */
5030
        cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.delegateConfig } );
5031
 
5032
        /**
5033
        * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
5034
        * @config LOCALE_WEEKDAYS
5035
        * @type String
5036
        * @default "short"
5037
        */
5038
        cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.delegateConfig } );
5039
 
5040
        /**
5041
        * The value used to delimit individual dates in a date string passed to various Calendar functions.
5042
        * @config DATE_DELIMITER
5043
        * @type String
5044
        * @default ","
5045
        */
5046
        cfg.addProperty(DEF_CFG.DATE_DELIMITER.key,  { value:DEF_CFG.DATE_DELIMITER.value, handler:this.delegateConfig } );
5047
 
5048
        /**
5049
        * The value used to delimit date fields in a date string passed to various Calendar functions.
5050
        * @config DATE_FIELD_DELIMITER
5051
        * @type String
5052
        * @default "/"
5053
        */
5054
        cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key,{ value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.delegateConfig } );
5055
 
5056
        /**
5057
        * The value used to delimit date ranges in a date string passed to various Calendar functions.
5058
        * @config DATE_RANGE_DELIMITER
5059
        * @type String
5060
        * @default "-"
5061
        */
5062
        cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key,{ value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.delegateConfig } );
5063
 
5064
        /**
5065
        * The position of the month in a month/year date string
5066
        * @config MY_MONTH_POSITION
5067
        * @type Number
5068
        * @default 1
5069
        */
5070
        cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5071
 
5072
        /**
5073
        * The position of the year in a month/year date string
5074
        * @config MY_YEAR_POSITION
5075
        * @type Number
5076
        * @default 2
5077
        */
5078
        cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5079
 
5080
        /**
5081
        * The position of the month in a month/day date string
5082
        * @config MD_MONTH_POSITION
5083
        * @type Number
5084
        * @default 1
5085
        */
5086
        cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5087
 
5088
        /**
5089
        * The position of the day in a month/year date string
5090
        * @config MD_DAY_POSITION
5091
        * @type Number
5092
        * @default 2
5093
        */
5094
        cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key,  { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5095
 
5096
        /**
5097
        * The position of the month in a month/day/year date string
5098
        * @config MDY_MONTH_POSITION
5099
        * @type Number
5100
        * @default 1
5101
        */
5102
        cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5103
 
5104
        /**
5105
        * The position of the day in a month/day/year date string
5106
        * @config MDY_DAY_POSITION
5107
        * @type Number
5108
        * @default 2
5109
        */
5110
        cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5111
 
5112
        /**
5113
        * The position of the year in a month/day/year date string
5114
        * @config MDY_YEAR_POSITION
5115
        * @type Number
5116
        * @default 3
5117
        */
5118
        cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5119
 
5120
        /**
5121
        * The position of the month in the month year label string used as the Calendar header
5122
        * @config MY_LABEL_MONTH_POSITION
5123
        * @type Number
5124
        * @default 1
5125
        */
5126
        cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5127
 
5128
        /**
5129
        * The position of the year in the month year label string used as the Calendar header
5130
        * @config MY_LABEL_YEAR_POSITION
5131
        * @type Number
5132
        * @default 2
5133
        */
5134
        cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5135
 
5136
        /**
5137
        * The suffix used after the month when rendering the Calendar header
5138
        * @config MY_LABEL_MONTH_SUFFIX
5139
        * @type String
5140
        * @default " "
5141
        */
5142
        cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.delegateConfig } );
5143
 
5144
        /**
5145
        * The suffix used after the year when rendering the Calendar header
5146
        * @config MY_LABEL_YEAR_SUFFIX
5147
        * @type String
5148
        * @default ""
5149
        */
5150
        cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.delegateConfig } );
5151
 
5152
        /**
5153
        * Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a
5154
        * specific Month/Year without having to scroll sequentially through months.
5155
        * <p>
5156
        * Setting this property to null (default value) or false, will disable the CalendarNavigator UI.
5157
        * </p>
5158
        * <p>
5159
        * Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values.
5160
        * </p>
5161
        * <p>
5162
        * This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI.
5163
        * The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object.
5164
        * Any properties which are not provided will use the default values (defined in the CalendarNavigator class).
5165
        * </p>
5166
        * <dl>
5167
        * <dt>strings</dt>
5168
        * <dd><em>Object</em> :  An object with the properties shown below, defining the string labels to use in the Navigator's UI. The strings are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
5169
        *     <dl>
5170
        *         <dt>month</dt><dd><em>HTML</em> : The markup to use for the month label. Defaults to "Month".</dd>
5171
        *         <dt>year</dt><dd><em>HTML</em> : The markup to use for the year label. Defaults to "Year".</dd>
5172
        *         <dt>submit</dt><dd><em>HTML</em> : The markup to use for the submit button label. Defaults to "Okay".</dd>
5173
        *         <dt>cancel</dt><dd><em>HTML</em> : The markup to use for the cancel button label. Defaults to "Cancel".</dd>
5174
        *         <dt>invalidYear</dt><dd><em>HTML</em> : The markup to use for invalid year values. Defaults to "Year needs to be a number".</dd>
5175
        *     </dl>
5176
        * </dd>
5177
        * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd>
5178
        * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
5179
        * </dl>
5180
        * <p>E.g.</p>
5181
        * <pre>
5182
        * var navConfig = {
5183
        *   strings: {
5184
        *    month:"Calendar Month",
5185
        *    year:"Calendar Year",
5186
        *    submit: "Submit",
5187
        *    cancel: "Cancel",
5188
        *    invalidYear: "Please enter a valid year"
5189
        *   },
5190
        *   monthFormat: YAHOO.widget.Calendar.SHORT,
5191
        *   initialFocus: "month"
5192
        * }
5193
        * </pre>
5194
        * @config navigator
5195
        * @type {Object|Boolean}
5196
        * @default null
5197
        */
5198
        cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
5199
 
5200
        /**
5201
         * The map of UI strings which the CalendarGroup UI uses.
5202
         *
5203
         * @config strings
5204
         * @type {Object}
5205
         * @default An object with the properties shown below:
5206
         *     <dl>
5207
         *         <dt>previousMonth</dt><dd><em>HTML</em> : The markup to use for the "Previous Month" navigation label. Defaults to "Previous Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
5208
         *         <dt>nextMonth</dt><dd><em>HTML</em> : The markup to use for the "Next Month" navigation UI. Defaults to "Next Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
5209
         *         <dt>close</dt><dd><em>HTML</em> : The markup to use for the close button label. Defaults to "Close". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
5210
         *     </dl>
5211
         */
5212
        cfg.addProperty(DEF_CFG.STRINGS.key, {
5213
            value:DEF_CFG.STRINGS.value,
5214
            handler:this.configStrings,
5215
            validator: function(val) {
5216
                return Lang.isObject(val);
5217
            },
5218
            supercedes: DEF_CFG.STRINGS.supercedes
5219
        });
5220
    },
5221
 
5222
    /**
5223
    * Initializes CalendarGroup's built-in CustomEvents
5224
    * @method initEvents
5225
    */
5226
    initEvents : function() {
5227
 
5228
        var me = this,
5229
            strEvent = "Event",
5230
            CE = YAHOO.util.CustomEvent;
5231
 
5232
        /**
5233
        * Proxy subscriber to subscribe to the CalendarGroup's child Calendars' CustomEvents
5234
        * @method sub
5235
        * @private
5236
        * @param {Function} fn The function to subscribe to this CustomEvent
5237
        * @param {Object} obj The CustomEvent's scope object
5238
        * @param {Boolean} bOverride Whether or not to apply scope correction
5239
        */
5240
        var sub = function(fn, obj, bOverride) {
5241
            for (var p=0;p<me.pages.length;++p) {
5242
                var cal = me.pages[p];
5243
                cal[this.type + strEvent].subscribe(fn, obj, bOverride);
5244
            }
5245
        };
5246
 
5247
        /**
5248
        * Proxy unsubscriber to unsubscribe from the CalendarGroup's child Calendars' CustomEvents
5249
        * @method unsub
5250
        * @private
5251
        * @param {Function} fn The function to subscribe to this CustomEvent
5252
        * @param {Object} obj The CustomEvent's scope object
5253
        */
5254
        var unsub = function(fn, obj) {
5255
            for (var p=0;p<me.pages.length;++p) {
5256
                var cal = me.pages[p];
5257
                cal[this.type + strEvent].unsubscribe(fn, obj);
5258
            }
5259
        };
5260
 
5261
        var defEvents = Calendar._EVENT_TYPES;
5262
 
5263
        /**
5264
        * Fired before a date selection is made
5265
        * @event beforeSelectEvent
5266
        */
5267
        me.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
5268
        me.beforeSelectEvent.subscribe = sub; me.beforeSelectEvent.unsubscribe = unsub;
5269
 
5270
        /**
5271
        * Fired when a date selection is made
5272
        * @event selectEvent
5273
        * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
5274
        */
5275
        me.selectEvent = new CE(defEvents.SELECT);
5276
        me.selectEvent.subscribe = sub; me.selectEvent.unsubscribe = unsub;
5277
 
5278
        /**
5279
        * Fired before a date or set of dates is deselected
5280
        * @event beforeDeselectEvent
5281
        */
5282
        me.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
5283
        me.beforeDeselectEvent.subscribe = sub; me.beforeDeselectEvent.unsubscribe = unsub;
5284
 
5285
        /**
5286
        * Fired when a date or set of dates has been deselected
5287
        * @event deselectEvent
5288
        * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
5289
        */
5290
        me.deselectEvent = new CE(defEvents.DESELECT);
5291
        me.deselectEvent.subscribe = sub; me.deselectEvent.unsubscribe = unsub;
5292
 
5293
        /**
5294
        * Fired when the Calendar page is changed
5295
        * @event changePageEvent
5296
        */
5297
        me.changePageEvent = new CE(defEvents.CHANGE_PAGE);
5298
        me.changePageEvent.subscribe = sub; me.changePageEvent.unsubscribe = unsub;
5299
 
5300
        /**
5301
        * Fired before the Calendar is rendered
5302
        * @event beforeRenderEvent
5303
        */
5304
        me.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
5305
        me.beforeRenderEvent.subscribe = sub; me.beforeRenderEvent.unsubscribe = unsub;
5306
 
5307
        /**
5308
        * Fired when the Calendar is rendered
5309
        * @event renderEvent
5310
        */
5311
        me.renderEvent = new CE(defEvents.RENDER);
5312
        me.renderEvent.subscribe = sub; me.renderEvent.unsubscribe = unsub;
5313
 
5314
        /**
5315
        * Fired when the Calendar is reset
5316
        * @event resetEvent
5317
        */
5318
        me.resetEvent = new CE(defEvents.RESET);
5319
        me.resetEvent.subscribe = sub; me.resetEvent.unsubscribe = unsub;
5320
 
5321
        /**
5322
        * Fired when the Calendar is cleared
5323
        * @event clearEvent
5324
        */
5325
        me.clearEvent = new CE(defEvents.CLEAR);
5326
        me.clearEvent.subscribe = sub; me.clearEvent.unsubscribe = unsub;
5327
 
5328
        /**
5329
        * Fired just before the CalendarGroup is to be shown
5330
        * @event beforeShowEvent
5331
        */
5332
        me.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
5333
 
5334
        /**
5335
        * Fired after the CalendarGroup is shown
5336
        * @event showEvent
5337
        */
5338
        me.showEvent = new CE(defEvents.SHOW);
5339
 
5340
        /**
5341
        * Fired just before the CalendarGroup is to be hidden
5342
        * @event beforeHideEvent
5343
        */
5344
        me.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
5345
 
5346
        /**
5347
        * Fired after the CalendarGroup is hidden
5348
        * @event hideEvent
5349
        */
5350
        me.hideEvent = new CE(defEvents.HIDE);
5351
 
5352
        /**
5353
        * Fired just before the CalendarNavigator is to be shown
5354
        * @event beforeShowNavEvent
5355
        */
5356
        me.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
5357
 
5358
        /**
5359
        * Fired after the CalendarNavigator is shown
5360
        * @event showNavEvent
5361
        */
5362
        me.showNavEvent = new CE(defEvents.SHOW_NAV);
5363
 
5364
        /**
5365
        * Fired just before the CalendarNavigator is to be hidden
5366
        * @event beforeHideNavEvent
5367
        */
5368
        me.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
5369
 
5370
        /**
5371
        * Fired after the CalendarNavigator is hidden
5372
        * @event hideNavEvent
5373
        */
5374
        me.hideNavEvent = new CE(defEvents.HIDE_NAV);
5375
 
5376
        /**
5377
        * Fired just before the CalendarNavigator is to be rendered
5378
        * @event beforeRenderNavEvent
5379
        */
5380
        me.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
5381
 
5382
        /**
5383
        * Fired after the CalendarNavigator is rendered
5384
        * @event renderNavEvent
5385
        */
5386
        me.renderNavEvent = new CE(defEvents.RENDER_NAV);
5387
 
5388
        /**
5389
        * Fired just before the CalendarGroup is to be destroyed
5390
        * @event beforeDestroyEvent
5391
        */
5392
        me.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
5393
 
5394
        /**
5395
        * Fired after the CalendarGroup is destroyed. This event should be used
5396
        * for notification only. When this event is fired, important CalendarGroup instance
5397
        * properties, dom references and event listeners have already been
5398
        * removed/dereferenced, and hence the CalendarGroup instance is not in a usable
5399
        * state.
5400
        *
5401
        * @event destroyEvent
5402
        */
5403
        me.destroyEvent = new CE(defEvents.DESTROY);
5404
    },
5405
 
5406
    /**
5407
    * The default Config handler for the "pages" property
5408
    * @method configPages
5409
    * @param {String} type The CustomEvent type (usually the property name)
5410
    * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5411
    * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5412
    */
5413
    configPages : function(type, args, obj) {
5414
        var pageCount = args[0],
5415
            cfgPageDate = DEF_CFG.PAGEDATE.key,
5416
            sep = "_",
5417
            caldate,
5418
            firstPageDate = null,
5419
            groupCalClass = "groupcal",
5420
            firstClass = "first-of-type",
5421
            lastClass = "last-of-type";
5422
 
5423
        for (var p=0;p<pageCount;++p) {
5424
            var calId = this.id + sep + p,
5425
                calContainerId = this.containerId + sep + p,
5426
                childConfig = this.cfg.getConfig();
5427
 
5428
            childConfig.close = false;
5429
            childConfig.title = false;
5430
            childConfig.navigator = null;
5431
 
5432
            if (p > 0) {
5433
                caldate = new Date(firstPageDate);
5434
                this._setMonthOnDate(caldate, caldate.getMonth() + p);
5435
                childConfig.pageDate = caldate;
5436
            }
5437
 
5438
            var cal = this.constructChild(calId, calContainerId, childConfig);
5439
 
5440
            Dom.removeClass(cal.oDomContainer, this.Style.CSS_SINGLE);
5441
            Dom.addClass(cal.oDomContainer, groupCalClass);
5442
 
5443
            if (p===0) {
5444
                firstPageDate = cal.cfg.getProperty(cfgPageDate);
5445
                Dom.addClass(cal.oDomContainer, firstClass);
5446
            }
5447
 
5448
            if (p==(pageCount-1)) {
5449
                Dom.addClass(cal.oDomContainer, lastClass);
5450
            }
5451
 
5452
            cal.parent = this;
5453
            cal.index = p;
5454
 
5455
            this.pages[this.pages.length] = cal;
5456
        }
5457
    },
5458
 
5459
    /**
5460
    * The default Config handler for the "pagedate" property
5461
    * @method configPageDate
5462
    * @param {String} type The CustomEvent type (usually the property name)
5463
    * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5464
    * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5465
    */
5466
    configPageDate : function(type, args, obj) {
5467
        var val = args[0],
5468
            firstPageDate;
5469
 
5470
        var cfgPageDate = DEF_CFG.PAGEDATE.key;
5471
 
5472
        for (var p=0;p<this.pages.length;++p) {
5473
            var cal = this.pages[p];
5474
            if (p === 0) {
5475
                firstPageDate = cal._parsePageDate(val);
5476
                cal.cfg.setProperty(cfgPageDate, firstPageDate);
5477
            } else {
5478
                var pageDate = new Date(firstPageDate);
5479
                this._setMonthOnDate(pageDate, pageDate.getMonth() + p);
5480
                cal.cfg.setProperty(cfgPageDate, pageDate);
5481
            }
5482
        }
5483
    },
5484
 
5485
    /**
5486
    * The default Config handler for the CalendarGroup "selected" property
5487
    * @method configSelected
5488
    * @param {String} type The CustomEvent type (usually the property name)
5489
    * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5490
    * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5491
    */
5492
    configSelected : function(type, args, obj) {
5493
        var cfgSelected = DEF_CFG.SELECTED.key;
5494
        this.delegateConfig(type, args, obj);
5495
        var selected = (this.pages.length > 0) ? this.pages[0].cfg.getProperty(cfgSelected) : [];
5496
        this.cfg.setProperty(cfgSelected, selected, true);
5497
    },
5498
 
5499
 
5500
    /**
5501
    * Delegates a configuration property to the CustomEvents associated with the CalendarGroup's children
5502
    * @method delegateConfig
5503
    * @param {String} type The CustomEvent type (usually the property name)
5504
    * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5505
    * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5506
    */
5507
    delegateConfig : function(type, args, obj) {
5508
        var val = args[0];
5509
        var cal;
5510
 
5511
        for (var p=0;p<this.pages.length;p++) {
5512
            cal = this.pages[p];
5513
            cal.cfg.setProperty(type, val);
5514
        }
5515
    },
5516
 
5517
    /**
5518
    * Adds a function to all child Calendars within this CalendarGroup.
5519
    * @method setChildFunction
5520
    * @param {String}  fnName  The name of the function
5521
    * @param {Function}  fn   The function to apply to each Calendar page object
5522
    */
5523
    setChildFunction : function(fnName, fn) {
5524
        var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
5525
 
5526
        for (var p=0;p<pageCount;++p) {
5527
            this.pages[p][fnName] = fn;
5528
        }
5529
    },
5530
 
5531
    /**
5532
    * Calls a function within all child Calendars within this CalendarGroup.
5533
    * @method callChildFunction
5534
    * @param {String}  fnName  The name of the function
5535
    * @param {Array}  args  The arguments to pass to the function
5536
    */
5537
    callChildFunction : function(fnName, args) {
5538
        var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
5539
 
5540
        for (var p=0;p<pageCount;++p) {
5541
            var page = this.pages[p];
5542
            if (page[fnName]) {
5543
                var fn = page[fnName];
5544
                fn.call(page, args);
5545
            }
5546
        }
5547
    },
5548
 
5549
    /**
5550
    * Constructs a child calendar. This method can be overridden if a subclassed version of the default
5551
    * calendar is to be used.
5552
    * @method constructChild
5553
    * @param {String} id   The id of the table element that will represent the calendar widget
5554
    * @param {String} containerId The id of the container div element that will wrap the calendar table
5555
    * @param {Object} config  The configuration object containing the Calendar's arguments
5556
    * @return {YAHOO.widget.Calendar} The YAHOO.widget.Calendar instance that is constructed
5557
    */
5558
    constructChild : function(id,containerId,config) {
5559
        var container = document.getElementById(containerId);
5560
        if (! container) {
5561
            container = document.createElement("div");
5562
            container.id = containerId;
5563
            this.oDomContainer.appendChild(container);
5564
        }
5565
        return new Calendar(id,containerId,config);
5566
    },
5567
 
5568
    /**
5569
    * Sets the calendar group's month explicitly. This month will be set into the first
5570
    * page of the multi-page calendar, and all other months will be iterated appropriately.
5571
    * @method setMonth
5572
    * @param {Number} month  The numeric month, from 0 (January) to 11 (December)
5573
    */
5574
    setMonth : function(month) {
5575
        month = parseInt(month, 10);
5576
        var currYear;
5577
 
5578
        var cfgPageDate = DEF_CFG.PAGEDATE.key;
5579
 
5580
        for (var p=0; p<this.pages.length; ++p) {
5581
            var cal = this.pages[p];
5582
            var pageDate = cal.cfg.getProperty(cfgPageDate);
5583
            if (p === 0) {
5584
                currYear = pageDate.getFullYear();
5585
            } else {
5586
                pageDate.setFullYear(currYear);
5587
            }
5588
            this._setMonthOnDate(pageDate, month+p);
5589
            cal.cfg.setProperty(cfgPageDate, pageDate);
5590
        }
5591
    },
5592
 
5593
    /**
5594
    * Sets the calendar group's year explicitly. This year will be set into the first
5595
    * page of the multi-page calendar, and all other months will be iterated appropriately.
5596
    * @method setYear
5597
    * @param {Number} year  The numeric 4-digit year
5598
    */
5599
    setYear : function(year) {
5600
 
5601
        var cfgPageDate = DEF_CFG.PAGEDATE.key;
5602
 
5603
        year = parseInt(year, 10);
5604
        for (var p=0;p<this.pages.length;++p) {
5605
            var cal = this.pages[p];
5606
            var pageDate = cal.cfg.getProperty(cfgPageDate);
5607
 
5608
            if ((pageDate.getMonth()+1) == 1 && p>0) {
5609
                year+=1;
5610
            }
5611
            cal.setYear(year);
5612
        }
5613
    },
5614
 
5615
    /**
5616
    * Calls the render function of all child calendars within the group.
5617
    * @method render
5618
    */
5619
    render : function() {
5620
        this.renderHeader();
5621
        for (var p=0;p<this.pages.length;++p) {
5622
            var cal = this.pages[p];
5623
            cal.render();
5624
        }
5625
        this.renderFooter();
5626
    },
5627
 
5628
    /**
5629
    * Selects a date or a collection of dates on the current calendar. This method, by default,
5630
    * does not call the render method explicitly. Once selection has completed, render must be
5631
    * called for the changes to be reflected visually.
5632
    * @method select
5633
    * @param    {String/Date/Date[]}    date    The date string of dates to select in the current calendar. Valid formats are
5634
    *                               individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
5635
    *                               Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
5636
    *                               This method can also take a JavaScript Date object or an array of Date objects.
5637
    * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5638
    */
5639
    select : function(date) {
5640
        for (var p=0;p<this.pages.length;++p) {
5641
            var cal = this.pages[p];
5642
            cal.select(date);
5643
        }
5644
        return this.getSelectedDates();
5645
    },
5646
 
5647
    /**
5648
    * 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.
5649
    * The value of the MULTI_SELECT Configuration attribute will determine the set of dates which get selected.
5650
    * <ul>
5651
    *    <li>If MULTI_SELECT is false, selectCell will select the cell at the specified index for only the last displayed Calendar page.</li>
5652
    *    <li>If MULTI_SELECT is true, selectCell will select the cell at the specified index, on each displayed Calendar page.</li>
5653
    * </ul>
5654
    * @method selectCell
5655
    * @param {Number} cellIndex The index of the cell to be selected.
5656
    * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5657
    */
5658
    selectCell : function(cellIndex) {
5659
        for (var p=0;p<this.pages.length;++p) {
5660
            var cal = this.pages[p];
5661
            cal.selectCell(cellIndex);
5662
        }
5663
        return this.getSelectedDates();
5664
    },
5665
 
5666
    /**
5667
    * Deselects a date or a collection of dates on the current calendar. This method, by default,
5668
    * does not call the render method explicitly. Once deselection has completed, render must be
5669
    * called for the changes to be reflected visually.
5670
    * @method deselect
5671
    * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
5672
    *        individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
5673
    *        Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
5674
    *        This method can also take a JavaScript Date object or an array of Date objects.
5675
    * @return {Date[]}   Array of JavaScript Date objects representing all individual dates that are currently selected.
5676
    */
5677
    deselect : function(date) {
5678
        for (var p=0;p<this.pages.length;++p) {
5679
            var cal = this.pages[p];
5680
            cal.deselect(date);
5681
        }
5682
        return this.getSelectedDates();
5683
    },
5684
 
5685
    /**
5686
    * Deselects all dates on the current calendar.
5687
    * @method deselectAll
5688
    * @return {Date[]}  Array of JavaScript Date objects representing all individual dates that are currently selected.
5689
    *      Assuming that this function executes properly, the return value should be an empty array.
5690
    *      However, the empty array is returned for the sake of being able to check the selection status
5691
    *      of the calendar.
5692
    */
5693
    deselectAll : function() {
5694
        for (var p=0;p<this.pages.length;++p) {
5695
            var cal = this.pages[p];
5696
            cal.deselectAll();
5697
        }
5698
        return this.getSelectedDates();
5699
    },
5700
 
5701
    /**
5702
    * 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.
5703
    * deselectCell will deselect the cell at the specified index on each displayed Calendar page.
5704
    *
5705
    * @method deselectCell
5706
    * @param {Number} cellIndex The index of the cell to deselect.
5707
    * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5708
    */
5709
    deselectCell : function(cellIndex) {
5710
        for (var p=0;p<this.pages.length;++p) {
5711
            var cal = this.pages[p];
5712
            cal.deselectCell(cellIndex);
5713
        }
5714
        return this.getSelectedDates();
5715
    },
5716
 
5717
    /**
5718
    * Resets the calendar widget to the originally selected month and year, and
5719
    * sets the calendar to the initial selection(s).
5720
    * @method reset
5721
    */
5722
    reset : function() {
5723
        for (var p=0;p<this.pages.length;++p) {
5724
            var cal = this.pages[p];
5725
            cal.reset();
5726
        }
5727
    },
5728
 
5729
    /**
5730
    * Clears the selected dates in the current calendar widget and sets the calendar
5731
    * to the current month and year.
5732
    * @method clear
5733
    */
5734
    clear : function() {
5735
        for (var p=0;p<this.pages.length;++p) {
5736
            var cal = this.pages[p];
5737
            cal.clear();
5738
        }
5739
 
5740
        this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
5741
        this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.pages[0].today.getTime()));
5742
        this.render();
5743
    },
5744
 
5745
    /**
5746
    * Navigates to the next month page in the calendar widget.
5747
    * @method nextMonth
5748
    */
5749
    nextMonth : function() {
5750
        for (var p=0;p<this.pages.length;++p) {
5751
            var cal = this.pages[p];
5752
            cal.nextMonth();
5753
        }
5754
    },
5755
 
5756
    /**
5757
    * Navigates to the previous month page in the calendar widget.
5758
    * @method previousMonth
5759
    */
5760
    previousMonth : function() {
5761
        for (var p=this.pages.length-1;p>=0;--p) {
5762
            var cal = this.pages[p];
5763
            cal.previousMonth();
5764
        }
5765
    },
5766
 
5767
    /**
5768
    * Navigates to the next year in the currently selected month in the calendar widget.
5769
    * @method nextYear
5770
    */
5771
    nextYear : function() {
5772
        for (var p=0;p<this.pages.length;++p) {
5773
            var cal = this.pages[p];
5774
            cal.nextYear();
5775
        }
5776
    },
5777
 
5778
    /**
5779
    * Navigates to the previous year in the currently selected month in the calendar widget.
5780
    * @method previousYear
5781
    */
5782
    previousYear : function() {
5783
        for (var p=0;p<this.pages.length;++p) {
5784
            var cal = this.pages[p];
5785
            cal.previousYear();
5786
        }
5787
    },
5788
 
5789
    /**
5790
    * Gets the list of currently selected dates from the calendar.
5791
    * @return   An array of currently selected JavaScript Date objects.
5792
    * @type Date[]
5793
    */
5794
    getSelectedDates : function() {
5795
        var returnDates = [];
5796
        var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
5797
        for (var d=0;d<selected.length;++d) {
5798
            var dateArray = selected[d];
5799
 
5800
            var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
5801
            returnDates.push(date);
5802
        }
5803
 
5804
        returnDates.sort( function(a,b) { return a-b; } );
5805
        return returnDates;
5806
    },
5807
 
5808
    /**
5809
    * Adds a renderer to the render stack. The function reference passed to this method will be executed
5810
    * when a date cell matches the conditions specified in the date string for this renderer.
5811
    *
5812
    * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should
5813
    * escape markup used to set the cell contents, if coming from an external source.<p>
5814
    * @method addRenderer
5815
    * @param {String} sDates  A date string to associate with the specified renderer. Valid formats
5816
    *         include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
5817
    * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5818
    */
5819
    addRenderer : function(sDates, fnRender) {
5820
        for (var p=0;p<this.pages.length;++p) {
5821
            var cal = this.pages[p];
5822
            cal.addRenderer(sDates, fnRender);
5823
        }
5824
    },
5825
 
5826
    /**
5827
    * Adds a month renderer to the render stack. The function reference passed to this method will be executed
5828
    * when a date cell matches the month passed to this method
5829
    *
5830
    * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should
5831
    * escape markup used to set the cell contents, if coming from an external source.<p>
5832
    * @method addMonthRenderer
5833
    * @param {Number} month  The month (1-12) to associate with this renderer
5834
    * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5835
    */
5836
    addMonthRenderer : function(month, fnRender) {
5837
        for (var p=0;p<this.pages.length;++p) {
5838
            var cal = this.pages[p];
5839
            cal.addMonthRenderer(month, fnRender);
5840
        }
5841
    },
5842
 
5843
    /**
5844
    * Adds a weekday renderer to the render stack. The function reference passed to this method will be executed
5845
    * when a date cell matches the weekday passed to this method.
5846
    *
5847
    * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should
5848
    * escape HTML used to set the cell contents, if coming from an external source.<p>
5849
    *
5850
    * @method addWeekdayRenderer
5851
    * @param {Number} weekday  The weekday (Sunday = 1, Monday = 2 ... Saturday = 7) to associate with this renderer
5852
    * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5853
    */
5854
    addWeekdayRenderer : function(weekday, fnRender) {
5855
        for (var p=0;p<this.pages.length;++p) {
5856
            var cal = this.pages[p];
5857
            cal.addWeekdayRenderer(weekday, fnRender);
5858
        }
5859
    },
5860
 
5861
    /**
5862
     * Removes all custom renderers added to the CalendarGroup through the addRenderer, addMonthRenderer and
5863
     * addWeekRenderer methods. CalendarGroup's render method needs to be called to after removing renderers
5864
     * to see the changes applied.
5865
     *
5866
     * @method removeRenderers
5867
     */
5868
    removeRenderers : function() {
5869
        this.callChildFunction("removeRenderers");
5870
    },
5871
 
5872
    /**
5873
    * Renders the header for the CalendarGroup.
5874
    * @method renderHeader
5875
    */
5876
    renderHeader : function() {
5877
        // EMPTY DEFAULT IMPL
5878
    },
5879
 
5880
    /**
5881
    * Renders a footer for the 2-up calendar container. By default, this method is
5882
    * unimplemented.
5883
    * @method renderFooter
5884
    */
5885
    renderFooter : function() {
5886
        // EMPTY DEFAULT IMPL
5887
    },
5888
 
5889
    /**
5890
    * Adds the designated number of months to the current calendar month, and sets the current
5891
    * calendar page date to the new month.
5892
    * @method addMonths
5893
    * @param {Number} count The number of months to add to the current calendar
5894
    */
5895
    addMonths : function(count) {
5896
        this.callChildFunction("addMonths", count);
5897
    },
5898
 
5899
    /**
5900
    * Subtracts the designated number of months from the current calendar month, and sets the current
5901
    * calendar page date to the new month.
5902
    * @method subtractMonths
5903
    * @param {Number} count The number of months to subtract from the current calendar
5904
    */
5905
    subtractMonths : function(count) {
5906
        this.callChildFunction("subtractMonths", count);
5907
    },
5908
 
5909
    /**
5910
    * Adds the designated number of years to the current calendar, and sets the current
5911
    * calendar page date to the new month.
5912
    * @method addYears
5913
    * @param {Number} count The number of years to add to the current calendar
5914
    */
5915
    addYears : function(count) {
5916
        this.callChildFunction("addYears", count);
5917
    },
5918
 
5919
    /**
5920
    * Subtcats the designated number of years from the current calendar, and sets the current
5921
    * calendar page date to the new month.
5922
    * @method subtractYears
5923
    * @param {Number} count The number of years to subtract from the current calendar
5924
    */
5925
    subtractYears : function(count) {
5926
        this.callChildFunction("subtractYears", count);
5927
    },
5928
 
5929
    /**
5930
     * Returns the Calendar page instance which has a pagedate (month/year) matching the given date.
5931
     * Returns null if no match is found.
5932
     *
5933
     * @method getCalendarPage
5934
     * @param {Date} date The JavaScript Date object for which a Calendar page is to be found.
5935
     * @return {Calendar} The Calendar page instance representing the month to which the date
5936
     * belongs.
5937
     */
5938
    getCalendarPage : function(date) {
5939
        var cal = null;
5940
        if (date) {
5941
            var y = date.getFullYear(),
5942
                m = date.getMonth();
5943
 
5944
            var pages = this.pages;
5945
            for (var i = 0; i < pages.length; ++i) {
5946
                var pageDate = pages[i].cfg.getProperty("pagedate");
5947
                if (pageDate.getFullYear() === y && pageDate.getMonth() === m) {
5948
                    cal = pages[i];
5949
                    break;
5950
                }
5951
            }
5952
        }
5953
        return cal;
5954
    },
5955
 
5956
    /**
5957
    * Sets the month on a Date object, taking into account year rollover if the month is less than 0 or greater than 11.
5958
    * 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
5959
    * @method _setMonthOnDate
5960
    * @private
5961
    * @param {Date} date The Date object on which to set the month index
5962
    * @param {Number} iMonth The month index to set
5963
    */
5964
    _setMonthOnDate : function(date, iMonth) {
5965
        // Bug in Safari 1.3, 2.0 (WebKit build < 420), Date.setMonth does not work consistently if iMonth is not 0-11
5966
        if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420 && (iMonth < 0 || iMonth > 11)) {
5967
            var newDate = DateMath.add(date, DateMath.MONTH, iMonth-date.getMonth());
5968
            date.setTime(newDate.getTime());
5969
        } else {
5970
            date.setMonth(iMonth);
5971
        }
5972
    },
5973
 
5974
    /**
5975
     * Fixes the width of the CalendarGroup container element, to account for miswrapped floats
5976
     * @method _fixWidth
5977
     * @private
5978
     */
5979
    _fixWidth : function() {
5980
        var w = 0;
5981
        for (var p=0;p<this.pages.length;++p) {
5982
            var cal = this.pages[p];
5983
            w += cal.oDomContainer.offsetWidth;
5984
        }
5985
        if (w > 0) {
5986
            this.oDomContainer.style.width = w + "px";
5987
        }
5988
    },
5989
 
5990
    /**
5991
    * Returns a string representation of the object.
5992
    * @method toString
5993
    * @return {String} A string representation of the CalendarGroup object.
5994
    */
5995
    toString : function() {
5996
        return "CalendarGroup " + this.id;
5997
    },
5998
 
5999
    /**
6000
     * Destroys the CalendarGroup instance. The method will remove references
6001
     * to HTML elements, remove any event listeners added by the CalendarGroup.
6002
     *
6003
     * It will also destroy the Config and CalendarNavigator instances created by the
6004
     * CalendarGroup and the individual Calendar instances created for each page.
6005
     *
6006
     * @method destroy
6007
     */
6008
    destroy : function() {
6009
 
6010
        if (this.beforeDestroyEvent.fire()) {
6011
 
6012
            var cal = this;
6013
 
6014
            // Child objects
6015
            if (cal.navigator) {
6016
                cal.navigator.destroy();
6017
            }
6018
 
6019
            if (cal.cfg) {
6020
                cal.cfg.destroy();
6021
            }
6022
 
6023
            // DOM event listeners
6024
            Event.purgeElement(cal.oDomContainer, true);
6025
 
6026
            // Generated markup/DOM - Not removing the container DIV since we didn't create it.
6027
            Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_CONTAINER);
6028
            Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_MULTI_UP);
6029
 
6030
            for (var i = 0, l = cal.pages.length; i < l; i++) {
6031
                cal.pages[i].destroy();
6032
                cal.pages[i] = null;
6033
            }
6034
 
6035
            cal.oDomContainer.innerHTML = "";
6036
 
6037
            // JS-to-DOM references
6038
            cal.oDomContainer = null;
6039
 
6040
            this.destroyEvent.fire();
6041
        }
6042
    }
6043
};
6044
 
6045
/**
6046
* CSS class representing the container for the calendar
6047
* @property YAHOO.widget.CalendarGroup.CSS_CONTAINER
6048
* @static
6049
* @final
6050
* @type String
6051
*/
6052
CalendarGroup.CSS_CONTAINER = "yui-calcontainer";
6053
 
6054
/**
6055
* CSS class representing the container for the calendar
6056
* @property YAHOO.widget.CalendarGroup.CSS_MULTI_UP
6057
* @static
6058
* @final
6059
* @type String
6060
*/
6061
CalendarGroup.CSS_MULTI_UP = "multi";
6062
 
6063
/**
6064
* CSS class representing the title for the 2-up calendar
6065
* @property YAHOO.widget.CalendarGroup.CSS_2UPTITLE
6066
* @static
6067
* @final
6068
* @type String
6069
*/
6070
CalendarGroup.CSS_2UPTITLE = "title";
6071
 
6072
/**
6073
* CSS class representing the close icon for the 2-up calendar
6074
* @property YAHOO.widget.CalendarGroup.CSS_2UPCLOSE
6075
* @static
6076
* @final
6077
* @deprecated Along with Calendar.IMG_ROOT and NAV_ARROW_LEFT, NAV_ARROW_RIGHT configuration properties.
6078
*     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
6079
* @type String
6080
*/
6081
CalendarGroup.CSS_2UPCLOSE = "close-icon";
6082
 
6083
YAHOO.lang.augmentProto(CalendarGroup, Calendar, "buildDayLabel",
6084
                                                 "buildMonthLabel",
6085
                                                 "renderOutOfBoundsDate",
6086
                                                 "renderRowHeader",
6087
                                                 "renderRowFooter",
6088
                                                 "renderCellDefault",
6089
                                                 "styleCellDefault",
6090
                                                 "renderCellStyleHighlight1",
6091
                                                 "renderCellStyleHighlight2",
6092
                                                 "renderCellStyleHighlight3",
6093
                                                 "renderCellStyleHighlight4",
6094
                                                 "renderCellStyleToday",
6095
                                                 "renderCellStyleSelected",
6096
                                                 "renderCellNotThisMonth",
6097
                                                 "styleCellNotThisMonth",
6098
                                                 "renderBodyCellRestricted",
6099
                                                 "initStyles",
6100
                                                 "configTitle",
6101
                                                 "configClose",
6102
                                                 "configIframe",
6103
                                                 "configStrings",
6104
                                                 "configToday",
6105
                                                 "configNavigator",
6106
                                                 "createTitleBar",
6107
                                                 "createCloseButton",
6108
                                                 "removeTitleBar",
6109
                                                 "removeCloseButton",
6110
                                                 "hide",
6111
                                                 "show",
6112
                                                 "toDate",
6113
                                                 "_toDate",
6114
                                                 "_parseArgs",
6115
                                                 "browser");
6116
 
6117
YAHOO.widget.CalGrp = CalendarGroup;
6118
YAHOO.widget.CalendarGroup = CalendarGroup;
6119
 
6120
/**
6121
* @class YAHOO.widget.Calendar2up
6122
* @extends YAHOO.widget.CalendarGroup
6123
* @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
6124
*/
6125
YAHOO.widget.Calendar2up = function(id, containerId, config) {
6126
    this.init(id, containerId, config);
6127
};
6128
 
6129
YAHOO.extend(YAHOO.widget.Calendar2up, CalendarGroup);
6130
 
6131
/**
6132
* @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
6133
*/
6134
YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up;
6135
 
6136
})();
6137
/**
6138
 * The CalendarNavigator is used along with a Calendar/CalendarGroup to
6139
 * provide a Month/Year popup navigation control, allowing the user to navigate
6140
 * to a specific month/year in the Calendar/CalendarGroup without having to
6141
 * scroll through months sequentially
6142
 *
6143
 * @namespace YAHOO.widget
6144
 * @class CalendarNavigator
6145
 * @constructor
6146
 * @param {Calendar|CalendarGroup} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached.
6147
 */
6148
YAHOO.widget.CalendarNavigator = function(cal) {
6149
    this.init(cal);
6150
};
6151
 
6152
(function() {
6153
    // Setup static properties (inside anon fn, so that we can use shortcuts)
6154
    var CN = YAHOO.widget.CalendarNavigator;
6155
 
6156
    /**
6157
     * YAHOO.widget.CalendarNavigator.CLASSES contains constants
6158
     * for the class values applied to the CalendarNaviatgator's
6159
     * DOM elements
6160
     * @property YAHOO.widget.CalendarNavigator.CLASSES
6161
     * @type Object
6162
     * @static
6163
     */
6164
    CN.CLASSES = {
6165
        /**
6166
         * Class applied to the Calendar Navigator's bounding box
6167
         * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV
6168
         * @type String
6169
         * @static
6170
         */
6171
        NAV :"yui-cal-nav",
6172
        /**
6173
         * Class applied to the Calendar/CalendarGroup's bounding box to indicate
6174
         * the Navigator is currently visible
6175
         * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV_VISIBLE
6176
         * @type String
6177
         * @static
6178
         */
6179
        NAV_VISIBLE: "yui-cal-nav-visible",
6180
        /**
6181
         * Class applied to the Navigator mask's bounding box
6182
         * @property YAHOO.widget.CalendarNavigator.CLASSES.MASK
6183
         * @type String
6184
         * @static
6185
         */
6186
        MASK : "yui-cal-nav-mask",
6187
        /**
6188
         * Class applied to the year label/control bounding box
6189
         * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR
6190
         * @type String
6191
         * @static
6192
         */
6193
        YEAR : "yui-cal-nav-y",
6194
        /**
6195
         * Class applied to the month label/control bounding box
6196
         * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH
6197
         * @type String
6198
         * @static
6199
         */
6200
        MONTH : "yui-cal-nav-m",
6201
        /**
6202
         * Class applied to the submit/cancel button's bounding box
6203
         * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTONS
6204
         * @type String
6205
         * @static
6206
         */
6207
        BUTTONS : "yui-cal-nav-b",
6208
        /**
6209
         * Class applied to buttons wrapping element
6210
         * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTON
6211
         * @type String
6212
         * @static
6213
         */
6214
        BUTTON : "yui-cal-nav-btn",
6215
        /**
6216
         * Class applied to the validation error area's bounding box
6217
         * @property YAHOO.widget.CalendarNavigator.CLASSES.ERROR
6218
         * @type String
6219
         * @static
6220
         */
6221
        ERROR : "yui-cal-nav-e",
6222
        /**
6223
         * Class applied to the year input control
6224
         * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR_CTRL
6225
         * @type String
6226
         * @static
6227
         */
6228
        YEAR_CTRL : "yui-cal-nav-yc",
6229
        /**
6230
         * Class applied to the month input control
6231
         * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH_CTRL
6232
         * @type String
6233
         * @static
6234
         */
6235
        MONTH_CTRL : "yui-cal-nav-mc",
6236
        /**
6237
         * Class applied to controls with invalid data (e.g. a year input field with invalid an year)
6238
         * @property YAHOO.widget.CalendarNavigator.CLASSES.INVALID
6239
         * @type String
6240
         * @static
6241
         */
6242
        INVALID : "yui-invalid",
6243
        /**
6244
         * Class applied to default controls
6245
         * @property YAHOO.widget.CalendarNavigator.CLASSES.DEFAULT
6246
         * @type String
6247
         * @static
6248
         */
6249
        DEFAULT : "yui-default"
6250
    };
6251
 
6252
    /**
6253
     * Object literal containing the default configuration values for the CalendarNavigator
6254
     * The configuration object is expected to follow the format below, with the properties being
6255
     * case sensitive.
6256
     * <dl>
6257
     * <dt>strings</dt>
6258
     * <dd><em>Object</em> :  An object with the properties shown below, defining the string labels to use in the Navigator's UI
6259
     *     <dl>
6260
     *         <dt>month</dt><dd><em>HTML</em> : The markup to use for the month label. Defaults to "Month".</dd>
6261
     *         <dt>year</dt><dd><em>HTML</em> : The markup to use for the year label. Defaults to "Year".</dd>
6262
     *         <dt>submit</dt><dd><em>HTML</em> : The markup to use for the submit button label. Defaults to "Okay".</dd>
6263
     *         <dt>cancel</dt><dd><em>HTML</em> : The markup to use for the cancel button label. Defaults to "Cancel".</dd>
6264
     *         <dt>invalidYear</dt><dd><em>HTML</em> : The markup to use for invalid year values. Defaults to "Year needs to be a number".</dd>
6265
     *     </dl>
6266
     * </dd>
6267
     * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd>
6268
     * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
6269
     * </dl>
6270
     * @property DEFAULT_CONFIG
6271
     * @type Object
6272
     * @static
6273
     */
6274
    CN.DEFAULT_CONFIG = {
6275
        strings : {
6276
            month: "Month",
6277
            year: "Year",
6278
            submit: "Okay",
6279
            cancel: "Cancel",
6280
            invalidYear : "Year needs to be a number"
6281
        },
6282
        monthFormat: YAHOO.widget.Calendar.LONG,
6283
        initialFocus: "year"
6284
    };
6285
 
6286
    /**
6287
     * Object literal containing the default configuration values for the CalendarNavigator
6288
     * @property _DEFAULT_CFG
6289
     * @protected
6290
     * @deprecated Made public. See the public DEFAULT_CONFIG property
6291
     * @type Object
6292
     * @static
6293
     */
6294
    CN._DEFAULT_CFG = CN.DEFAULT_CONFIG;
6295
 
6296
 
6297
    /**
6298
     * The suffix added to the Calendar/CalendarGroup's ID, to generate
6299
     * a unique ID for the Navigator and it's bounding box.
6300
     * @property YAHOO.widget.CalendarNavigator.ID_SUFFIX
6301
     * @static
6302
     * @type String
6303
     * @final
6304
     */
6305
    CN.ID_SUFFIX = "_nav";
6306
    /**
6307
     * The suffix added to the Navigator's ID, to generate
6308
     * a unique ID for the month control.
6309
     * @property YAHOO.widget.CalendarNavigator.MONTH_SUFFIX
6310
     * @static
6311
     * @type String
6312
     * @final
6313
     */
6314
    CN.MONTH_SUFFIX = "_month";
6315
    /**
6316
     * The suffix added to the Navigator's ID, to generate
6317
     * a unique ID for the year control.
6318
     * @property YAHOO.widget.CalendarNavigator.YEAR_SUFFIX
6319
     * @static
6320
     * @type String
6321
     * @final
6322
     */
6323
    CN.YEAR_SUFFIX = "_year";
6324
    /**
6325
     * The suffix added to the Navigator's ID, to generate
6326
     * a unique ID for the error bounding box.
6327
     * @property YAHOO.widget.CalendarNavigator.ERROR_SUFFIX
6328
     * @static
6329
     * @type String
6330
     * @final
6331
     */
6332
    CN.ERROR_SUFFIX = "_error";
6333
    /**
6334
     * The suffix added to the Navigator's ID, to generate
6335
     * a unique ID for the "Cancel" button.
6336
     * @property YAHOO.widget.CalendarNavigator.CANCEL_SUFFIX
6337
     * @static
6338
     * @type String
6339
     * @final
6340
     */
6341
    CN.CANCEL_SUFFIX = "_cancel";
6342
    /**
6343
     * The suffix added to the Navigator's ID, to generate
6344
     * a unique ID for the "Submit" button.
6345
     * @property YAHOO.widget.CalendarNavigator.SUBMIT_SUFFIX
6346
     * @static
6347
     * @type String
6348
     * @final
6349
     */
6350
    CN.SUBMIT_SUFFIX = "_submit";
6351
 
6352
    /**
6353
     * The number of digits to which the year input control is to be limited.
6354
     * @property YAHOO.widget.CalendarNavigator.YR_MAX_DIGITS
6355
     * @static
6356
     * @type Number
6357
     */
6358
    CN.YR_MAX_DIGITS = 4;
6359
 
6360
    /**
6361
     * The amount by which to increment the current year value,
6362
     * when the arrow up/down key is pressed on the year control
6363
     * @property YAHOO.widget.CalendarNavigator.YR_MINOR_INC
6364
     * @static
6365
     * @type Number
6366
     */
6367
    CN.YR_MINOR_INC = 1;
6368
 
6369
    /**
6370
     * The amount by which to increment the current year value,
6371
     * when the page up/down key is pressed on the year control
6372
     * @property YAHOO.widget.CalendarNavigator.YR_MAJOR_INC
6373
     * @static
6374
     * @type Number
6375
     */
6376
    CN.YR_MAJOR_INC = 10;
6377
 
6378
    /**
6379
     * Artificial delay (in ms) between the time the Navigator is hidden
6380
     * and the Calendar/CalendarGroup state is updated. Allows the user
6381
     * the see the Calendar/CalendarGroup page changing. If set to 0
6382
     * the Calendar/CalendarGroup page will be updated instantly
6383
     * @property YAHOO.widget.CalendarNavigator.UPDATE_DELAY
6384
     * @static
6385
     * @type Number
6386
     */
6387
    CN.UPDATE_DELAY = 50;
6388
 
6389
    /**
6390
     * Regular expression used to validate the year input
6391
     * @property YAHOO.widget.CalendarNavigator.YR_PATTERN
6392
     * @static
6393
     * @type RegExp
6394
     */
6395
    CN.YR_PATTERN = /^\d+$/;
6396
    /**
6397
     * Regular expression used to trim strings
6398
     * @property YAHOO.widget.CalendarNavigator.TRIM
6399
     * @static
6400
     * @type RegExp
6401
     */
6402
    CN.TRIM = /^\s*(.*?)\s*$/;
6403
})();
6404
 
6405
YAHOO.widget.CalendarNavigator.prototype = {
6406
 
6407
    /**
6408
     * The unique ID for this CalendarNavigator instance
6409
     * @property id
6410
     * @type String
6411
     */
6412
    id : null,
6413
 
6414
    /**
6415
     * The Calendar/CalendarGroup instance to which the navigator belongs
6416
     * @property cal
6417
     * @type {Calendar|CalendarGroup}
6418
     */
6419
    cal : null,
6420
 
6421
    /**
6422
     * Reference to the HTMLElement used to render the navigator's bounding box
6423
     * @property navEl
6424
     * @type HTMLElement
6425
     */
6426
    navEl : null,
6427
 
6428
    /**
6429
     * Reference to the HTMLElement used to render the navigator's mask
6430
     * @property maskEl
6431
     * @type HTMLElement
6432
     */
6433
    maskEl : null,
6434
 
6435
    /**
6436
     * Reference to the HTMLElement used to input the year
6437
     * @property yearEl
6438
     * @type HTMLElement
6439
     */
6440
    yearEl : null,
6441
 
6442
    /**
6443
     * Reference to the HTMLElement used to input the month
6444
     * @property monthEl
6445
     * @type HTMLElement
6446
     */
6447
    monthEl : null,
6448
 
6449
    /**
6450
     * Reference to the HTMLElement used to display validation errors
6451
     * @property errorEl
6452
     * @type HTMLElement
6453
     */
6454
    errorEl : null,
6455
 
6456
    /**
6457
     * Reference to the HTMLElement used to update the Calendar/Calendar group
6458
     * with the month/year values
6459
     * @property submitEl
6460
     * @type HTMLElement
6461
     */
6462
    submitEl : null,
6463
 
6464
    /**
6465
     * Reference to the HTMLElement used to hide the navigator without updating the
6466
     * Calendar/Calendar group
6467
     * @property cancelEl
6468
     * @type HTMLElement
6469
     */
6470
    cancelEl : null,
6471
 
6472
    /**
6473
     * Reference to the first focusable control in the navigator (by default monthEl)
6474
     * @property firstCtrl
6475
     * @type HTMLElement
6476
     */
6477
    firstCtrl : null,
6478
 
6479
    /**
6480
     * Reference to the last focusable control in the navigator (by default cancelEl)
6481
     * @property lastCtrl
6482
     * @type HTMLElement
6483
     */
6484
    lastCtrl : null,
6485
 
6486
    /**
6487
     * The document containing the Calendar/Calendar group instance
6488
     * @protected
6489
     * @property _doc
6490
     * @type HTMLDocument
6491
     */
6492
    _doc : null,
6493
 
6494
    /**
6495
     * Internal state property for the current year displayed in the navigator
6496
     * @protected
6497
     * @property _year
6498
     * @type Number
6499
     */
6500
    _year: null,
6501
 
6502
    /**
6503
     * Internal state property for the current month index displayed in the navigator
6504
     * @protected
6505
     * @property _month
6506
     * @type Number
6507
     */
6508
    _month: 0,
6509
 
6510
    /**
6511
     * Private internal state property which indicates whether or not the
6512
     * Navigator has been rendered.
6513
     * @private
6514
     * @property __rendered
6515
     * @type Boolean
6516
     */
6517
    __rendered: false,
6518
 
6519
    /**
6520
     * Init lifecycle method called as part of construction
6521
     *
6522
     * @method init
6523
     * @param {Calendar} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached
6524
     */
6525
    init : function(cal) {
6526
        var calBox = cal.oDomContainer;
6527
 
6528
        this.cal = cal;
6529
        this.id = calBox.id + YAHOO.widget.CalendarNavigator.ID_SUFFIX;
6530
        this._doc = calBox.ownerDocument;
6531
 
6532
        /**
6533
         * Private flag, to identify IE Quirks
6534
         * @private
6535
         * @property __isIEQuirks
6536
         */
6537
        var ie = YAHOO.env.ua.ie;
6538
        this.__isIEQuirks = (ie && ((ie <= 6) || (this._doc.compatMode == "BackCompat")));
6539
    },
6540
 
6541
    /**
6542
     * Displays the navigator and mask, updating the input controls to reflect the
6543
     * currently set month and year. The show method will invoke the render method
6544
     * if the navigator has not been renderered already, allowing for lazy rendering
6545
     * of the control.
6546
     *
6547
     * The show method will fire the Calendar/CalendarGroup's beforeShowNav and showNav events
6548
     *
6549
     * @method show
6550
     */
6551
    show : function() {
6552
        var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
6553
 
6554
        if (this.cal.beforeShowNavEvent.fire()) {
6555
            if (!this.__rendered) {
6556
                this.render();
6557
            }
6558
            this.clearErrors();
6559
 
6560
            this._updateMonthUI();
6561
            this._updateYearUI();
6562
            this._show(this.navEl, true);
6563
 
6564
            this.setInitialFocus();
6565
            this.showMask();
6566
 
6567
            YAHOO.util.Dom.addClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
6568
            this.cal.showNavEvent.fire();
6569
        }
6570
    },
6571
 
6572
    /**
6573
     * Hides the navigator and mask
6574
     *
6575
     * The show method will fire the Calendar/CalendarGroup's beforeHideNav event and hideNav events
6576
     * @method hide
6577
     */
6578
    hide : function() {
6579
        var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
6580
 
6581
        if (this.cal.beforeHideNavEvent.fire()) {
6582
            this._show(this.navEl, false);
6583
            this.hideMask();
6584
            YAHOO.util.Dom.removeClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
6585
            this.cal.hideNavEvent.fire();
6586
        }
6587
    },
6588
 
6589
 
6590
    /**
6591
     * Displays the navigator's mask element
6592
     *
6593
     * @method showMask
6594
     */
6595
    showMask : function() {
6596
        this._show(this.maskEl, true);
6597
        if (this.__isIEQuirks) {
6598
            this._syncMask();
6599
        }
6600
    },
6601
 
6602
    /**
6603
     * Hides the navigator's mask element
6604
     *
6605
     * @method hideMask
6606
     */
6607
    hideMask : function() {
6608
        this._show(this.maskEl, false);
6609
    },
6610
 
6611
    /**
6612
     * Returns the current month set on the navigator
6613
     *
6614
     * Note: This may not be the month set in the UI, if
6615
     * the UI contains an invalid value.
6616
     *
6617
     * @method getMonth
6618
     * @return {Number} The Navigator's current month index
6619
     */
6620
    getMonth: function() {
6621
        return this._month;
6622
    },
6623
 
6624
    /**
6625
     * Returns the current year set on the navigator
6626
     *
6627
     * Note: This may not be the year set in the UI, if
6628
     * the UI contains an invalid value.
6629
     *
6630
     * @method getYear
6631
     * @return {Number} The Navigator's current year value
6632
     */
6633
    getYear: function() {
6634
        return this._year;
6635
    },
6636
 
6637
    /**
6638
     * Sets the current month on the Navigator, and updates the UI
6639
     *
6640
     * @method setMonth
6641
     * @param {Number} nMonth The month index, from 0 (Jan) through 11 (Dec).
6642
     */
6643
    setMonth : function(nMonth) {
6644
        if (nMonth >= 0 && nMonth < 12) {
6645
            this._month = nMonth;
6646
        }
6647
        this._updateMonthUI();
6648
    },
6649
 
6650
    /**
6651
     * Sets the current year on the Navigator, and updates the UI. If the
6652
     * provided year is invalid, it will not be set.
6653
     *
6654
     * @method setYear
6655
     * @param {Number} nYear The full year value to set the Navigator to.
6656
     */
6657
    setYear : function(nYear) {
6658
        var yrPattern = YAHOO.widget.CalendarNavigator.YR_PATTERN;
6659
        if (YAHOO.lang.isNumber(nYear) && yrPattern.test(nYear+"")) {
6660
            this._year = nYear;
6661
        }
6662
        this._updateYearUI();
6663
    },
6664
 
6665
    /**
6666
     * Renders the HTML for the navigator, adding it to the
6667
     * document and attaches event listeners if it has not
6668
     * already been rendered.
6669
     *
6670
     * @method render
6671
     */
6672
    render: function() {
6673
        this.cal.beforeRenderNavEvent.fire();
6674
        if (!this.__rendered) {
6675
            this.createNav();
6676
            this.createMask();
6677
            this.applyListeners();
6678
            this.__rendered = true;
6679
        }
6680
        this.cal.renderNavEvent.fire();
6681
    },
6682
 
6683
    /**
6684
     * Creates the navigator's containing HTMLElement, it's contents, and appends
6685
     * the containg element to the Calendar/CalendarGroup's container.
6686
     *
6687
     * @method createNav
6688
     */
6689
    createNav : function() {
6690
        var NAV = YAHOO.widget.CalendarNavigator;
6691
        var doc = this._doc;
6692
 
6693
        var d = doc.createElement("div");
6694
        d.className = NAV.CLASSES.NAV;
6695
 
6696
        var htmlBuf = this.renderNavContents([]);
6697
 
6698
        d.innerHTML = htmlBuf.join('');
6699
        this.cal.oDomContainer.appendChild(d);
6700
 
6701
        this.navEl = d;
6702
 
6703
        this.yearEl = doc.getElementById(this.id + NAV.YEAR_SUFFIX);
6704
        this.monthEl = doc.getElementById(this.id + NAV.MONTH_SUFFIX);
6705
        this.errorEl = doc.getElementById(this.id + NAV.ERROR_SUFFIX);
6706
        this.submitEl = doc.getElementById(this.id + NAV.SUBMIT_SUFFIX);
6707
        this.cancelEl = doc.getElementById(this.id + NAV.CANCEL_SUFFIX);
6708
 
6709
        if (YAHOO.env.ua.gecko && this.yearEl && this.yearEl.type == "text") {
6710
            // Avoid XUL error on focus, select [ https://bugzilla.mozilla.org/show_bug.cgi?id=236791,
6711
            // supposedly fixed in 1.8.1, but there are reports of it still being around for methods other than blur ]
6712
            this.yearEl.setAttribute("autocomplete", "off");
6713
        }
6714
 
6715
        this._setFirstLastElements();
6716
    },
6717
 
6718
    /**
6719
     * Creates the Mask HTMLElement and appends it to the Calendar/CalendarGroups
6720
     * container.
6721
     *
6722
     * @method createMask
6723
     */
6724
    createMask : function() {
6725
        var C = YAHOO.widget.CalendarNavigator.CLASSES;
6726
 
6727
        var d = this._doc.createElement("div");
6728
        d.className = C.MASK;
6729
 
6730
        this.cal.oDomContainer.appendChild(d);
6731
        this.maskEl = d;
6732
    },
6733
 
6734
    /**
6735
     * Used to set the width/height of the mask in pixels to match the Calendar Container.
6736
     * Currently only used for IE6 or IE in quirks mode. The other A-Grade browser are handled using CSS (width/height 100%).
6737
     * <p>
6738
     * The method is also registered as an HTMLElement resize listener on the Calendars container element.
6739
     * </p>
6740
     * @protected
6741
     * @method _syncMask
6742
     */
6743
    _syncMask : function() {
6744
        var c = this.cal.oDomContainer;
6745
        if (c && this.maskEl) {
6746
            var r = YAHOO.util.Dom.getRegion(c);
6747
            YAHOO.util.Dom.setStyle(this.maskEl, "width", r.right - r.left + "px");
6748
            YAHOO.util.Dom.setStyle(this.maskEl, "height", r.bottom - r.top + "px");
6749
        }
6750
    },
6751
 
6752
    /**
6753
     * Renders the contents of the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
6754
     *
6755
     * @method renderNavContents
6756
     *
6757
     * @param {HTML[]} html The HTML buffer to append the HTML to.
6758
     * @return {HTML[]} A reference to the buffer passed in.
6759
     */
6760
    renderNavContents : function(html) {
6761
        var NAV = YAHOO.widget.CalendarNavigator,
6762
            C = NAV.CLASSES,
6763
            h = html; // just to use a shorter name
6764
 
6765
        h[h.length] = '<div class="' + C.MONTH + '">';
6766
        this.renderMonth(h);
6767
        h[h.length] = '</div>';
6768
        h[h.length] = '<div class="' + C.YEAR + '">';
6769
        this.renderYear(h);
6770
        h[h.length] = '</div>';
6771
        h[h.length] = '<div class="' + C.BUTTONS + '">';
6772
        this.renderButtons(h);
6773
        h[h.length] = '</div>';
6774
        h[h.length] = '<div class="' + C.ERROR + '" id="' + this.id + NAV.ERROR_SUFFIX + '"></div>';
6775
 
6776
        return h;
6777
    },
6778
 
6779
    /**
6780
     * Renders the month label and control for the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
6781
     *
6782
     * @method renderNavContents
6783
     * @param {HTML[]} html The HTML buffer to append the HTML to.
6784
     * @return {HTML[]} A reference to the buffer passed in.
6785
     */
6786
    renderMonth : function(html) {
6787
        var NAV = YAHOO.widget.CalendarNavigator,
6788
            C = NAV.CLASSES;
6789
 
6790
        var id = this.id + NAV.MONTH_SUFFIX,
6791
            mf = this.__getCfg("monthFormat"),
6792
            months = this.cal.cfg.getProperty((mf == YAHOO.widget.Calendar.SHORT) ? "MONTHS_SHORT" : "MONTHS_LONG"),
6793
            h = html;
6794
 
6795
        if (months && months.length > 0) {
6796
            h[h.length] = '<label for="' + id + '">';
6797
            h[h.length] = this.__getCfg("month", true);
6798
            h[h.length] = '</label>';
6799
            h[h.length] = '<select name="' + id + '" id="' + id + '" class="' + C.MONTH_CTRL + '">';
6800
            for (var i = 0; i < months.length; i++) {
6801
                h[h.length] = '<option value="' + i + '">';
6802
                h[h.length] = months[i];
6803
                h[h.length] = '</option>';
6804
            }
6805
            h[h.length] = '</select>';
6806
        }
6807
        return h;
6808
    },
6809
 
6810
    /**
6811
     * Renders the year label and control for the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
6812
     *
6813
     * @method renderYear
6814
     * @param {Array} html The HTML buffer to append the HTML to.
6815
     * @return {Array} A reference to the buffer passed in.
6816
     */
6817
    renderYear : function(html) {
6818
        var NAV = YAHOO.widget.CalendarNavigator,
6819
            C = NAV.CLASSES;
6820
 
6821
        var id = this.id + NAV.YEAR_SUFFIX,
6822
            size = NAV.YR_MAX_DIGITS,
6823
            h = html;
6824
 
6825
        h[h.length] = '<label for="' + id + '">';
6826
        h[h.length] = this.__getCfg("year", true);
6827
        h[h.length] = '</label>';
6828
        h[h.length] = '<input type="text" name="' + id + '" id="' + id + '" class="' + C.YEAR_CTRL + '" maxlength="' + size + '"/>';
6829
        return h;
6830
    },
6831
 
6832
    /**
6833
     * Renders the submit/cancel buttons for the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
6834
     *
6835
     * @method renderButtons
6836
     * @param {Array} html The HTML buffer to append the HTML to.
6837
     * @return {Array} A reference to the buffer passed in.
6838
     */
6839
    renderButtons : function(html) {
6840
        var C = YAHOO.widget.CalendarNavigator.CLASSES;
6841
        var h = html;
6842
 
6843
        h[h.length] = '<span class="' + C.BUTTON + ' ' + C.DEFAULT + '">';
6844
        h[h.length] = '<button type="button" id="' + this.id + '_submit' + '">';
6845
        h[h.length] = this.__getCfg("submit", true);
6846
        h[h.length] = '</button>';
6847
        h[h.length] = '</span>';
6848
        h[h.length] = '<span class="' + C.BUTTON +'">';
6849
        h[h.length] = '<button type="button" id="' + this.id + '_cancel' + '">';
6850
        h[h.length] = this.__getCfg("cancel", true);
6851
        h[h.length] = '</button>';
6852
        h[h.length] = '</span>';
6853
 
6854
        return h;
6855
    },
6856
 
6857
    /**
6858
     * Attaches DOM event listeners to the rendered elements
6859
     * <p>
6860
     * The method will call applyKeyListeners, to setup keyboard specific
6861
     * listeners
6862
     * </p>
6863
     * @method applyListeners
6864
     */
6865
    applyListeners : function() {
6866
        var E = YAHOO.util.Event;
6867
 
6868
        function yearUpdateHandler() {
6869
            if (this.validate()) {
6870
                this.setYear(this._getYearFromUI());
6871
            }
6872
        }
6873
 
6874
        function monthUpdateHandler() {
6875
            this.setMonth(this._getMonthFromUI());
6876
        }
6877
 
6878
        E.on(this.submitEl, "click", this.submit, this, true);
6879
        E.on(this.cancelEl, "click", this.cancel, this, true);
6880
        E.on(this.yearEl, "blur", yearUpdateHandler, this, true);
6881
        E.on(this.monthEl, "change", monthUpdateHandler, this, true);
6882
 
6883
        if (this.__isIEQuirks) {
6884
            YAHOO.util.Event.on(this.cal.oDomContainer, "resize", this._syncMask, this, true);
6885
        }
6886
 
6887
        this.applyKeyListeners();
6888
    },
6889
 
6890
    /**
6891
     * Removes/purges DOM event listeners from the rendered elements
6892
     *
6893
     * @method purgeListeners
6894
     */
6895
    purgeListeners : function() {
6896
        var E = YAHOO.util.Event;
6897
        E.removeListener(this.submitEl, "click", this.submit);
6898
        E.removeListener(this.cancelEl, "click", this.cancel);
6899
        E.removeListener(this.yearEl, "blur");
6900
        E.removeListener(this.monthEl, "change");
6901
        if (this.__isIEQuirks) {
6902
            E.removeListener(this.cal.oDomContainer, "resize", this._syncMask);
6903
        }
6904
 
6905
        this.purgeKeyListeners();
6906
    },
6907
 
6908
    /**
6909
     * Attaches DOM listeners for keyboard support.
6910
     * Tab/Shift-Tab looping, Enter Key Submit on Year element,
6911
     * Up/Down/PgUp/PgDown year increment on Year element
6912
     * <p>
6913
     * NOTE: MacOSX Safari 2.x doesn't let you tab to buttons and
6914
     * MacOSX Gecko does not let you tab to buttons or select controls,
6915
     * so for these browsers, Tab/Shift-Tab looping is limited to the
6916
     * elements which can be reached using the tab key.
6917
     * </p>
6918
     * @method applyKeyListeners
6919
     */
6920
    applyKeyListeners : function() {
6921
        var E = YAHOO.util.Event,
6922
            ua = YAHOO.env.ua;
6923
 
6924
        // IE/Safari 3.1 doesn't fire keypress for arrow/pg keys (non-char keys)
6925
        var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
6926
 
6927
        // - IE/Safari 3.1 doesn't fire keypress for non-char keys
6928
        // - Opera doesn't allow us to cancel keydown or keypress for tab, but
6929
        //   changes focus successfully on keydown (keypress is too late to change focus - opera's already moved on).
6930
        var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
6931
 
6932
        // Everyone likes keypress for Enter (char keys) - whoo hoo!
6933
        E.on(this.yearEl, "keypress", this._handleEnterKey, this, true);
6934
 
6935
        E.on(this.yearEl, arrowEvt, this._handleDirectionKeys, this, true);
6936
        E.on(this.lastCtrl, tabEvt, this._handleTabKey, this, true);
6937
        E.on(this.firstCtrl, tabEvt, this._handleShiftTabKey, this, true);
6938
    },
6939
 
6940
    /**
6941
     * Removes/purges DOM listeners for keyboard support
6942
     *
6943
     * @method purgeKeyListeners
6944
     */
6945
    purgeKeyListeners : function() {
6946
        var E = YAHOO.util.Event,
6947
            ua = YAHOO.env.ua;
6948
 
6949
        var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
6950
        var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
6951
 
6952
        E.removeListener(this.yearEl, "keypress", this._handleEnterKey);
6953
        E.removeListener(this.yearEl, arrowEvt, this._handleDirectionKeys);
6954
        E.removeListener(this.lastCtrl, tabEvt, this._handleTabKey);
6955
        E.removeListener(this.firstCtrl, tabEvt, this._handleShiftTabKey);
6956
    },
6957
 
6958
    /**
6959
     * Updates the Calendar/CalendarGroup's pagedate with the currently set month and year if valid.
6960
     * <p>
6961
     * If the currently set month/year is invalid, a validation error will be displayed and the
6962
     * Calendar/CalendarGroup's pagedate will not be updated.
6963
     * </p>
6964
     * @method submit
6965
     */
6966
    submit : function() {
6967
        if (this.validate()) {
6968
            this.hide();
6969
 
6970
            this.setMonth(this._getMonthFromUI());
6971
            this.setYear(this._getYearFromUI());
6972
 
6973
            var cal = this.cal;
6974
 
6975
            // Artificial delay, just to help the user see something changed
6976
            var delay = YAHOO.widget.CalendarNavigator.UPDATE_DELAY;
6977
            if (delay > 0) {
6978
                var nav = this;
6979
                window.setTimeout(function(){ nav._update(cal); }, delay);
6980
            } else {
6981
                this._update(cal);
6982
            }
6983
        }
6984
    },
6985
 
6986
    /**
6987
     * Updates the Calendar rendered state, based on the state of the CalendarNavigator
6988
     * @method _update
6989
     * @param cal The Calendar instance to update
6990
     * @protected
6991
     */
6992
    _update : function(cal) {
6993
        var date = YAHOO.widget.DateMath.getDate(this.getYear() - cal.cfg.getProperty("YEAR_OFFSET"), this.getMonth(), 1);
6994
        cal.cfg.setProperty("pagedate", date);
6995
        cal.render();
6996
    },
6997
 
6998
    /**
6999
     * Hides the navigator and mask, without updating the Calendar/CalendarGroup's state
7000
     *
7001
     * @method cancel
7002
     */
7003
    cancel : function() {
7004
        this.hide();
7005
    },
7006
 
7007
    /**
7008
     * Validates the current state of the UI controls
7009
     *
7010
     * @method validate
7011
     * @return {Boolean} true, if the current UI state contains valid values, false if not
7012
     */
7013
    validate : function() {
7014
        if (this._getYearFromUI() !== null) {
7015
            this.clearErrors();
7016
            return true;
7017
        } else {
7018
            this.setYearError();
7019
            this.setError(this.__getCfg("invalidYear", true));
7020
            return false;
7021
        }
7022
    },
7023
 
7024
    /**
7025
     * Displays an error message in the Navigator's error panel.
7026
     *
7027
     * @method setError
7028
     * @param {HTML} msg The markup for the error message to display. NOTE: The msg passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
7029
     */
7030
    setError : function(msg) {
7031
        if (this.errorEl) {
7032
            this.errorEl.innerHTML = msg;
7033
            this._show(this.errorEl, true);
7034
        }
7035
    },
7036
 
7037
    /**
7038
     * Clears the navigator's error message and hides the error panel
7039
     * @method clearError
7040
     */
7041
    clearError : function() {
7042
        if (this.errorEl) {
7043
            this.errorEl.innerHTML = "";
7044
            this._show(this.errorEl, false);
7045
        }
7046
    },
7047
 
7048
    /**
7049
     * Displays the validation error UI for the year control
7050
     * @method setYearError
7051
     */
7052
    setYearError : function() {
7053
        YAHOO.util.Dom.addClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
7054
    },
7055
 
7056
    /**
7057
     * Removes the validation error UI for the year control
7058
     * @method clearYearError
7059
     */
7060
    clearYearError : function() {
7061
        YAHOO.util.Dom.removeClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
7062
    },
7063
 
7064
    /**
7065
     * Clears all validation and error messages in the UI
7066
     * @method clearErrors
7067
     */
7068
    clearErrors : function() {
7069
        this.clearError();
7070
        this.clearYearError();
7071
    },
7072
 
7073
    /**
7074
     * Sets the initial focus, based on the configured value
7075
     * @method setInitialFocus
7076
     */
7077
    setInitialFocus : function() {
7078
        var el = this.submitEl,
7079
            f = this.__getCfg("initialFocus");
7080
 
7081
        if (f && f.toLowerCase) {
7082
            f = f.toLowerCase();
7083
            if (f == "year") {
7084
                el = this.yearEl;
7085
                try {
7086
                    this.yearEl.select();
7087
                } catch (selErr) {
7088
                    // Ignore;
7089
                }
7090
            } else if (f == "month") {
7091
                el = this.monthEl;
7092
            }
7093
        }
7094
 
7095
        if (el && YAHOO.lang.isFunction(el.focus)) {
7096
            try {
7097
                el.focus();
7098
            } catch (focusErr) {
7099
                // TODO: Fall back if focus fails?
7100
            }
7101
        }
7102
    },
7103
 
7104
    /**
7105
     * Removes all renderered HTML elements for the Navigator from
7106
     * the DOM, purges event listeners and clears (nulls) any property
7107
     * references to HTML references
7108
     * @method erase
7109
     */
7110
    erase : function() {
7111
        if (this.__rendered) {
7112
            this.purgeListeners();
7113
 
7114
            // Clear out innerHTML references
7115
            this.yearEl = null;
7116
            this.monthEl = null;
7117
            this.errorEl = null;
7118
            this.submitEl = null;
7119
            this.cancelEl = null;
7120
            this.firstCtrl = null;
7121
            this.lastCtrl = null;
7122
            if (this.navEl) {
7123
                this.navEl.innerHTML = "";
7124
            }
7125
 
7126
            var p = this.navEl.parentNode;
7127
            if (p) {
7128
                p.removeChild(this.navEl);
7129
            }
7130
            this.navEl = null;
7131
 
7132
            var pm = this.maskEl.parentNode;
7133
            if (pm) {
7134
                pm.removeChild(this.maskEl);
7135
            }
7136
            this.maskEl = null;
7137
            this.__rendered = false;
7138
        }
7139
    },
7140
 
7141
    /**
7142
     * Destroys the Navigator object and any HTML references
7143
     * @method destroy
7144
     */
7145
    destroy : function() {
7146
        this.erase();
7147
        this._doc = null;
7148
        this.cal = null;
7149
        this.id = null;
7150
    },
7151
 
7152
    /**
7153
     * Protected implementation to handle how UI elements are
7154
     * hidden/shown.
7155
     *
7156
     * @method _show
7157
     * @protected
7158
     */
7159
    _show : function(el, bShow) {
7160
        if (el) {
7161
            YAHOO.util.Dom.setStyle(el, "display", (bShow) ? "block" : "none");
7162
        }
7163
    },
7164
 
7165
    /**
7166
     * Returns the month value (index), from the month UI element
7167
     * @protected
7168
     * @method _getMonthFromUI
7169
     * @return {Number} The month index, or 0 if a UI element for the month
7170
     * is not found
7171
     */
7172
    _getMonthFromUI : function() {
7173
        if (this.monthEl) {
7174
            return this.monthEl.selectedIndex;
7175
        } else {
7176
            return 0; // Default to Jan
7177
        }
7178
    },
7179
 
7180
    /**
7181
     * Returns the year value, from the Navitator's year UI element
7182
     * @protected
7183
     * @method _getYearFromUI
7184
     * @return {Number} The year value set in the UI, if valid. null is returned if
7185
     * the UI does not contain a valid year value.
7186
     */
7187
    _getYearFromUI : function() {
7188
        var NAV = YAHOO.widget.CalendarNavigator;
7189
 
7190
        var yr = null;
7191
        if (this.yearEl) {
7192
            var value = this.yearEl.value;
7193
            value = value.replace(NAV.TRIM, "$1");
7194
 
7195
            if (NAV.YR_PATTERN.test(value)) {
7196
                yr = parseInt(value, 10);
7197
            }
7198
        }
7199
        return yr;
7200
    },
7201
 
7202
    /**
7203
     * Updates the Navigator's year UI, based on the year value set on the Navigator object
7204
     * @protected
7205
     * @method _updateYearUI
7206
     */
7207
    _updateYearUI : function() {
7208
        if (this.yearEl && this._year !== null) {
7209
            this.yearEl.value = this._year;
7210
        }
7211
    },
7212
 
7213
    /**
7214
     * Updates the Navigator's month UI, based on the month value set on the Navigator object
7215
     * @protected
7216
     * @method _updateMonthUI
7217
     */
7218
    _updateMonthUI : function() {
7219
        if (this.monthEl) {
7220
            this.monthEl.selectedIndex = this._month;
7221
        }
7222
    },
7223
 
7224
    /**
7225
     * Sets up references to the first and last focusable element in the Navigator's UI
7226
     * in terms of tab order (Naviagator's firstEl and lastEl properties). The references
7227
     * are used to control modality by looping around from the first to the last control
7228
     * and visa versa for tab/shift-tab navigation.
7229
     * <p>
7230
     * See <a href="#applyKeyListeners">applyKeyListeners</a>
7231
     * </p>
7232
     * @protected
7233
     * @method _setFirstLastElements
7234
     */
7235
    _setFirstLastElements : function() {
7236
        this.firstCtrl = this.monthEl;
7237
        this.lastCtrl = this.cancelEl;
7238
 
7239
        // Special handling for MacOSX.
7240
        // - Safari 2.x can't focus on buttons
7241
        // - Gecko can't focus on select boxes or buttons
7242
        if (this.__isMac) {
7243
            if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420){
7244
                this.firstCtrl = this.monthEl;
7245
                this.lastCtrl = this.yearEl;
7246
            }
7247
            if (YAHOO.env.ua.gecko) {
7248
                this.firstCtrl = this.yearEl;
7249
                this.lastCtrl = this.yearEl;
7250
            }
7251
        }
7252
    },
7253
 
7254
    /**
7255
     * Default Keyboard event handler to capture Enter
7256
     * on the Navigator's year control (yearEl)
7257
     *
7258
     * @method _handleEnterKey
7259
     * @protected
7260
     * @param {Event} e The DOM event being handled
7261
     */
7262
    _handleEnterKey : function(e) {
7263
        var KEYS = YAHOO.util.KeyListener.KEY;
7264
 
7265
        if (YAHOO.util.Event.getCharCode(e) == KEYS.ENTER) {
7266
            YAHOO.util.Event.preventDefault(e);
7267
            this.submit();
7268
        }
7269
    },
7270
 
7271
    /**
7272
     * Default Keyboard event handler to capture up/down/pgup/pgdown
7273
     * on the Navigator's year control (yearEl).
7274
     *
7275
     * @method _handleDirectionKeys
7276
     * @protected
7277
     * @param {Event} e The DOM event being handled
7278
     */
7279
    _handleDirectionKeys : function(e) {
7280
        var E = YAHOO.util.Event,
7281
            KEYS = YAHOO.util.KeyListener.KEY,
7282
            NAV = YAHOO.widget.CalendarNavigator;
7283
 
7284
        var value = (this.yearEl.value) ? parseInt(this.yearEl.value, 10) : null;
7285
        if (isFinite(value)) {
7286
            var dir = false;
7287
            switch(E.getCharCode(e)) {
7288
                case KEYS.UP:
7289
                    this.yearEl.value = value + NAV.YR_MINOR_INC;
7290
                    dir = true;
7291
                    break;
7292
                case KEYS.DOWN:
7293
                    this.yearEl.value = Math.max(value - NAV.YR_MINOR_INC, 0);
7294
                    dir = true;
7295
                    break;
7296
                case KEYS.PAGE_UP:
7297
                    this.yearEl.value = value + NAV.YR_MAJOR_INC;
7298
                    dir = true;
7299
                    break;
7300
                case KEYS.PAGE_DOWN:
7301
                    this.yearEl.value = Math.max(value - NAV.YR_MAJOR_INC, 0);
7302
                    dir = true;
7303
                    break;
7304
                default:
7305
                    break;
7306
            }
7307
            if (dir) {
7308
                E.preventDefault(e);
7309
                try {
7310
                    this.yearEl.select();
7311
                } catch(err) {
7312
                    // Ignore
7313
                }
7314
            }
7315
        }
7316
    },
7317
 
7318
    /**
7319
     * Default Keyboard event handler to capture Tab
7320
     * on the last control (lastCtrl) in the Navigator.
7321
     *
7322
     * @method _handleTabKey
7323
     * @protected
7324
     * @param {Event} e The DOM event being handled
7325
     */
7326
    _handleTabKey : function(e) {
7327
        var E = YAHOO.util.Event,
7328
            KEYS = YAHOO.util.KeyListener.KEY;
7329
 
7330
        if (E.getCharCode(e) == KEYS.TAB && !e.shiftKey) {
7331
            try {
7332
                E.preventDefault(e);
7333
                this.firstCtrl.focus();
7334
            } catch (err) {
7335
                // Ignore - mainly for focus edge cases
7336
            }
7337
        }
7338
    },
7339
 
7340
    /**
7341
     * Default Keyboard event handler to capture Shift-Tab
7342
     * on the first control (firstCtrl) in the Navigator.
7343
     *
7344
     * @method _handleShiftTabKey
7345
     * @protected
7346
     * @param {Event} e The DOM event being handled
7347
     */
7348
    _handleShiftTabKey : function(e) {
7349
        var E = YAHOO.util.Event,
7350
            KEYS = YAHOO.util.KeyListener.KEY;
7351
 
7352
        if (e.shiftKey && E.getCharCode(e) == KEYS.TAB) {
7353
            try {
7354
                E.preventDefault(e);
7355
                this.lastCtrl.focus();
7356
            } catch (err) {
7357
                // Ignore - mainly for focus edge cases
7358
            }
7359
        }
7360
    },
7361
 
7362
    /**
7363
     * Retrieve Navigator configuration values from
7364
     * the parent Calendar/CalendarGroup's config value.
7365
     * <p>
7366
     * If it has not been set in the user provided configuration, the method will
7367
     * return the default value of the configuration property, as set in DEFAULT_CONFIG
7368
     * </p>
7369
     * @private
7370
     * @method __getCfg
7371
     * @param {String} Case sensitive property name.
7372
     * @param {Boolean} true, if the property is a string property, false if not.
7373
     * @return The value of the configuration property
7374
     */
7375
    __getCfg : function(prop, bIsStr) {
7376
        var DEF_CFG = YAHOO.widget.CalendarNavigator.DEFAULT_CONFIG;
7377
        var cfg = this.cal.cfg.getProperty("navigator");
7378
 
7379
        if (bIsStr) {
7380
            return (cfg !== true && cfg.strings && cfg.strings[prop]) ? cfg.strings[prop] : DEF_CFG.strings[prop];
7381
        } else {
7382
            return (cfg !== true && cfg[prop]) ? cfg[prop] : DEF_CFG[prop];
7383
        }
7384
    },
7385
 
7386
    /**
7387
     * Private flag, to identify MacOS
7388
     * @private
7389
     * @property __isMac
7390
     */
7391
    __isMac : (navigator.userAgent.toLowerCase().indexOf("macintosh") != -1)
7392
 
7393
};
7394
YAHOO.register("calendar", YAHOO.widget.Calendar, {version: "2.9.0", build: "2800"});
7395
 
7396
}, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom", "yui2-event", "yui2-skin-sam-calendar"], "supersedes": ["yui2-datemath"]});