Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('calendar-base', function (Y, NAME) {
2
 
3
/**
4
 * The CalendarBase submodule is a basic UI calendar view that displays
5
 * a range of dates in a two-dimensional month grid, with one or more
6
 * months visible at a single time. CalendarBase supports custom date
7
 * rendering, multiple calendar panes, and selection.
8
 * @module calendar
9
 * @submodule calendar-base
10
 */
11
 
12
var getCN                 = Y.ClassNameManager.getClassName,
13
    CALENDAR              = 'calendar',
14
    CAL_GRID              = getCN(CALENDAR, 'grid'),
15
    CAL_LEFT_GRID         = getCN(CALENDAR, 'left-grid'),
16
    CAL_RIGHT_GRID        = getCN(CALENDAR, 'right-grid'),
17
    CAL_BODY              = getCN(CALENDAR, 'body'),
18
    CAL_HD                = getCN(CALENDAR, 'header'),
19
    CAL_HD_LABEL          = getCN(CALENDAR, 'header-label'),
20
    CAL_WDAYROW           = getCN(CALENDAR, 'weekdayrow'),
21
    CAL_WDAY              = getCN(CALENDAR, 'weekday'),
22
    CAL_COL_HIDDEN        = getCN(CALENDAR, 'column-hidden'),
23
    CAL_DAY_SELECTED      = getCN(CALENDAR, 'day-selected'),
24
    SELECTION_DISABLED    = getCN(CALENDAR, 'selection-disabled'),
25
    CAL_ROW               = getCN(CALENDAR, 'row'),
26
    CAL_DAY               = getCN(CALENDAR, 'day'),
27
    CAL_PREVMONTH_DAY     = getCN(CALENDAR, 'prevmonth-day'),
28
    CAL_NEXTMONTH_DAY     = getCN(CALENDAR, 'nextmonth-day'),
29
    CAL_ANCHOR            = getCN(CALENDAR, 'anchor'),
30
    CAL_PANE              = getCN(CALENDAR, 'pane'),
31
    CAL_STATUS            = getCN(CALENDAR, 'status'),
32
    L           = Y.Lang,
33
    substitute  = L.sub,
34
    arrayEach   = Y.Array.each,
35
    objEach     = Y.Object.each,
36
    iOf         = Y.Array.indexOf,
37
    hasKey      = Y.Object.hasKey,
38
    setVal      = Y.Object.setValue,
39
    isEmpty     = Y.Object.isEmpty,
40
    ydate       = Y.DataType.Date;
41
 
42
/** Create a calendar view to represent a single or multiple
43
    * month range of dates, rendered as a grid with date and
44
    * weekday labels.
45
    *
46
    * @class CalendarBase
47
    * @extends Widget
48
    * @param config {Object} Configuration object (see Configuration
49
    * attributes)
50
    * @constructor
51
    */
52
function CalendarBase() {
53
    CalendarBase.superclass.constructor.apply ( this, arguments );
54
}
55
 
56
 
57
 
58
Y.CalendarBase = Y.extend( CalendarBase, Y.Widget, {
59
 
60
    /**
61
     * A storage for various properties of individual month
62
     * panes.
63
     *
64
     * @property _paneProperties
65
     * @type Object
66
     * @private
67
     */
68
    _paneProperties : {},
69
 
70
    /**
71
     * The number of month panes in the calendar, deduced
72
     * from the CONTENT_TEMPLATE's number of {calendar_grid}
73
     * tokens.
74
     *
75
     * @property _paneNumber
76
     * @type Number
77
     * @private
78
     */
79
    _paneNumber : 1,
80
 
81
    /**
82
     * The unique id used to prefix various elements of this
83
     * calendar instance.
84
     *
85
     * @property _calendarId
86
     * @type String
87
     * @private
88
     */
89
    _calendarId : null,
90
 
91
    /**
92
     * The hash map of selected dates, populated with
93
     * selectDates() and deselectDates() methods
94
     *
95
     * @property _selectedDates
96
     * @type Object
97
     * @private
98
     */
99
    _selectedDates : {},
100
 
101
    /**
102
     * A private copy of the rules object, populated
103
     * by setting the customRenderer attribute.
104
     *
105
     * @property _rules
106
     * @type Object
107
     * @private
108
     */
109
    _rules : {},
110
 
111
    /**
112
     * A private copy of the filterFunction, populated
113
     * by setting the customRenderer attribute.
114
     *
115
     * @property _filterFunction
116
     * @type Function
117
     * @private
118
     */
119
    _filterFunction : null,
120
 
121
    /**
122
     * Storage for calendar cells modified by any custom
123
     * formatting. The storage is cleared, used to restore
124
     * cells to the original state, and repopulated accordingly
125
     * when the calendar is rerendered.
126
     *
127
     * @property _storedDateCells
128
     * @type Object
129
     * @private
130
     */
131
    _storedDateCells : {},
132
 
133
    /**
134
     * Designated initializer
135
     * Initializes instance-level properties of
136
     * calendar.
137
     *
138
     * @method initializer
139
     */
140
    initializer : function () {
141
        this._paneProperties = {};
142
        this._calendarId = Y.guid('calendar');
143
        this._selectedDates = {};
144
        if (isEmpty(this._rules)) {
145
             this._rules = {};
146
        }
147
        this._storedDateCells = {};
148
    },
149
 
150
    /**
151
     * renderUI implementation
152
     *
153
     * Creates a visual representation of the calendar based on existing parameters.
154
     * @method renderUI
155
     */
156
    renderUI : function () {
157
 
158
        var contentBox = this.get('contentBox');
159
        contentBox.appendChild(this._initCalendarHTML(this.get('date')));
160
 
161
        if (this.get('showPrevMonth')) {
162
                this._afterShowPrevMonthChange();
163
        }
164
        if (this.get('showNextMonth')) {
165
                this._afterShowNextMonthChange();
166
        }
167
 
168
        this._renderCustomRules();
169
        this._renderSelectedDates();
170
 
171
        this.get("boundingBox").setAttribute("aria-labelledby", this._calendarId + "_header");
172
 
173
    },
174
 
175
    /**
176
     * bindUI implementation
177
     *
178
     * Assigns listeners to relevant events that change the state
179
     * of the calendar.
180
     * @method bindUI
181
     */
182
    bindUI : function () {
183
        this.after('dateChange', this._afterDateChange);
184
        this.after('showPrevMonthChange', this._afterShowPrevMonthChange);
185
        this.after('showNextMonthChange', this._afterShowNextMonthChange);
186
        this.after('headerRendererChange', this._afterHeaderRendererChange);
187
        this.after('customRendererChange', this._afterCustomRendererChange);
188
        this.after('enabledDatesRuleChange', this._afterCustomRendererChange);
189
        this.after('disabledDatesRuleChange', this._afterCustomRendererChange);
190
        this.after('focusedChange', this._afterFocusedChange);
191
        this.after('selectionChange', this._renderSelectedDates);
192
        this._bindCalendarEvents();
193
    },
194
 
195
 
196
    /**
197
     * An internal utility method that generates a list of selected dates
198
     * from the hash storage.
199
     *
200
     * @method _getSelectedDatesList
201
     * @protected
202
     * @return {Array} The array of `Date`s that are currently selected.
203
     */
204
    _getSelectedDatesList : function () {
205
        var output = [];
206
 
207
        objEach (this._selectedDates, function (year) {
208
            objEach (year, function (month) {
209
                objEach (month, function (day) {
210
                    output.push (day);
211
                }, this);
212
            }, this);
213
        }, this);
214
 
215
        return output;
216
    },
217
 
218
    /**
219
     * A utility method that returns all dates selected in a specific month.
220
     *
221
     * @method _getSelectedDatesInMonth
222
     * @param {Date} oDate corresponding to the month for which selected dates
223
     * are requested.
224
     * @protected
225
     * @return {Array} The array of `Date`s in a given month that are currently selected.
226
     */
227
    _getSelectedDatesInMonth : function (oDate) {
228
        var year = oDate.getFullYear(),
229
            month = oDate.getMonth();
230
 
231
        if (hasKey(this._selectedDates, year) && hasKey(this._selectedDates[year], month)) {
232
            return Y.Object.values(this._selectedDates[year][month]);
233
        } else {
234
            return [];
235
        }
236
    },
237
 
238
 
239
    /**
240
     * An internal parsing method that receives a String list of numbers
241
     * and number ranges (of the form "1,2,3,4-6,7-9,10,11" etc.) and checks
242
     * whether a specific number is included in this list. Used for looking
243
     * up dates in the customRenderer rule set.
244
     *
245
     * @method _isNumInList
246
     * @param {Number} num The number to look for in a list.
247
     * @param {String} strList The list of numbers of the form "1,2,3,4-6,7-8,9", etc.
248
     * @private
249
     * @return {boolean} Returns true if the given number is in the given list.
250
     */
251
    _isNumInList : function (num, strList) {
252
        if (strList === "all") {
253
            return true;
254
        } else {
255
            var elements = strList.split(","),
256
                i = elements.length,
257
                range;
258
 
259
            while (i--) {
260
                range = elements[i].split("-");
261
                if (range.length === 2 && num >= parseInt(range[0], 10) && num <= parseInt(range[1], 10)) {
262
                    return true;
263
                }
264
                else if (range.length === 1 && (parseInt(elements[i], 10) === num)) {
265
                    return true;
266
                }
267
            }
268
            return false;
269
        }
270
    },
271
 
272
    /**
273
     * Given a specific date, returns an array of rules (from the customRenderer rule set)
274
     * that the given date matches.
275
     *
276
     * @method _getRulesForDate
277
     * @param {Date} oDate The date for which an array of rules is needed
278
     * @private
279
     * @return {Array} Returns an array of `String`s, each containg the name of
280
     * a rule that the given date matches.
281
     */
282
    _getRulesForDate : function (oDate) {
283
        var year = oDate.getFullYear(),
284
                month = oDate.getMonth(),
285
                date = oDate.getDate(),
286
                wday = oDate.getDay(),
287
                rules = this._rules,
288
                outputRules = [],
289
                years, months, dates, days;
290
 
291
        for (years in rules) {
292
            if (this._isNumInList(year, years)) {
293
                if (L.isString(rules[years])) {
294
                        outputRules.push(rules[years]);
295
                }
296
                else {
297
                    for (months in rules[years]) {
298
                        if (this._isNumInList(month, months)) {
299
                            if (L.isString(rules[years][months])) {
300
                                    outputRules.push(rules[years][months]);
301
                            }
302
                            else {
303
                                for (dates in rules[years][months]) {
304
                                    if (this._isNumInList(date, dates)) {
305
                                        if (L.isString(rules[years][months][dates])) {
306
                                                outputRules.push(rules[years][months][dates]);
307
                                        }
308
                                        else {
309
                                            for (days in rules[years][months][dates]) {
310
                                                if (this._isNumInList(wday, days)) {
311
                                                    if (L.isString(rules[years][months][dates][days])) {
312
                                                        outputRules.push(rules[years][months][dates][days]);
313
                                                    }
314
                                                }
315
                                            }
316
                                        }
317
                                    }
318
                                }
319
                            }
320
                        }
321
                    }
322
                }
323
            }
324
        }
325
        return outputRules;
326
    },
327
 
328
    /**
329
     * A utility method which, given a specific date and a name of the rule,
330
     * checks whether the date matches the given rule.
331
     *
332
     * @method _matchesRule
333
     * @param {Date} oDate The date to check
334
     * @param {String} rule The name of the rule that the date should match.
335
     * @private
336
     * @return {boolean} Returns true if the date matches the given rule.
337
     *
338
     */
339
    _matchesRule : function (oDate, rule) {
340
        return (iOf(this._getRulesForDate(oDate), rule) >= 0);
341
    },
342
 
343
    /**
344
     * A utility method which checks whether a given date matches the `enabledDatesRule`
345
     * or does not match the `disabledDatesRule` and therefore whether it can be selected.
346
     * @method _canBeSelected
347
     * @param {Date} oDate The date to check
348
     * @private
349
     * @return {boolean} Returns true if the date can be selected; false otherwise.
350
     */
351
    _canBeSelected : function (oDate) {
352
 
353
        var enabledDatesRule = this.get("enabledDatesRule"),
354
            disabledDatesRule = this.get("disabledDatesRule");
355
 
356
        if (enabledDatesRule) {
357
            return this._matchesRule(oDate, enabledDatesRule);
358
        } else if (disabledDatesRule) {
359
            return !this._matchesRule(oDate, disabledDatesRule);
360
        } else {
361
            return true;
362
        }
363
    },
364
 
365
    /**
366
     * Selects a given date or array of dates.
367
     * @method selectDates
368
     * @param {Date|Array} dates A `Date` or `Array` of `Date`s.
369
     * @return {CalendarBase} A reference to this object
370
     * @chainable
371
     */
372
    selectDates : function (dates) {
373
        if (ydate.isValidDate(dates)) {
374
            this._addDateToSelection(dates);
375
        }
376
        else if (L.isArray(dates)) {
377
            this._addDatesToSelection(dates);
378
        }
379
        return this;
380
    },
381
 
382
    /**
383
     * Deselects a given date or array of dates, or deselects
384
     * all dates if no argument is specified.
385
     * @method deselectDates
386
     * @param {Date|Array} [dates] A `Date` or `Array` of `Date`s, or no
387
     * argument if all dates should be deselected.
388
     * @return {CalendarBase} A reference to this object
389
     * @chainable
390
     */
391
    deselectDates : function (dates) {
392
        if (!dates) {
393
            this._clearSelection();
394
        }
395
        else if (ydate.isValidDate(dates)) {
396
            this._removeDateFromSelection(dates);
397
        }
398
        else if (L.isArray(dates)) {
399
            this._removeDatesFromSelection(dates);
400
        }
401
        return this;
402
    },
403
 
404
    /**
405
     * A utility method that adds a given date to selection..
406
     * @method _addDateToSelection
407
     * @param {Date} oDate The date to add to selection.
408
     * @param {Number} [index] An optional parameter that is used
409
     * to differentiate between individual date selections and multiple
410
     * date selections.
411
     * @private
412
     */
413
    _addDateToSelection : function (oDate, index) {
414
        oDate = this._normalizeTime(oDate);
415
 
416
        if (this._canBeSelected(oDate)) {
417
 
418
            var year = oDate.getFullYear(),
419
                month = oDate.getMonth(),
420
                day = oDate.getDate();
421
 
422
            if (hasKey(this._selectedDates, year)) {
423
                if (hasKey(this._selectedDates[year], month)) {
424
                    this._selectedDates[year][month][day] = oDate;
425
                } else {
426
                    this._selectedDates[year][month] = {};
427
                    this._selectedDates[year][month][day] = oDate;
428
                }
429
            } else {
430
                this._selectedDates[year] = {};
431
                this._selectedDates[year][month] = {};
432
                this._selectedDates[year][month][day] = oDate;
433
            }
434
 
435
            this._selectedDates = setVal(this._selectedDates, [year, month, day], oDate);
436
 
437
            if (!index) {
438
                this._fireSelectionChange();
439
            }
440
        }
441
    },
442
 
443
    /**
444
     * A utility method that adds a given list of dates to selection.
445
     * @method _addDatesToSelection
446
     * @param {Array} datesArray The list of dates to add to selection.
447
     * @private
448
     */
449
    _addDatesToSelection : function (datesArray) {
450
        arrayEach(datesArray, this._addDateToSelection, this);
451
        this._fireSelectionChange();
452
    },
453
 
454
    /**
455
     * A utility method that adds a given range of dates to selection.
456
     * @method _addDateRangeToSelection
457
     * @param {Date} startDate The first date of the given range.
458
     * @param {Date} endDate The last date of the given range.
459
     * @private
460
     */
461
    _addDateRangeToSelection : function (startDate, endDate) {
462
 
463
        var timezoneDifference = (endDate.getTimezoneOffset() - startDate.getTimezoneOffset())*60000,
464
            startTime = startDate.getTime(),
465
            endTime   = endDate.getTime(),
466
            tempTime,
467
            time,
468
            addedDate;
469
 
470
        if (startTime > endTime) {
471
            tempTime = startTime;
472
            startTime = endTime;
473
            endTime = tempTime + timezoneDifference;
474
        } else {
475
            endTime = endTime - timezoneDifference;
476
        }
477
 
478
 
479
        for (time = startTime; time <= endTime; time += 86400000) {
480
            addedDate = new Date(time);
481
            addedDate.setHours(12);
482
            this._addDateToSelection(addedDate, time);
483
        }
484
        this._fireSelectionChange();
485
    },
486
 
487
    /**
488
     * A utility method that removes a given date from selection..
489
     * @method _removeDateFromSelection
490
     * @param {Date} oDate The date to remove from selection.
491
     * @param {Number} [index] An optional parameter that is used
492
     * to differentiate between individual date selections and multiple
493
     * date selections.
494
     * @private
495
     */
496
    _removeDateFromSelection : function (oDate, index) {
497
        var year = oDate.getFullYear(),
498
            month = oDate.getMonth(),
499
            day = oDate.getDate();
500
 
501
        if (hasKey(this._selectedDates, year) &&
502
            hasKey(this._selectedDates[year], month) &&
503
            hasKey(this._selectedDates[year][month], day)
504
        ) {
505
            delete this._selectedDates[year][month][day];
506
            if (!index) {
507
                this._fireSelectionChange();
508
            }
509
        }
510
    },
511
 
512
    /**
513
     * A utility method that removes a given list of dates from selection.
514
     * @method _removeDatesFromSelection
515
     * @param {Array} datesArray The list of dates to remove from selection.
516
     * @private
517
     */
518
    _removeDatesFromSelection : function (datesArray) {
519
        arrayEach(datesArray, this._removeDateFromSelection, this);
520
        this._fireSelectionChange();
521
    },
522
 
523
    /**
524
     * A utility method that removes a given range of dates from selection.
525
     * @method _removeDateRangeFromSelection
526
     * @param {Date} startDate The first date of the given range.
527
     * @param {Date} endDate The last date of the given range.
528
     * @private
529
     */
530
    _removeDateRangeFromSelection : function (startDate, endDate) {
531
        var startTime = startDate.getTime(),
532
            endTime   = endDate.getTime(),
533
            time;
534
 
535
        for (time = startTime; time <= endTime; time += 86400000) {
536
            this._removeDateFromSelection(new Date(time), time);
537
        }
538
 
539
        this._fireSelectionChange();
540
    },
541
 
542
    /**
543
     * A utility method that removes all dates from selection.
544
     * @method _clearSelection
545
     * @param {boolean} noevent A Boolean specifying whether a selectionChange
546
     * event should be fired. If true, the event is not fired.
547
     * @private
548
     */
549
    _clearSelection : function (noevent) {
550
        this._selectedDates = {};
551
        this.get("contentBox").all("." + CAL_DAY_SELECTED).removeClass(CAL_DAY_SELECTED).setAttribute("aria-selected", false);
552
        if (!noevent) {
553
            this._fireSelectionChange();
554
        }
555
    },
556
 
557
    /**
558
     * A utility method that fires a selectionChange event.
559
     * @method _fireSelectionChange
560
     * @private
561
     */
562
    _fireSelectionChange : function () {
563
 
564
        /**
565
        * Fired when the set of selected dates changes. Contains a payload with
566
        * a `newSelection` property with an array of selected dates.
567
        *
568
        * @event selectionChange
569
        */
570
        this.fire("selectionChange", {newSelection: this._getSelectedDatesList()});
571
    },
572
 
573
    /**
574
     * A utility method that restores cells modified by custom formatting.
575
     * @method _restoreModifiedCells
576
     * @private
577
     */
578
    _restoreModifiedCells : function () {
579
        var contentbox = this.get("contentBox"),
580
            id;
581
        for (id in this._storedDateCells) {
582
            contentbox.one("#" + id).replace(this._storedDateCells[id]);
583
            delete this._storedDateCells[id];
584
        }
585
    },
586
 
587
    /**
588
     * A rendering assist method that renders all cells modified by the customRenderer
589
     * rules, as well as the enabledDatesRule and disabledDatesRule.
590
     * @method _renderCustomRules
591
     * @private
592
     */
593
    _renderCustomRules : function () {
594
 
595
        this.get("contentBox").all("." + CAL_DAY + ",." + CAL_NEXTMONTH_DAY).removeClass(SELECTION_DISABLED).setAttribute("aria-disabled", false);
596
 
597
        if (!isEmpty(this._rules)) {
598
            var paneNum,
599
                paneDate,
600
                dateArray;
601
 
602
            for (paneNum = 0; paneNum < this._paneNumber; paneNum++) {
603
                paneDate = ydate.addMonths(this.get("date"), paneNum);
604
                dateArray = ydate.listOfDatesInMonth(paneDate);
605
                arrayEach(dateArray, Y.bind(this._renderCustomRulesHelper, this));
606
            }
607
        }
608
    },
609
 
610
    /**
611
    * A handler for a date selection event (either a click or a keyboard
612
    *   selection) that adds the appropriate CSS class to a specific DOM
613
    *   node corresponding to the date and sets its aria-selected
614
    *   attribute to true.
615
    *
616
    * @method _renderCustomRulesHelper
617
    * @private
618
    */
619
    _renderCustomRulesHelper: function (date) {
620
        var enRule = this.get("enabledDatesRule"),
621
            disRule = this.get("disabledDatesRule"),
622
            matchingRules,
623
            dateNode;
624
 
625
        matchingRules = this._getRulesForDate(date);
626
        if (matchingRules.length > 0) {
627
            if ((enRule && iOf(matchingRules, enRule) < 0) || (!enRule && disRule && iOf(matchingRules, disRule) >= 0)) {
628
                this._disableDate(date);
629
            }
630
 
631
            if (L.isFunction(this._filterFunction)) {
632
                dateNode = this._dateToNode(date);
633
                this._storedDateCells[dateNode.get("id")] = dateNode.cloneNode(true);
634
                this._filterFunction (date, dateNode, matchingRules);
635
            }
636
        } else if (enRule) {
637
            this._disableDate(date);
638
        }
639
    },
640
 
641
    /**
642
     * A rendering assist method that renders all cells that are currently selected.
643
     * @method _renderSelectedDates
644
     * @private
645
     */
646
    _renderSelectedDates : function () {
647
        this.get("contentBox").all("." + CAL_DAY_SELECTED).removeClass(CAL_DAY_SELECTED).setAttribute("aria-selected", false);
648
 
649
        var paneNum,
650
            paneDate,
651
            dateArray;
652
 
653
        for (paneNum = 0; paneNum < this._paneNumber; paneNum++) {
654
            paneDate = ydate.addMonths(this.get("date"), paneNum);
655
            dateArray = this._getSelectedDatesInMonth(paneDate);
656
 
657
            arrayEach(dateArray, Y.bind(this._renderSelectedDatesHelper, this));
658
        }
659
    },
660
 
661
    /**
662
    * Takes in a date and determines whether that date has any rules
663
    *   matching it in the customRenderer; then calls the specified
664
    *   filterFunction if that's the case and/or disables the date
665
    *   if the rule is specified as a disabledDatesRule.
666
    *
667
    * @method _renderSelectedDatesHelper
668
    * @private
669
    */
670
    _renderSelectedDatesHelper: function (date) {
671
        this._dateToNode(date).addClass(CAL_DAY_SELECTED).setAttribute("aria-selected", true);
672
    },
673
 
674
    /**
675
     * Add the selection-disabled class and aria-disabled attribute to a node corresponding
676
     * to a given date.
677
     *
678
     * @method _disableDate
679
     * @param {Date} date The date to disable
680
     * @private
681
     */
682
    _disableDate: function (date) {
683
       this._dateToNode(date).addClass(SELECTION_DISABLED).setAttribute("aria-disabled", true);
684
    },
685
 
686
    /**
687
     * A utility method that converts a date to the node wrapping the calendar cell
688
     * the date corresponds to..
689
     * @method _dateToNode
690
     * @param {Date} oDate The date to convert to Node
691
     * @protected
692
     * @return {Node} The node wrapping the DOM element of the cell the date
693
     * corresponds to.
694
     */
695
    _dateToNode : function (oDate) {
696
        var day = oDate.getDate(),
697
            col = 0,
698
            daymod = day%7,
699
            paneNum = (12 + oDate.getMonth() - this.get("date").getMonth()) % 12,
700
            paneId = this._calendarId + "_pane_" + paneNum,
701
            cutoffCol = this._paneProperties[paneId].cutoffCol;
702
 
703
        switch (daymod) {
704
            case (0):
705
                if (cutoffCol >= 6) {
706
                    col = 12;
707
                } else {
708
                    col = 5;
709
                }
710
                break;
711
            case (1):
712
                    col = 6;
713
                break;
714
            case (2):
715
                if (cutoffCol > 0) {
716
                    col = 7;
717
                } else {
718
                    col = 0;
719
                }
720
                break;
721
            case (3):
722
                if (cutoffCol > 1) {
723
                    col = 8;
724
                } else {
725
                    col = 1;
726
                }
727
                break;
728
            case (4):
729
                if (cutoffCol > 2) {
730
                    col = 9;
731
                } else {
732
                    col = 2;
733
                }
734
                break;
735
            case (5):
736
                if (cutoffCol > 3) {
737
                    col = 10;
738
                } else {
739
                    col = 3;
740
                }
741
                break;
742
            case (6):
743
                if (cutoffCol > 4) {
744
                    col = 11;
745
                } else {
746
                    col = 4;
747
                }
748
                break;
749
        }
750
        return(this.get("contentBox").one("#" + this._calendarId + "_pane_" + paneNum + "_" + col + "_" + day));
751
 
752
    },
753
 
754
    /**
755
     * A utility method that converts a node corresponding to the DOM element of
756
     * the cell for a particular date to that date.
757
     * @method _nodeToDate
758
     * @param {Node} oNode The Node wrapping the DOM element of a particular date cell.
759
     * @protected
760
     * @return {Date} The date corresponding to the DOM element that the given node wraps.
761
     */
762
    _nodeToDate : function (oNode) {
763
 
764
        var idParts = oNode.get("id").split("_").reverse(),
765
            paneNum = parseInt(idParts[2], 10),
766
            day  = parseInt(idParts[0], 10),
767
            shiftedDate = ydate.addMonths(this.get("date"), paneNum),
768
            year = shiftedDate.getFullYear(),
769
            month = shiftedDate.getMonth();
770
 
771
        return new Date(year, month, day, 12, 0, 0, 0);
772
    },
773
 
774
    /**
775
     * A placeholder method, called from bindUI, to bind the Calendar events.
776
     * @method _bindCalendarEvents
777
     * @protected
778
     */
779
    _bindCalendarEvents : function () {},
780
 
781
    /**
782
     * A utility method that normalizes a given date by converting it to the 1st
783
     * day of the month the date is in, with the time set to noon.
784
     * @method _normalizeDate
785
     * @param {Date} oDate The date to normalize
786
     * @protected
787
     * @return {Date} The normalized date, set to the first of the month, with time
788
     * set to noon.
789
     */
790
    _normalizeDate : function (date) {
791
        if (date) {
792
            return new Date(date.getFullYear(), date.getMonth(), 1, 12, 0, 0, 0);
793
        } else {
794
            return null;
795
        }
796
    },
797
 
798
    /**
799
     * A utility method that normalizes a given date by setting its time to noon.
800
     * @method _normalizeTime
801
     * @param {Date} oDate The date to normalize
802
     * @protected
803
     * @return {Date} The normalized date
804
     * set to noon.
805
     */
806
    _normalizeTime : function (date) {
807
        if (date) {
808
            return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 12, 0, 0, 0);
809
        } else {
810
            return null;
811
        }
812
    },
813
 
814
 
815
    /**
816
     * A render assist utility method that computes the cutoff column for the calendar
817
     * rendering mask.
818
     * @method _getCutoffColumn
819
     * @param {Date} date The date of the month grid to compute the cutoff column for.
820
     * @param {Number} firstday The first day of the week (modified by internationalized calendars)
821
     * @private
822
     * @return {Number} The number of the cutoff column.
823
     */
824
    _getCutoffColumn : function (date, firstday) {
825
        var distance = this._normalizeDate(date).getDay() - firstday,
826
            cutOffColumn = 6 - (distance + 7) % 7;
827
        return cutOffColumn;
828
    },
829
 
830
    /**
831
     * A render assist method that turns on the view of the previous month's dates
832
     * in a given calendar pane.
833
     * @method _turnPrevMonthOn
834
     * @param {Node} pane The calendar pane that needs its previous month's dates view
835
     * modified.
836
     * @protected
837
     */
838
    _turnPrevMonthOn : function (pane) {
839
        var pane_id = pane.get("id"),
840
            pane_date = this._paneProperties[pane_id].paneDate,
841
            daysInPrevMonth = ydate.daysInMonth(ydate.addMonths(pane_date, -1)),
842
            cell;
843
 
844
        if (!this._paneProperties[pane_id].hasOwnProperty("daysInPrevMonth")) {
845
            this._paneProperties[pane_id].daysInPrevMonth = 0;
846
        }
847
 
848
        if (daysInPrevMonth !== this._paneProperties[pane_id].daysInPrevMonth) {
849
 
850
            this._paneProperties[pane_id].daysInPrevMonth = daysInPrevMonth;
851
 
852
            for (cell = 5; cell >= 0; cell--) {
853
                pane.one("#" + pane_id + "_" + cell + "_" + (cell-5)).set('text', daysInPrevMonth--);
854
            }
855
        }
856
    },
857
 
858
    /**
859
     * A render assist method that turns off the view of the previous month's dates
860
     * in a given calendar pane.
861
     * @method _turnPrevMonthOff
862
     * @param {Node} pane The calendar pane that needs its previous month's dates view
863
     * modified.
864
     * @protected
865
     */
866
    _turnPrevMonthOff : function (pane) {
867
        var pane_id = pane.get("id"),
868
            cell;
869
 
870
        this._paneProperties[pane_id].daysInPrevMonth = 0;
871
 
872
        for (cell = 5; cell >= 0; cell--) {
873
            pane.one("#" + pane_id + "_" + cell + "_" + (cell-5)).setContent("&nbsp;");
874
        }
875
    },
876
 
877
    /**
878
     * A render assist method that cleans up the last few cells in the month grid
879
     * when the number of days in the month changes.
880
     * @method _cleanUpNextMonthCells
881
     * @param {Node} pane The calendar pane that needs the last date cells cleaned up.
882
     * @private
883
     */
884
    _cleanUpNextMonthCells : function (pane) {
885
        var pane_id = pane.get("id");
886
            pane.one("#" + pane_id + "_6_29").removeClass(CAL_NEXTMONTH_DAY);
887
            pane.one("#" + pane_id + "_7_30").removeClass(CAL_NEXTMONTH_DAY);
888
            pane.one("#" + pane_id + "_8_31").removeClass(CAL_NEXTMONTH_DAY);
889
            pane.one("#" + pane_id + "_0_30").removeClass(CAL_NEXTMONTH_DAY);
890
            pane.one("#" + pane_id + "_1_31").removeClass(CAL_NEXTMONTH_DAY);
891
    },
892
 
893
    /**
894
     * A render assist method that turns on the view of the next month's dates
895
     * in a given calendar pane.
896
     * @method _turnNextMonthOn
897
     * @param {Node} pane The calendar pane that needs its next month's dates view
898
     * modified.
899
     * @protected
900
     */
901
    _turnNextMonthOn : function (pane) {
902
        var dayCounter = 1,
903
            pane_id = pane.get("id"),
904
            daysInMonth = this._paneProperties[pane_id].daysInMonth,
905
            cutoffCol = this._paneProperties[pane_id].cutoffCol,
906
            cell,
907
            startingCell;
908
 
909
        for (cell = daysInMonth - 22; cell < cutoffCol + 7; cell++) {
910
            pane.one("#" + pane_id + "_" + cell + "_" + (cell+23)).set("text", dayCounter++).addClass(CAL_NEXTMONTH_DAY);
911
        }
912
 
913
        startingCell = cutoffCol;
914
 
915
        if (daysInMonth === 31 && (cutoffCol <= 1)) {
916
            startingCell = 2;
917
        } else if (daysInMonth === 30 && cutoffCol === 0) {
918
            startingCell = 1;
919
        }
920
 
921
        for (cell = startingCell ; cell < cutoffCol + 7; cell++) {
922
            pane.one("#" + pane_id + "_" + cell + "_" + (cell+30)).set("text", dayCounter++).addClass(CAL_NEXTMONTH_DAY);
923
        }
924
    },
925
 
926
    /**
927
     * A render assist method that turns off the view of the next month's dates
928
     * in a given calendar pane.
929
     * @method _turnNextMonthOff
930
     * @param {Node} pane The calendar pane that needs its next month's dates view
931
     * modified.
932
     * @protected
933
     */
934
    _turnNextMonthOff : function (pane) {
935
            var pane_id = pane.get("id"),
936
                daysInMonth = this._paneProperties[pane_id].daysInMonth,
937
                cutoffCol = this._paneProperties[pane_id].cutoffCol,
938
                cell,
939
                startingCell;
940
 
941
            for (cell = daysInMonth - 22; cell <= 12; cell++) {
942
                pane.one("#" + pane_id + "_" + cell + "_" + (cell+23)).setContent("&nbsp;").addClass(CAL_NEXTMONTH_DAY);
943
            }
944
 
945
            startingCell = 0;
946
 
947
            if (daysInMonth === 31 && (cutoffCol <= 1)) {
948
                startingCell = 2;
949
            } else if (daysInMonth === 30 && cutoffCol === 0) {
950
                startingCell = 1;
951
            }
952
 
953
            for (cell = startingCell ; cell <= 12; cell++) {
954
                pane.one("#" + pane_id + "_" + cell + "_" + (cell+30)).setContent("&nbsp;").addClass(CAL_NEXTMONTH_DAY);
955
            }
956
    },
957
 
958
    /**
959
     * The handler for the change in the showNextMonth attribute.
960
     * @method _afterShowNextMonthChange
961
     * @private
962
     */
963
    _afterShowNextMonthChange : function () {
964
 
965
        var contentBox = this.get('contentBox'),
966
            lastPane = contentBox.one("#" + this._calendarId + "_pane_" + (this._paneNumber - 1));
967
 
968
        this._cleanUpNextMonthCells(lastPane);
969
 
970
        if (this.get('showNextMonth')) {
971
            this._turnNextMonthOn(lastPane);
972
        } else {
973
            this._turnNextMonthOff(lastPane);
974
        }
975
 
976
    },
977
 
978
    /**
979
     * The handler for the change in the showPrevMonth attribute.
980
     * @method _afterShowPrevMonthChange
981
     * @private
982
     */
983
    _afterShowPrevMonthChange : function () {
984
        var contentBox = this.get('contentBox'),
985
            firstPane = contentBox.one("#" + this._calendarId + "_pane_" + 0);
986
 
987
        if (this.get('showPrevMonth')) {
988
            this._turnPrevMonthOn(firstPane);
989
        } else {
990
            this._turnPrevMonthOff(firstPane);
991
        }
992
 
993
    },
994
 
995
     /**
996
     * The handler for the change in the headerRenderer attribute.
997
     * @method _afterHeaderRendererChange
998
     * @private
999
     */
1000
    _afterHeaderRendererChange : function () {
1001
        var headerCell = this.get("contentBox").one("." + CAL_HD_LABEL);
1002
        headerCell.setContent(this._updateCalendarHeader(this.get('date')));
1003
    },
1004
 
1005
     /**
1006
     * The handler for the change in the customRenderer attribute.
1007
     * @method _afterCustomRendererChange
1008
     * @private
1009
     */
1010
    _afterCustomRendererChange : function () {
1011
        this._restoreModifiedCells();
1012
        this._renderCustomRules();
1013
    },
1014
 
1015
     /**
1016
     * The handler for the change in the date attribute. Modifies the calendar
1017
     * view by shifting the calendar grid mask and running custom rendering and
1018
     * selection rendering as necessary.
1019
     * @method _afterDateChange
1020
     * @private
1021
     */
1022
    _afterDateChange : function () {
1023
 
1024
        var contentBox = this.get('contentBox'),
1025
            headerCell = contentBox.one("." + CAL_HD).one("." + CAL_HD_LABEL),
1026
            calendarPanes = contentBox.all("." + CAL_GRID),
1027
            currentDate = this.get("date"),
1028
            counter = 0;
1029
 
1030
        contentBox.setStyle("visibility", "hidden");
1031
        headerCell.setContent(this._updateCalendarHeader(currentDate));
1032
 
1033
        this._restoreModifiedCells();
1034
 
1035
        calendarPanes.each(function (curNode) {
1036
            this._rerenderCalendarPane(ydate.addMonths(currentDate, counter++), curNode);
1037
        }, this);
1038
 
1039
        this._afterShowPrevMonthChange();
1040
        this._afterShowNextMonthChange();
1041
 
1042
        this._renderCustomRules();
1043
        this._renderSelectedDates();
1044
 
1045
        contentBox.setStyle("visibility", "inherit");
1046
    },
1047
 
1048
 
1049
     /**
1050
     * A rendering assist method that initializes the HTML for a single
1051
     * calendar pane.
1052
     * @method _initCalendarPane
1053
     * @param {Date} baseDate The date corresponding to the month of the given
1054
     * calendar pane.
1055
     * @param {String} pane_id The id of the pane, to be used as a prefix for
1056
     * element ids in the given pane.
1057
     * @private
1058
     */
1059
    _initCalendarPane : function (baseDate, pane_id) {
1060
        var dateFormat = Y.Intl.get('datatype-date-format'),
1061
            weekDays = dateFormat.A,
1062
            // Get the first day of the week from the internationalization package, or else use Sunday as default.
1063
            firstday = this.get('strings.first_weekday') || 0,
1064
            // Compute the cutoff column of the masked calendar table, based on the start date and the first day of week.
1065
            cutoffCol = this._getCutoffColumn(baseDate, firstday),
1066
            // Compute the number of days in the month based on starting date
1067
            daysInMonth = ydate.daysInMonth(baseDate),
1068
            // Initialize the array of individual row HTML strings
1069
            row_array = ['','','','','',''],
1070
            // Initialize the partial templates object
1071
            partials = {},
1072
 
1073
            shortWeekDays,
1074
            day,
1075
            row,
1076
            column,
1077
            date,
1078
            id_date,
1079
            calendar_day_class,
1080
            column_visibility,
1081
            output;
1082
 
1083
        if (Y.Intl.getLang('calendar-base')) {
1084
            shortWeekDays = this.get('strings.very_short_weekdays');
1085
        } else {
1086
            shortWeekDays = dateFormat.a;
1087
        }
1088
 
1089
        // Initialize the partial template for the weekday row cells.
1090
        partials.weekday_row = '';
1091
 
1092
        // Populate the partial template for the weekday row cells with weekday names
1093
        for (day = firstday; day <= firstday + 6; day++) {
1094
            partials.weekday_row +=
1095
                substitute(CalendarBase.WEEKDAY_TEMPLATE, {
1096
                    short_weekdayname: shortWeekDays[day%7],
1097
                    weekdayname: weekDays[day%7]
1098
                });
1099
        }
1100
 
1101
        // Populate the partial template for the weekday row container with the weekday row cells
1102
        partials.weekday_row_template = substitute(CalendarBase.WEEKDAY_ROW_TEMPLATE, partials);
1103
 
1104
        // Populate the array of individual row HTML strings
1105
        for (row = 0; row <= 5; row++) {
1106
 
1107
            for (column = 0; column <= 12; column++) {
1108
 
1109
                // Compute the value of the date that needs to populate the cell
1110
                date = 7*row - 5 + column;
1111
 
1112
                // Compose the value of the unique id of the current calendar cell
1113
                id_date = pane_id + "_" + column + "_" + date;
1114
 
1115
                // Set the calendar day class to one of three possible values
1116
                calendar_day_class = CAL_DAY;
1117
 
1118
                if (date < 1) {
1119
                    calendar_day_class = CAL_PREVMONTH_DAY;
1120
                } else if (date > daysInMonth) {
1121
                    calendar_day_class = CAL_NEXTMONTH_DAY;
1122
                }
1123
 
1124
                // Cut off dates that fall before the first and after the last date of the month
1125
                if (date < 1 || date > daysInMonth) {
1126
                    date = "&nbsp;";
1127
                }
1128
 
1129
                // Decide on whether a column in the masked table is visible or not based on the value of the cutoff column.
1130
                column_visibility = (column >= cutoffCol && column < (cutoffCol + 7)) ? '' : CAL_COL_HIDDEN;
1131
 
1132
                // Substitute the values into the partial calendar day template and add it to the current row HTML string
1133
                row_array[row] += substitute (CalendarBase.CALDAY_TEMPLATE, {
1134
                    day_content: date,
1135
                    calendar_col_class: "calendar_col" + column,
1136
                    calendar_col_visibility_class: column_visibility,
1137
                    calendar_day_class: calendar_day_class,
1138
                    calendar_day_id: id_date
1139
                });
1140
            }
1141
        }
1142
 
1143
        // Instantiate the partial calendar pane body template
1144
        partials.body_template = '';
1145
 
1146
        // Populate the body template with the rows templates
1147
        arrayEach (row_array, function (v) {
1148
             partials.body_template += substitute(CalendarBase.CALDAY_ROW_TEMPLATE, {calday_row: v});
1149
        });
1150
 
1151
        // Populate the calendar grid id
1152
        partials.calendar_pane_id = pane_id;
1153
 
1154
        // Populate the calendar pane tabindex
1155
        partials.calendar_pane_tabindex = this.get("tabIndex");
1156
        partials.pane_arialabel = ydate.format(baseDate, { format: "%B %Y" });
1157
 
1158
 
1159
        // Generate final output by substituting class names.
1160
        output = substitute(substitute (CalendarBase.CALENDAR_GRID_TEMPLATE, partials),
1161
                                                        CalendarBase.CALENDAR_STRINGS);
1162
 
1163
        // Store the initialized pane information
1164
        this._paneProperties[pane_id] = {cutoffCol: cutoffCol, daysInMonth: daysInMonth, paneDate: baseDate};
1165
 
1166
        return output;
1167
    },
1168
 
1169
     /**
1170
     * A rendering assist method that rerenders a specified calendar pane, based
1171
     * on a new Date.
1172
     * @method _rerenderCalendarPane
1173
     * @param {Date} newDate The date corresponding to the month of the given
1174
     * calendar pane.
1175
     * @param {Node} pane The node corresponding to the calendar pane to be rerenders.
1176
     * @private
1177
     */
1178
    _rerenderCalendarPane : function (newDate, pane) {
1179
 
1180
        // Get the first day of the week from the internationalization package, or else use Sunday as default.
1181
        var firstday = this.get('strings.first_weekday') || 0,
1182
            // Compute the cutoff column of the masked calendar table, based on the start date and the first day of week.
1183
            cutoffCol = this._getCutoffColumn(newDate, firstday),
1184
            // Compute the number of days in the month based on starting date
1185
            daysInMonth = ydate.daysInMonth(newDate),
1186
            // Get pane id for easier reference
1187
            paneId = pane.get("id"),
1188
            column,
1189
            currentColumn,
1190
            curCell;
1191
 
1192
        // Hide the pane before making DOM changes to speed them up
1193
        pane.setStyle("visibility", "hidden");
1194
        pane.setAttribute("aria-label", ydate.format(newDate, {format:"%B %Y"}));
1195
 
1196
        // Go through all columns, and flip their visibility setting based on whether they are within the unmasked range.
1197
        for (column = 0; column <= 12; column++) {
1198
            currentColumn = pane.all("." + "calendar_col" + column);
1199
            currentColumn.removeClass(CAL_COL_HIDDEN);
1200
 
1201
            if (column < cutoffCol || column >= (cutoffCol + 7)) {
1202
                currentColumn.addClass(CAL_COL_HIDDEN);
1203
            } else {
1204
                // Clean up dates in visible columns to account for the correct number of days in a month
1205
                switch(column) {
1206
                    case 0:
1207
                        curCell = pane.one("#" + paneId + "_0_30");
1208
                        if (daysInMonth >= 30) {
1209
                            curCell.set("text", "30");
1210
                            curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);
1211
                        } else {
1212
                            curCell.setContent("&nbsp;");
1213
                            curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);
1214
                        }
1215
                        break;
1216
                    case 1:
1217
                        curCell = pane.one("#" + paneId + "_1_31");
1218
                        if (daysInMonth >= 31) {
1219
                            curCell.set("text", "31");
1220
                            curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);
1221
                        } else {
1222
                            curCell.setContent("&nbsp;");
1223
                            curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);
1224
                        }
1225
                        break;
1226
                    case 6:
1227
                        curCell = pane.one("#" + paneId + "_6_29");
1228
                        if (daysInMonth >= 29) {
1229
                            curCell.set("text", "29");
1230
                            curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);
1231
                        } else {
1232
                            curCell.setContent("&nbsp;");
1233
                            curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);
1234
                        }
1235
                        break;
1236
                    case 7:
1237
                        curCell = pane.one("#" + paneId + "_7_30");
1238
                        if (daysInMonth >= 30) {
1239
                            curCell.set("text", "30");
1240
                            curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);
1241
                        } else {
1242
                            curCell.setContent("&nbsp;");
1243
                            curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);
1244
                        }
1245
                        break;
1246
                    case 8:
1247
                        curCell = pane.one("#" + paneId + "_8_31");
1248
                        if (daysInMonth >= 31) {
1249
                            curCell.set("text", "31");
1250
                            curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);
1251
                        } else {
1252
                            curCell.setContent("&nbsp;");
1253
                            curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);
1254
                        }
1255
                        break;
1256
                }
1257
            }
1258
        }
1259
 
1260
        // Update stored pane properties
1261
        this._paneProperties[paneId].cutoffCol = cutoffCol;
1262
        this._paneProperties[paneId].daysInMonth = daysInMonth;
1263
        this._paneProperties[paneId].paneDate = newDate;
1264
 
1265
        // Bring the pane visibility back after all DOM changes are done
1266
        pane.setStyle("visibility", "inherit");
1267
 
1268
    },
1269
 
1270
     /**
1271
     * A rendering assist method that updates the calendar header based
1272
     * on a given date and potentially the provided headerRenderer.
1273
     * @method _updateCalendarHeader
1274
     * @param {Date} baseDate The date with which to update the calendar header.
1275
     * @private
1276
     */
1277
    _updateCalendarHeader : function (baseDate) {
1278
        var headerString = "",
1279
            headerRenderer = this.get("headerRenderer");
1280
 
1281
        if (Y.Lang.isString(headerRenderer)) {
1282
            headerString = ydate.format(baseDate, {format:headerRenderer});
1283
        } else if (headerRenderer instanceof Function) {
1284
            headerString = headerRenderer.call(this, baseDate);
1285
        }
1286
 
1287
        return headerString;
1288
    },
1289
 
1290
     /**
1291
     * A rendering assist method that initializes the calendar header HTML
1292
     * based on a given date and potentially the provided headerRenderer.
1293
     * @method _initCalendarHeader
1294
     * @param {Date} baseDate The date with which to initialize the calendar header.
1295
     * @private
1296
     */
1297
    _initCalendarHeader : function (baseDate) {
1298
        return substitute(substitute(CalendarBase.HEADER_TEMPLATE, {
1299
                calheader: this._updateCalendarHeader(baseDate),
1300
                calendar_id: this._calendarId
1301
            }), CalendarBase.CALENDAR_STRINGS );
1302
    },
1303
 
1304
     /**
1305
     * A rendering assist method that initializes the calendar HTML
1306
     * based on a given date.
1307
     * @method _initCalendarHTML
1308
     * @param {Date} baseDate The date with which to initialize the calendar.
1309
     * @private
1310
     */
1311
    _initCalendarHTML : function (baseDate) {
1312
        // Instantiate the partials holder
1313
        var partials = {},
1314
            // Counter for iterative template replacement.
1315
            counter = 0,
1316
            singlePane,
1317
            output;
1318
 
1319
        // Generate the template for the header
1320
        partials.header_template =  this._initCalendarHeader(baseDate);
1321
        partials.calendar_id = this._calendarId;
1322
 
1323
        partials.body_template = substitute(substitute (CalendarBase.CONTENT_TEMPLATE, partials),
1324
                                                                                 CalendarBase.CALENDAR_STRINGS);
1325
 
1326
        // Instantiate the iterative template replacer function
1327
        function paneReplacer () {
1328
            singlePane = this._initCalendarPane(ydate.addMonths(baseDate, counter), partials.calendar_id + "_pane_" + counter);
1329
            counter++;
1330
            return singlePane;
1331
        }
1332
 
1333
        // Go through all occurrences of the calendar_grid_template token and replace it with an appropriate calendar grid.
1334
        output = partials.body_template.replace(/\{calendar_grid_template\}/g, Y.bind(paneReplacer, this));
1335
 
1336
        // Update the paneNumber count
1337
        this._paneNumber = counter;
1338
 
1339
        return output;
1340
    }
1341
}, {
1342
 
1343
     /**
1344
        * The CSS classnames for the calendar templates.
1345
        * @property CALENDAR_STRINGS
1346
        * @type Object
1347
        * @readOnly
1348
        * @protected
1349
        * @static
1350
        */
1351
    CALENDAR_STRINGS: {
1352
        calendar_grid_class       : CAL_GRID,
1353
        calendar_body_class       : CAL_BODY,
1354
        calendar_hd_class         : CAL_HD,
1355
        calendar_hd_label_class   : CAL_HD_LABEL,
1356
        calendar_weekdayrow_class : CAL_WDAYROW,
1357
        calendar_weekday_class    : CAL_WDAY,
1358
        calendar_row_class        : CAL_ROW,
1359
        calendar_day_class        : CAL_DAY,
1360
        calendar_dayanchor_class  : CAL_ANCHOR,
1361
        calendar_pane_class       : CAL_PANE,
1362
        calendar_right_grid_class : CAL_RIGHT_GRID,
1363
        calendar_left_grid_class  : CAL_LEFT_GRID,
1364
        calendar_status_class     : CAL_STATUS
1365
    },
1366
 
1367
    /*
1368
 
1369
    ARIA_STATUS_TEMPLATE: '<div role="status" aria-atomic="true" class="{calendar_status_class}"></div>',
1370
 
1371
    AriaStatus : null,
1372
 
1373
    updateStatus : function (statusString) {
1374
 
1375
        if (!CalendarBase.AriaStatus) {
1376
            CalendarBase.AriaStatus = create(
1377
                                                         substitute (CalendarBase.ARIA_STATUS_TEMPLATE,
1378
                                                                                 CalendarBase.CALENDAR_STRINGS));
1379
            Y.one("body").append(CalendarBase.AriaStatus);
1380
        }
1381
 
1382
            CalendarBase.AriaStatus.set("text", statusString);
1383
    },
1384
 
1385
    */
1386
 
1387
     /**
1388
        * The main content template for calendar.
1389
        * @property CONTENT_TEMPLATE
1390
        * @type String
1391
        * @protected
1392
        * @static
1393
        */
1394
    CONTENT_TEMPLATE:  '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +
1395
                        '{header_template}' +
1396
                        '<div class="yui3-u-1">' +
1397
                        '{calendar_grid_template}' +
1398
                        '</div>' +
1399
                        '</div>',
1400
 
1401
     /**
1402
        * A single pane template for calendar (same as default CONTENT_TEMPLATE)
1403
        * @property ONE_PANE_TEMPLATE
1404
        * @type String
1405
        * @protected
1406
        * @readOnly
1407
        * @static
1408
        */
1409
    ONE_PANE_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +
1410
                            '{header_template}' +
1411
                            '<div class="yui3-u-1">' +
1412
                                '{calendar_grid_template}' +
1413
                            '</div>' +
1414
                        '</div>',
1415
 
1416
     /**
1417
        * A two pane template for calendar.
1418
        * @property TWO_PANE_TEMPLATE
1419
        * @type String
1420
        * @protected
1421
        * @readOnly
1422
        * @static
1423
        */
1424
    TWO_PANE_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +
1425
                            '{header_template}' +
1426
                            '<div class="yui3-u-1-2">'+
1427
                                '<div class = "{calendar_left_grid_class}">' +
1428
                                    '{calendar_grid_template}' +
1429
                                '</div>' +
1430
                            '</div>' +
1431
                            '<div class="yui3-u-1-2">' +
1432
                                '<div class = "{calendar_right_grid_class}">' +
1433
                                    '{calendar_grid_template}' +
1434
                                '</div>' +
1435
                            '</div>' +
1436
                        '</div>',
1437
     /**
1438
        * A three pane template for calendar.
1439
        * @property THREE_PANE_TEMPLATE
1440
        * @type String
1441
        * @protected
1442
        * @readOnly
1443
        * @static
1444
        */
1445
    THREE_PANE_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +
1446
                            '{header_template}' +
1447
                            '<div class="yui3-u-1-3">' +
1448
                                '<div class="{calendar_left_grid_class}">' +
1449
                                    '{calendar_grid_template}' +
1450
                                '</div>' +
1451
                            '</div>' +
1452
                            '<div class="yui3-u-1-3">' +
1453
                                '{calendar_grid_template}' +
1454
                            '</div>' +
1455
                            '<div class="yui3-u-1-3">' +
1456
                                '<div class="{calendar_right_grid_class}">' +
1457
                                    '{calendar_grid_template}' +
1458
                                '</div>' +
1459
                            '</div>' +
1460
                        '</div>',
1461
     /**
1462
        * A template for the calendar grid.
1463
        * @property CALENDAR_GRID_TEMPLATE
1464
        * @type String
1465
        * @protected
1466
        * @static
1467
        */
1468
    CALENDAR_GRID_TEMPLATE: '<table class="{calendar_grid_class}" id="{calendar_pane_id}" role="grid" aria-readonly="true" ' +
1469
                                'aria-label="{pane_arialabel}" tabindex="{calendar_pane_tabindex}">' +
1470
                                '<thead>' +
1471
                                    '{weekday_row_template}' +
1472
                                '</thead>' +
1473
                                '<tbody>' +
1474
                                    '{body_template}' +
1475
                                '</tbody>' +
1476
                            '</table>',
1477
 
1478
     /**
1479
        * A template for the calendar header.
1480
        * @property HEADER_TEMPLATE
1481
        * @type String
1482
        * @protected
1483
        * @static
1484
        */
1485
    HEADER_TEMPLATE: '<div class="yui3-g {calendar_hd_class}">' +
1486
                        '<div class="yui3-u {calendar_hd_label_class}" id="{calendar_id}_header" aria-role="heading">' +
1487
                            '{calheader}' +
1488
                        '</div>' +
1489
                    '</div>',
1490
 
1491
     /**
1492
        * A template for the row of weekday names.
1493
        * @property WEEKDAY_ROW_TEMPLATE
1494
        * @type String
1495
        * @protected
1496
        * @static
1497
        */
1498
    WEEKDAY_ROW_TEMPLATE: '<tr class="{calendar_weekdayrow_class}" role="row">' +
1499
                            '{weekday_row}' +
1500
                        '</tr>',
1501
 
1502
     /**
1503
        * A template for a single row of calendar days.
1504
        * @property CALDAY_ROW_TEMPLATE
1505
        * @type String
1506
        * @protected
1507
        * @static
1508
        */
1509
    CALDAY_ROW_TEMPLATE: '<tr class="{calendar_row_class}" role="row">' +
1510
                            '{calday_row}' +
1511
                        '</tr>',
1512
 
1513
     /**
1514
        * A template for a single cell with a weekday name.
1515
        * @property WEEKDAY_TEMPLATE
1516
        * @type String
1517
        * @protected
1518
        * @static
1519
        */
1520
    WEEKDAY_TEMPLATE: '<th class="{calendar_weekday_class}" role="columnheader" aria-label="{weekdayname}">{short_weekdayname}</th>',
1521
 
1522
     /**
1523
        * A template for a single cell with a calendar day.
1524
        * @property CALDAY_TEMPLATE
1525
        * @type String
1526
        * @protected
1527
        * @static
1528
        */
1529
    CALDAY_TEMPLATE: '<td class="{calendar_col_class} {calendar_day_class} {calendar_col_visibility_class}" id="{calendar_day_id}" ' +
1530
                        'role="gridcell" tabindex="-1">' +
1531
                        '{day_content}' +
1532
                    '</td>',
1533
 
1534
     /**
1535
        * The identity of the widget.
1536
        *
1537
        * @property NAME
1538
        * @type String
1539
        * @default 'calendarBase'
1540
        * @readOnly
1541
        * @protected
1542
        * @static
1543
        */
1544
    NAME: 'calendarBase',
1545
 
1546
     /**
1547
        * Static property used to define the default attribute configuration of
1548
        * the Widget.
1549
        *
1550
        * @property ATTRS
1551
        * @type {Object}
1552
        * @protected
1553
        * @static
1554
        */
1555
    ATTRS: {
1556
        tabIndex: {
1557
            value: 1
1558
        },
1559
        /**
1560
         * The date corresponding to the current calendar view. Always
1561
         * normalized to the first of the month that contains the date
1562
         * at assignment time. Used as the first date visible in the
1563
         * calendar.
1564
         *
1565
         * @attribute date
1566
         * @type Date
1567
         * @default The first of the month containing today's date, as
1568
         * set on the end user's system.
1569
         */
1570
        date: {
1571
            value: new Date(),
1572
            setter: function (val) {
1573
                var newDate = this._normalizeDate(val);
1574
                if (ydate.areEqual(newDate, this.get('date'))) {
1575
                        return this.get('date');
1576
                } else {
1577
                        return newDate;
1578
                }
1579
            }
1580
        },
1581
 
1582
        /**
1583
         * A setting specifying whether to shows days from the previous
1584
         * month in the visible month's grid, if there are empty preceding
1585
         * cells available.
1586
         *
1587
         * @attribute showPrevMonth
1588
         * @type boolean
1589
         * @default false
1590
         */
1591
        showPrevMonth: {
1592
            value: false
1593
        },
1594
 
1595
        /**
1596
         * A setting specifying whether to shows days from the next
1597
         * month in the visible month's grid, if there are empty
1598
         * cells available at the end.
1599
         *
1600
         * @attribute showNextMonth
1601
         * @type boolean
1602
         * @default false
1603
         */
1604
        showNextMonth: {
1605
            value: false
1606
        },
1607
 
1608
        /**
1609
         * Strings and properties derived from the internationalization packages
1610
         * for the calendar.
1611
         *
1612
         * @attribute strings
1613
         * @type Object
1614
         * @protected
1615
         */
1616
        strings : {
1617
            valueFn: function() { return Y.Intl.get("calendar-base"); }
1618
        },
1619
 
1620
        /**
1621
         * Custom header renderer for the calendar.
1622
         *
1623
         * @attribute headerRenderer
1624
         * @type String | Function
1625
         */
1626
        headerRenderer: {
1627
            value: "%B %Y"
1628
        },
1629
 
1630
        /**
1631
         * The name of the rule which all enabled dates should match.
1632
         * Either disabledDatesRule or enabledDatesRule should be specified,
1633
         * or neither, but not both.
1634
         *
1635
         * @attribute enabledDatesRule
1636
         * @type String
1637
         * @default null
1638
         */
1639
        enabledDatesRule: {
1640
            value: null
1641
        },
1642
 
1643
        /**
1644
         * The name of the rule which all disabled dates should match.
1645
         * Either disabledDatesRule or enabledDatesRule should be specified,
1646
         * or neither, but not both.
1647
         *
1648
         * @attribute disabledDatesRule
1649
         * @type String
1650
         * @default null
1651
         */
1652
        disabledDatesRule: {
1653
            value: null
1654
        },
1655
 
1656
        /**
1657
         * A read-only attribute providing a list of currently selected dates.
1658
         *
1659
         * @attribute selectedDates
1660
         * @readOnly
1661
         * @type Array
1662
         */
1663
        selectedDates : {
1664
            readOnly: true,
1665
            getter: function () {
1666
                return (this._getSelectedDatesList());
1667
            }
1668
        },
1669
 
1670
        /**
1671
         * An object of the form {rules:Object, filterFunction:Function},
1672
         * providing  set of rules and a custom rendering function for
1673
         * customizing specific calendar cells.
1674
         *
1675
         * @attribute customRenderer
1676
         * @type Object
1677
         * @default {}
1678
         */
1679
        customRenderer : {
1680
            lazyAdd: false,
1681
            value: {},
1682
            setter: function (val) {
1683
                this._rules = val.rules;
1684
                this._filterFunction = val.filterFunction;
1685
            }
1686
        }
1687
    }
1688
 
1689
});
1690
 
1691
 
1692
}, '3.18.1', {
1693
    "requires": [
1694
        "widget",
1695
        "datatype-date",
1696
        "datatype-date-math",
1697
        "cssgrids"
1698
    ],
1699
    "lang": [
1700
        "de",
1701
        "en",
1702
        "es",
1703
        "es-AR",
1704
        "fr",
1705
        "hu",
1706
        "it",
1707
        "ja",
1708
        "nb-NO",
1709
        "nl",
1710
        "pt-BR",
1711
        "ru",
1712
        "zh-Hans",
1713
        "zh-Hans-CN",
1714
        "zh-Hant",
1715
        "zh-Hant-HK",
1716
        "zh-HANT-TW"
1717
    ],
1718
    "skinnable": true
1719
});