~andreserl/maas/packaging_precise_rebase

« back to all changes in this revision

Viewing changes to debian/extras/jslibs/yui/calendar-base/calendar-base-debug.js

  • Committer: Andres Rodriguez
  • Date: 2013-03-20 18:12:30 UTC
  • mfrom: (145.2.22 precise.sru)
  • Revision ID: andreserl@ubuntu.com-20130320181230-6l5guc0nhlv2z4p7
Re-base againts latest quantal released branch towards SRU

Show diffs side-by-side

added added

removed removed

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