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