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