AutorÃa | Ultima modificación | Ver Log |
YUI.add('calendar-base', function (Y, NAME) {/*** The CalendarBase submodule is a basic UI calendar view that displays* a range of dates in a two-dimensional month grid, with one or more* months visible at a single time. CalendarBase supports custom date* rendering, multiple calendar panes, and selection.* @module calendar* @submodule calendar-base*/var getCN = Y.ClassNameManager.getClassName,CALENDAR = 'calendar',CAL_GRID = getCN(CALENDAR, 'grid'),CAL_LEFT_GRID = getCN(CALENDAR, 'left-grid'),CAL_RIGHT_GRID = getCN(CALENDAR, 'right-grid'),CAL_BODY = getCN(CALENDAR, 'body'),CAL_HD = getCN(CALENDAR, 'header'),CAL_HD_LABEL = getCN(CALENDAR, 'header-label'),CAL_WDAYROW = getCN(CALENDAR, 'weekdayrow'),CAL_WDAY = getCN(CALENDAR, 'weekday'),CAL_COL_HIDDEN = getCN(CALENDAR, 'column-hidden'),CAL_DAY_SELECTED = getCN(CALENDAR, 'day-selected'),SELECTION_DISABLED = getCN(CALENDAR, 'selection-disabled'),CAL_ROW = getCN(CALENDAR, 'row'),CAL_DAY = getCN(CALENDAR, 'day'),CAL_PREVMONTH_DAY = getCN(CALENDAR, 'prevmonth-day'),CAL_NEXTMONTH_DAY = getCN(CALENDAR, 'nextmonth-day'),CAL_ANCHOR = getCN(CALENDAR, 'anchor'),CAL_PANE = getCN(CALENDAR, 'pane'),CAL_STATUS = getCN(CALENDAR, 'status'),L = Y.Lang,substitute = L.sub,arrayEach = Y.Array.each,objEach = Y.Object.each,iOf = Y.Array.indexOf,hasKey = Y.Object.hasKey,setVal = Y.Object.setValue,isEmpty = Y.Object.isEmpty,ydate = Y.DataType.Date;/** Create a calendar view to represent a single or multiple* month range of dates, rendered as a grid with date and* weekday labels.** @class CalendarBase* @extends Widget* @param config {Object} Configuration object (see Configuration* attributes)* @constructor*/function CalendarBase() {CalendarBase.superclass.constructor.apply ( this, arguments );}Y.CalendarBase = Y.extend( CalendarBase, Y.Widget, {/*** A storage for various properties of individual month* panes.** @property _paneProperties* @type Object* @private*/_paneProperties : {},/*** The number of month panes in the calendar, deduced* from the CONTENT_TEMPLATE's number of {calendar_grid}* tokens.** @property _paneNumber* @type Number* @private*/_paneNumber : 1,/*** The unique id used to prefix various elements of this* calendar instance.** @property _calendarId* @type String* @private*/_calendarId : null,/*** The hash map of selected dates, populated with* selectDates() and deselectDates() methods** @property _selectedDates* @type Object* @private*/_selectedDates : {},/*** A private copy of the rules object, populated* by setting the customRenderer attribute.** @property _rules* @type Object* @private*/_rules : {},/*** A private copy of the filterFunction, populated* by setting the customRenderer attribute.** @property _filterFunction* @type Function* @private*/_filterFunction : null,/*** Storage for calendar cells modified by any custom* formatting. The storage is cleared, used to restore* cells to the original state, and repopulated accordingly* when the calendar is rerendered.** @property _storedDateCells* @type Object* @private*/_storedDateCells : {},/*** Designated initializer* Initializes instance-level properties of* calendar.** @method initializer*/initializer : function () {this._paneProperties = {};this._calendarId = Y.guid('calendar');this._selectedDates = {};if (isEmpty(this._rules)) {this._rules = {};}this._storedDateCells = {};},/*** renderUI implementation** Creates a visual representation of the calendar based on existing parameters.* @method renderUI*/renderUI : function () {var contentBox = this.get('contentBox');contentBox.appendChild(this._initCalendarHTML(this.get('date')));if (this.get('showPrevMonth')) {this._afterShowPrevMonthChange();}if (this.get('showNextMonth')) {this._afterShowNextMonthChange();}this._renderCustomRules();this._renderSelectedDates();this.get("boundingBox").setAttribute("aria-labelledby", this._calendarId + "_header");},/*** bindUI implementation** Assigns listeners to relevant events that change the state* of the calendar.* @method bindUI*/bindUI : function () {this.after('dateChange', this._afterDateChange);this.after('showPrevMonthChange', this._afterShowPrevMonthChange);this.after('showNextMonthChange', this._afterShowNextMonthChange);this.after('headerRendererChange', this._afterHeaderRendererChange);this.after('customRendererChange', this._afterCustomRendererChange);this.after('enabledDatesRuleChange', this._afterCustomRendererChange);this.after('disabledDatesRuleChange', this._afterCustomRendererChange);this.after('focusedChange', this._afterFocusedChange);this.after('selectionChange', this._renderSelectedDates);this._bindCalendarEvents();},/*** An internal utility method that generates a list of selected dates* from the hash storage.** @method _getSelectedDatesList* @protected* @return {Array} The array of `Date`s that are currently selected.*/_getSelectedDatesList : function () {var output = [];objEach (this._selectedDates, function (year) {objEach (year, function (month) {objEach (month, function (day) {output.push (day);}, this);}, this);}, this);return output;},/*** A utility method that returns all dates selected in a specific month.** @method _getSelectedDatesInMonth* @param {Date} oDate corresponding to the month for which selected dates* are requested.* @protected* @return {Array} The array of `Date`s in a given month that are currently selected.*/_getSelectedDatesInMonth : function (oDate) {var year = oDate.getFullYear(),month = oDate.getMonth();if (hasKey(this._selectedDates, year) && hasKey(this._selectedDates[year], month)) {return Y.Object.values(this._selectedDates[year][month]);} else {return [];}},/*** An internal parsing method that receives a String list of numbers* and number ranges (of the form "1,2,3,4-6,7-9,10,11" etc.) and checks* whether a specific number is included in this list. Used for looking* up dates in the customRenderer rule set.** @method _isNumInList* @param {Number} num The number to look for in a list.* @param {String} strList The list of numbers of the form "1,2,3,4-6,7-8,9", etc.* @private* @return {boolean} Returns true if the given number is in the given list.*/_isNumInList : function (num, strList) {if (strList === "all") {return true;} else {var elements = strList.split(","),i = elements.length,range;while (i--) {range = elements[i].split("-");if (range.length === 2 && num >= parseInt(range[0], 10) && num <= parseInt(range[1], 10)) {return true;}else if (range.length === 1 && (parseInt(elements[i], 10) === num)) {return true;}}return false;}},/*** Given a specific date, returns an array of rules (from the customRenderer rule set)* that the given date matches.** @method _getRulesForDate* @param {Date} oDate The date for which an array of rules is needed* @private* @return {Array} Returns an array of `String`s, each containg the name of* a rule that the given date matches.*/_getRulesForDate : function (oDate) {var year = oDate.getFullYear(),month = oDate.getMonth(),date = oDate.getDate(),wday = oDate.getDay(),rules = this._rules,outputRules = [],years, months, dates, days;for (years in rules) {if (this._isNumInList(year, years)) {if (L.isString(rules[years])) {outputRules.push(rules[years]);}else {for (months in rules[years]) {if (this._isNumInList(month, months)) {if (L.isString(rules[years][months])) {outputRules.push(rules[years][months]);}else {for (dates in rules[years][months]) {if (this._isNumInList(date, dates)) {if (L.isString(rules[years][months][dates])) {outputRules.push(rules[years][months][dates]);}else {for (days in rules[years][months][dates]) {if (this._isNumInList(wday, days)) {if (L.isString(rules[years][months][dates][days])) {outputRules.push(rules[years][months][dates][days]);}}}}}}}}}}}}return outputRules;},/*** A utility method which, given a specific date and a name of the rule,* checks whether the date matches the given rule.** @method _matchesRule* @param {Date} oDate The date to check* @param {String} rule The name of the rule that the date should match.* @private* @return {boolean} Returns true if the date matches the given rule.**/_matchesRule : function (oDate, rule) {return (iOf(this._getRulesForDate(oDate), rule) >= 0);},/*** A utility method which checks whether a given date matches the `enabledDatesRule`* or does not match the `disabledDatesRule` and therefore whether it can be selected.* @method _canBeSelected* @param {Date} oDate The date to check* @private* @return {boolean} Returns true if the date can be selected; false otherwise.*/_canBeSelected : function (oDate) {var enabledDatesRule = this.get("enabledDatesRule"),disabledDatesRule = this.get("disabledDatesRule");if (enabledDatesRule) {return this._matchesRule(oDate, enabledDatesRule);} else if (disabledDatesRule) {return !this._matchesRule(oDate, disabledDatesRule);} else {return true;}},/*** Selects a given date or array of dates.* @method selectDates* @param {Date|Array} dates A `Date` or `Array` of `Date`s.* @return {CalendarBase} A reference to this object* @chainable*/selectDates : function (dates) {if (ydate.isValidDate(dates)) {this._addDateToSelection(dates);}else if (L.isArray(dates)) {this._addDatesToSelection(dates);}return this;},/*** Deselects a given date or array of dates, or deselects* all dates if no argument is specified.* @method deselectDates* @param {Date|Array} [dates] A `Date` or `Array` of `Date`s, or no* argument if all dates should be deselected.* @return {CalendarBase} A reference to this object* @chainable*/deselectDates : function (dates) {if (!dates) {this._clearSelection();}else if (ydate.isValidDate(dates)) {this._removeDateFromSelection(dates);}else if (L.isArray(dates)) {this._removeDatesFromSelection(dates);}return this;},/*** A utility method that adds a given date to selection..* @method _addDateToSelection* @param {Date} oDate The date to add to selection.* @param {Number} [index] An optional parameter that is used* to differentiate between individual date selections and multiple* date selections.* @private*/_addDateToSelection : function (oDate, index) {oDate = this._normalizeTime(oDate);if (this._canBeSelected(oDate)) {var year = oDate.getFullYear(),month = oDate.getMonth(),day = oDate.getDate();if (hasKey(this._selectedDates, year)) {if (hasKey(this._selectedDates[year], month)) {this._selectedDates[year][month][day] = oDate;} else {this._selectedDates[year][month] = {};this._selectedDates[year][month][day] = oDate;}} else {this._selectedDates[year] = {};this._selectedDates[year][month] = {};this._selectedDates[year][month][day] = oDate;}this._selectedDates = setVal(this._selectedDates, [year, month, day], oDate);if (!index) {this._fireSelectionChange();}}},/*** A utility method that adds a given list of dates to selection.* @method _addDatesToSelection* @param {Array} datesArray The list of dates to add to selection.* @private*/_addDatesToSelection : function (datesArray) {arrayEach(datesArray, this._addDateToSelection, this);this._fireSelectionChange();},/*** A utility method that adds a given range of dates to selection.* @method _addDateRangeToSelection* @param {Date} startDate The first date of the given range.* @param {Date} endDate The last date of the given range.* @private*/_addDateRangeToSelection : function (startDate, endDate) {var timezoneDifference = (endDate.getTimezoneOffset() - startDate.getTimezoneOffset())*60000,startTime = startDate.getTime(),endTime = endDate.getTime(),tempTime,time,addedDate;if (startTime > endTime) {tempTime = startTime;startTime = endTime;endTime = tempTime + timezoneDifference;} else {endTime = endTime - timezoneDifference;}for (time = startTime; time <= endTime; time += 86400000) {addedDate = new Date(time);addedDate.setHours(12);this._addDateToSelection(addedDate, time);}this._fireSelectionChange();},/*** A utility method that removes a given date from selection..* @method _removeDateFromSelection* @param {Date} oDate The date to remove from selection.* @param {Number} [index] An optional parameter that is used* to differentiate between individual date selections and multiple* date selections.* @private*/_removeDateFromSelection : function (oDate, index) {var year = oDate.getFullYear(),month = oDate.getMonth(),day = oDate.getDate();if (hasKey(this._selectedDates, year) &&hasKey(this._selectedDates[year], month) &&hasKey(this._selectedDates[year][month], day)) {delete this._selectedDates[year][month][day];if (!index) {this._fireSelectionChange();}}},/*** A utility method that removes a given list of dates from selection.* @method _removeDatesFromSelection* @param {Array} datesArray The list of dates to remove from selection.* @private*/_removeDatesFromSelection : function (datesArray) {arrayEach(datesArray, this._removeDateFromSelection, this);this._fireSelectionChange();},/*** A utility method that removes a given range of dates from selection.* @method _removeDateRangeFromSelection* @param {Date} startDate The first date of the given range.* @param {Date} endDate The last date of the given range.* @private*/_removeDateRangeFromSelection : function (startDate, endDate) {var startTime = startDate.getTime(),endTime = endDate.getTime(),time;for (time = startTime; time <= endTime; time += 86400000) {this._removeDateFromSelection(new Date(time), time);}this._fireSelectionChange();},/*** A utility method that removes all dates from selection.* @method _clearSelection* @param {boolean} noevent A Boolean specifying whether a selectionChange* event should be fired. If true, the event is not fired.* @private*/_clearSelection : function (noevent) {this._selectedDates = {};this.get("contentBox").all("." + CAL_DAY_SELECTED).removeClass(CAL_DAY_SELECTED).setAttribute("aria-selected", false);if (!noevent) {this._fireSelectionChange();}},/*** A utility method that fires a selectionChange event.* @method _fireSelectionChange* @private*/_fireSelectionChange : function () {/*** Fired when the set of selected dates changes. Contains a payload with* a `newSelection` property with an array of selected dates.** @event selectionChange*/this.fire("selectionChange", {newSelection: this._getSelectedDatesList()});},/*** A utility method that restores cells modified by custom formatting.* @method _restoreModifiedCells* @private*/_restoreModifiedCells : function () {var contentbox = this.get("contentBox"),id;for (id in this._storedDateCells) {contentbox.one("#" + id).replace(this._storedDateCells[id]);delete this._storedDateCells[id];}},/*** A rendering assist method that renders all cells modified by the customRenderer* rules, as well as the enabledDatesRule and disabledDatesRule.* @method _renderCustomRules* @private*/_renderCustomRules : function () {this.get("contentBox").all("." + CAL_DAY + ",." + CAL_NEXTMONTH_DAY).removeClass(SELECTION_DISABLED).setAttribute("aria-disabled", false);if (!isEmpty(this._rules)) {var paneNum,paneDate,dateArray;for (paneNum = 0; paneNum < this._paneNumber; paneNum++) {paneDate = ydate.addMonths(this.get("date"), paneNum);dateArray = ydate.listOfDatesInMonth(paneDate);arrayEach(dateArray, Y.bind(this._renderCustomRulesHelper, this));}}},/*** A handler for a date selection event (either a click or a keyboard* selection) that adds the appropriate CSS class to a specific DOM* node corresponding to the date and sets its aria-selected* attribute to true.** @method _renderCustomRulesHelper* @private*/_renderCustomRulesHelper: function (date) {var enRule = this.get("enabledDatesRule"),disRule = this.get("disabledDatesRule"),matchingRules,dateNode;matchingRules = this._getRulesForDate(date);if (matchingRules.length > 0) {if ((enRule && iOf(matchingRules, enRule) < 0) || (!enRule && disRule && iOf(matchingRules, disRule) >= 0)) {this._disableDate(date);}if (L.isFunction(this._filterFunction)) {dateNode = this._dateToNode(date);this._storedDateCells[dateNode.get("id")] = dateNode.cloneNode(true);this._filterFunction (date, dateNode, matchingRules);}} else if (enRule) {this._disableDate(date);}},/*** A rendering assist method that renders all cells that are currently selected.* @method _renderSelectedDates* @private*/_renderSelectedDates : function () {this.get("contentBox").all("." + CAL_DAY_SELECTED).removeClass(CAL_DAY_SELECTED).setAttribute("aria-selected", false);var paneNum,paneDate,dateArray;for (paneNum = 0; paneNum < this._paneNumber; paneNum++) {paneDate = ydate.addMonths(this.get("date"), paneNum);dateArray = this._getSelectedDatesInMonth(paneDate);arrayEach(dateArray, Y.bind(this._renderSelectedDatesHelper, this));}},/*** Takes in a date and determines whether that date has any rules* matching it in the customRenderer; then calls the specified* filterFunction if that's the case and/or disables the date* if the rule is specified as a disabledDatesRule.** @method _renderSelectedDatesHelper* @private*/_renderSelectedDatesHelper: function (date) {this._dateToNode(date).addClass(CAL_DAY_SELECTED).setAttribute("aria-selected", true);},/*** Add the selection-disabled class and aria-disabled attribute to a node corresponding* to a given date.** @method _disableDate* @param {Date} date The date to disable* @private*/_disableDate: function (date) {this._dateToNode(date).addClass(SELECTION_DISABLED).setAttribute("aria-disabled", true);},/*** A utility method that converts a date to the node wrapping the calendar cell* the date corresponds to..* @method _dateToNode* @param {Date} oDate The date to convert to Node* @protected* @return {Node} The node wrapping the DOM element of the cell the date* corresponds to.*/_dateToNode : function (oDate) {var day = oDate.getDate(),col = 0,daymod = day%7,paneNum = (12 + oDate.getMonth() - this.get("date").getMonth()) % 12,paneId = this._calendarId + "_pane_" + paneNum,cutoffCol = this._paneProperties[paneId].cutoffCol;switch (daymod) {case (0):if (cutoffCol >= 6) {col = 12;} else {col = 5;}break;case (1):col = 6;break;case (2):if (cutoffCol > 0) {col = 7;} else {col = 0;}break;case (3):if (cutoffCol > 1) {col = 8;} else {col = 1;}break;case (4):if (cutoffCol > 2) {col = 9;} else {col = 2;}break;case (5):if (cutoffCol > 3) {col = 10;} else {col = 3;}break;case (6):if (cutoffCol > 4) {col = 11;} else {col = 4;}break;}return(this.get("contentBox").one("#" + this._calendarId + "_pane_" + paneNum + "_" + col + "_" + day));},/*** A utility method that converts a node corresponding to the DOM element of* the cell for a particular date to that date.* @method _nodeToDate* @param {Node} oNode The Node wrapping the DOM element of a particular date cell.* @protected* @return {Date} The date corresponding to the DOM element that the given node wraps.*/_nodeToDate : function (oNode) {var idParts = oNode.get("id").split("_").reverse(),paneNum = parseInt(idParts[2], 10),day = parseInt(idParts[0], 10),shiftedDate = ydate.addMonths(this.get("date"), paneNum),year = shiftedDate.getFullYear(),month = shiftedDate.getMonth();return new Date(year, month, day, 12, 0, 0, 0);},/*** A placeholder method, called from bindUI, to bind the Calendar events.* @method _bindCalendarEvents* @protected*/_bindCalendarEvents : function () {},/*** A utility method that normalizes a given date by converting it to the 1st* day of the month the date is in, with the time set to noon.* @method _normalizeDate* @param {Date} oDate The date to normalize* @protected* @return {Date} The normalized date, set to the first of the month, with time* set to noon.*/_normalizeDate : function (date) {if (date) {return new Date(date.getFullYear(), date.getMonth(), 1, 12, 0, 0, 0);} else {return null;}},/*** A utility method that normalizes a given date by setting its time to noon.* @method _normalizeTime* @param {Date} oDate The date to normalize* @protected* @return {Date} The normalized date* set to noon.*/_normalizeTime : function (date) {if (date) {return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 12, 0, 0, 0);} else {return null;}},/*** A render assist utility method that computes the cutoff column for the calendar* rendering mask.* @method _getCutoffColumn* @param {Date} date The date of the month grid to compute the cutoff column for.* @param {Number} firstday The first day of the week (modified by internationalized calendars)* @private* @return {Number} The number of the cutoff column.*/_getCutoffColumn : function (date, firstday) {var distance = this._normalizeDate(date).getDay() - firstday,cutOffColumn = 6 - (distance + 7) % 7;return cutOffColumn;},/*** A render assist method that turns on the view of the previous month's dates* in a given calendar pane.* @method _turnPrevMonthOn* @param {Node} pane The calendar pane that needs its previous month's dates view* modified.* @protected*/_turnPrevMonthOn : function (pane) {var pane_id = pane.get("id"),pane_date = this._paneProperties[pane_id].paneDate,daysInPrevMonth = ydate.daysInMonth(ydate.addMonths(pane_date, -1)),cell;if (!this._paneProperties[pane_id].hasOwnProperty("daysInPrevMonth")) {this._paneProperties[pane_id].daysInPrevMonth = 0;}if (daysInPrevMonth !== this._paneProperties[pane_id].daysInPrevMonth) {this._paneProperties[pane_id].daysInPrevMonth = daysInPrevMonth;for (cell = 5; cell >= 0; cell--) {pane.one("#" + pane_id + "_" + cell + "_" + (cell-5)).set('text', daysInPrevMonth--);}}},/*** A render assist method that turns off the view of the previous month's dates* in a given calendar pane.* @method _turnPrevMonthOff* @param {Node} pane The calendar pane that needs its previous month's dates view* modified.* @protected*/_turnPrevMonthOff : function (pane) {var pane_id = pane.get("id"),cell;this._paneProperties[pane_id].daysInPrevMonth = 0;for (cell = 5; cell >= 0; cell--) {pane.one("#" + pane_id + "_" + cell + "_" + (cell-5)).setContent(" ");}},/*** A render assist method that cleans up the last few cells in the month grid* when the number of days in the month changes.* @method _cleanUpNextMonthCells* @param {Node} pane The calendar pane that needs the last date cells cleaned up.* @private*/_cleanUpNextMonthCells : function (pane) {var pane_id = pane.get("id");pane.one("#" + pane_id + "_6_29").removeClass(CAL_NEXTMONTH_DAY);pane.one("#" + pane_id + "_7_30").removeClass(CAL_NEXTMONTH_DAY);pane.one("#" + pane_id + "_8_31").removeClass(CAL_NEXTMONTH_DAY);pane.one("#" + pane_id + "_0_30").removeClass(CAL_NEXTMONTH_DAY);pane.one("#" + pane_id + "_1_31").removeClass(CAL_NEXTMONTH_DAY);},/*** A render assist method that turns on the view of the next month's dates* in a given calendar pane.* @method _turnNextMonthOn* @param {Node} pane The calendar pane that needs its next month's dates view* modified.* @protected*/_turnNextMonthOn : function (pane) {var dayCounter = 1,pane_id = pane.get("id"),daysInMonth = this._paneProperties[pane_id].daysInMonth,cutoffCol = this._paneProperties[pane_id].cutoffCol,cell,startingCell;for (cell = daysInMonth - 22; cell < cutoffCol + 7; cell++) {pane.one("#" + pane_id + "_" + cell + "_" + (cell+23)).set("text", dayCounter++).addClass(CAL_NEXTMONTH_DAY);}startingCell = cutoffCol;if (daysInMonth === 31 && (cutoffCol <= 1)) {startingCell = 2;} else if (daysInMonth === 30 && cutoffCol === 0) {startingCell = 1;}for (cell = startingCell ; cell < cutoffCol + 7; cell++) {pane.one("#" + pane_id + "_" + cell + "_" + (cell+30)).set("text", dayCounter++).addClass(CAL_NEXTMONTH_DAY);}},/*** A render assist method that turns off the view of the next month's dates* in a given calendar pane.* @method _turnNextMonthOff* @param {Node} pane The calendar pane that needs its next month's dates view* modified.* @protected*/_turnNextMonthOff : function (pane) {var pane_id = pane.get("id"),daysInMonth = this._paneProperties[pane_id].daysInMonth,cutoffCol = this._paneProperties[pane_id].cutoffCol,cell,startingCell;for (cell = daysInMonth - 22; cell <= 12; cell++) {pane.one("#" + pane_id + "_" + cell + "_" + (cell+23)).setContent(" ").addClass(CAL_NEXTMONTH_DAY);}startingCell = 0;if (daysInMonth === 31 && (cutoffCol <= 1)) {startingCell = 2;} else if (daysInMonth === 30 && cutoffCol === 0) {startingCell = 1;}for (cell = startingCell ; cell <= 12; cell++) {pane.one("#" + pane_id + "_" + cell + "_" + (cell+30)).setContent(" ").addClass(CAL_NEXTMONTH_DAY);}},/*** The handler for the change in the showNextMonth attribute.* @method _afterShowNextMonthChange* @private*/_afterShowNextMonthChange : function () {var contentBox = this.get('contentBox'),lastPane = contentBox.one("#" + this._calendarId + "_pane_" + (this._paneNumber - 1));this._cleanUpNextMonthCells(lastPane);if (this.get('showNextMonth')) {this._turnNextMonthOn(lastPane);} else {this._turnNextMonthOff(lastPane);}},/*** The handler for the change in the showPrevMonth attribute.* @method _afterShowPrevMonthChange* @private*/_afterShowPrevMonthChange : function () {var contentBox = this.get('contentBox'),firstPane = contentBox.one("#" + this._calendarId + "_pane_" + 0);if (this.get('showPrevMonth')) {this._turnPrevMonthOn(firstPane);} else {this._turnPrevMonthOff(firstPane);}},/*** The handler for the change in the headerRenderer attribute.* @method _afterHeaderRendererChange* @private*/_afterHeaderRendererChange : function () {var headerCell = this.get("contentBox").one("." + CAL_HD_LABEL);headerCell.setContent(this._updateCalendarHeader(this.get('date')));},/*** The handler for the change in the customRenderer attribute.* @method _afterCustomRendererChange* @private*/_afterCustomRendererChange : function () {this._restoreModifiedCells();this._renderCustomRules();},/*** The handler for the change in the date attribute. Modifies the calendar* view by shifting the calendar grid mask and running custom rendering and* selection rendering as necessary.* @method _afterDateChange* @private*/_afterDateChange : function () {var contentBox = this.get('contentBox'),headerCell = contentBox.one("." + CAL_HD).one("." + CAL_HD_LABEL),calendarPanes = contentBox.all("." + CAL_GRID),currentDate = this.get("date"),counter = 0;contentBox.setStyle("visibility", "hidden");headerCell.setContent(this._updateCalendarHeader(currentDate));this._restoreModifiedCells();calendarPanes.each(function (curNode) {this._rerenderCalendarPane(ydate.addMonths(currentDate, counter++), curNode);}, this);this._afterShowPrevMonthChange();this._afterShowNextMonthChange();this._renderCustomRules();this._renderSelectedDates();contentBox.setStyle("visibility", "inherit");},/*** A rendering assist method that initializes the HTML for a single* calendar pane.* @method _initCalendarPane* @param {Date} baseDate The date corresponding to the month of the given* calendar pane.* @param {String} pane_id The id of the pane, to be used as a prefix for* element ids in the given pane.* @private*/_initCalendarPane : function (baseDate, pane_id) {var dateFormat = Y.Intl.get('datatype-date-format'),weekDays = dateFormat.A,// Get the first day of the week from the internationalization package, or else use Sunday as default.firstday = this.get('strings.first_weekday') || 0,// Compute the cutoff column of the masked calendar table, based on the start date and the first day of week.cutoffCol = this._getCutoffColumn(baseDate, firstday),// Compute the number of days in the month based on starting datedaysInMonth = ydate.daysInMonth(baseDate),// Initialize the array of individual row HTML stringsrow_array = ['','','','','',''],// Initialize the partial templates objectpartials = {},shortWeekDays,day,row,column,date,id_date,calendar_day_class,column_visibility,output;if (Y.Intl.getLang('calendar-base')) {shortWeekDays = this.get('strings.very_short_weekdays');} else {shortWeekDays = dateFormat.a;}// Initialize the partial template for the weekday row cells.partials.weekday_row = '';// Populate the partial template for the weekday row cells with weekday namesfor (day = firstday; day <= firstday + 6; day++) {partials.weekday_row +=substitute(CalendarBase.WEEKDAY_TEMPLATE, {short_weekdayname: shortWeekDays[day%7],weekdayname: weekDays[day%7]});}// Populate the partial template for the weekday row container with the weekday row cellspartials.weekday_row_template = substitute(CalendarBase.WEEKDAY_ROW_TEMPLATE, partials);// Populate the array of individual row HTML stringsfor (row = 0; row <= 5; row++) {for (column = 0; column <= 12; column++) {// Compute the value of the date that needs to populate the celldate = 7*row - 5 + column;// Compose the value of the unique id of the current calendar cellid_date = pane_id + "_" + column + "_" + date;// Set the calendar day class to one of three possible valuescalendar_day_class = CAL_DAY;if (date < 1) {calendar_day_class = CAL_PREVMONTH_DAY;} else if (date > daysInMonth) {calendar_day_class = CAL_NEXTMONTH_DAY;}// Cut off dates that fall before the first and after the last date of the monthif (date < 1 || date > daysInMonth) {date = " ";}// Decide on whether a column in the masked table is visible or not based on the value of the cutoff column.column_visibility = (column >= cutoffCol && column < (cutoffCol + 7)) ? '' : CAL_COL_HIDDEN;// Substitute the values into the partial calendar day template and add it to the current row HTML stringrow_array[row] += substitute (CalendarBase.CALDAY_TEMPLATE, {day_content: date,calendar_col_class: "calendar_col" + column,calendar_col_visibility_class: column_visibility,calendar_day_class: calendar_day_class,calendar_day_id: id_date});}}// Instantiate the partial calendar pane body templatepartials.body_template = '';// Populate the body template with the rows templatesarrayEach (row_array, function (v) {partials.body_template += substitute(CalendarBase.CALDAY_ROW_TEMPLATE, {calday_row: v});});// Populate the calendar grid idpartials.calendar_pane_id = pane_id;// Populate the calendar pane tabindexpartials.calendar_pane_tabindex = this.get("tabIndex");partials.pane_arialabel = ydate.format(baseDate, { format: "%B %Y" });// Generate final output by substituting class names.output = substitute(substitute (CalendarBase.CALENDAR_GRID_TEMPLATE, partials),CalendarBase.CALENDAR_STRINGS);// Store the initialized pane informationthis._paneProperties[pane_id] = {cutoffCol: cutoffCol, daysInMonth: daysInMonth, paneDate: baseDate};return output;},/*** A rendering assist method that rerenders a specified calendar pane, based* on a new Date.* @method _rerenderCalendarPane* @param {Date} newDate The date corresponding to the month of the given* calendar pane.* @param {Node} pane The node corresponding to the calendar pane to be rerenders.* @private*/_rerenderCalendarPane : function (newDate, pane) {// Get the first day of the week from the internationalization package, or else use Sunday as default.var firstday = this.get('strings.first_weekday') || 0,// Compute the cutoff column of the masked calendar table, based on the start date and the first day of week.cutoffCol = this._getCutoffColumn(newDate, firstday),// Compute the number of days in the month based on starting datedaysInMonth = ydate.daysInMonth(newDate),// Get pane id for easier referencepaneId = pane.get("id"),column,currentColumn,curCell;// Hide the pane before making DOM changes to speed them uppane.setStyle("visibility", "hidden");pane.setAttribute("aria-label", ydate.format(newDate, {format:"%B %Y"}));// Go through all columns, and flip their visibility setting based on whether they are within the unmasked range.for (column = 0; column <= 12; column++) {currentColumn = pane.all("." + "calendar_col" + column);currentColumn.removeClass(CAL_COL_HIDDEN);if (column < cutoffCol || column >= (cutoffCol + 7)) {currentColumn.addClass(CAL_COL_HIDDEN);} else {// Clean up dates in visible columns to account for the correct number of days in a monthswitch(column) {case 0:curCell = pane.one("#" + paneId + "_0_30");if (daysInMonth >= 30) {curCell.set("text", "30");curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);} else {curCell.setContent(" ");curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);}break;case 1:curCell = pane.one("#" + paneId + "_1_31");if (daysInMonth >= 31) {curCell.set("text", "31");curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);} else {curCell.setContent(" ");curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);}break;case 6:curCell = pane.one("#" + paneId + "_6_29");if (daysInMonth >= 29) {curCell.set("text", "29");curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);} else {curCell.setContent(" ");curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);}break;case 7:curCell = pane.one("#" + paneId + "_7_30");if (daysInMonth >= 30) {curCell.set("text", "30");curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);} else {curCell.setContent(" ");curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);}break;case 8:curCell = pane.one("#" + paneId + "_8_31");if (daysInMonth >= 31) {curCell.set("text", "31");curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);} else {curCell.setContent(" ");curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);}break;}}}// Update stored pane propertiesthis._paneProperties[paneId].cutoffCol = cutoffCol;this._paneProperties[paneId].daysInMonth = daysInMonth;this._paneProperties[paneId].paneDate = newDate;// Bring the pane visibility back after all DOM changes are donepane.setStyle("visibility", "inherit");},/*** A rendering assist method that updates the calendar header based* on a given date and potentially the provided headerRenderer.* @method _updateCalendarHeader* @param {Date} baseDate The date with which to update the calendar header.* @private*/_updateCalendarHeader : function (baseDate) {var headerString = "",headerRenderer = this.get("headerRenderer");if (Y.Lang.isString(headerRenderer)) {headerString = ydate.format(baseDate, {format:headerRenderer});} else if (headerRenderer instanceof Function) {headerString = headerRenderer.call(this, baseDate);}return headerString;},/*** A rendering assist method that initializes the calendar header HTML* based on a given date and potentially the provided headerRenderer.* @method _initCalendarHeader* @param {Date} baseDate The date with which to initialize the calendar header.* @private*/_initCalendarHeader : function (baseDate) {return substitute(substitute(CalendarBase.HEADER_TEMPLATE, {calheader: this._updateCalendarHeader(baseDate),calendar_id: this._calendarId}), CalendarBase.CALENDAR_STRINGS );},/*** A rendering assist method that initializes the calendar HTML* based on a given date.* @method _initCalendarHTML* @param {Date} baseDate The date with which to initialize the calendar.* @private*/_initCalendarHTML : function (baseDate) {// Instantiate the partials holdervar partials = {},// Counter for iterative template replacement.counter = 0,singlePane,output;// Generate the template for the headerpartials.header_template = this._initCalendarHeader(baseDate);partials.calendar_id = this._calendarId;partials.body_template = substitute(substitute (CalendarBase.CONTENT_TEMPLATE, partials),CalendarBase.CALENDAR_STRINGS);// Instantiate the iterative template replacer functionfunction paneReplacer () {singlePane = this._initCalendarPane(ydate.addMonths(baseDate, counter), partials.calendar_id + "_pane_" + counter);counter++;return singlePane;}// Go through all occurrences of the calendar_grid_template token and replace it with an appropriate calendar grid.output = partials.body_template.replace(/\{calendar_grid_template\}/g, Y.bind(paneReplacer, this));// Update the paneNumber countthis._paneNumber = counter;return output;}}, {/*** The CSS classnames for the calendar templates.* @property CALENDAR_STRINGS* @type Object* @readOnly* @protected* @static*/CALENDAR_STRINGS: {calendar_grid_class : CAL_GRID,calendar_body_class : CAL_BODY,calendar_hd_class : CAL_HD,calendar_hd_label_class : CAL_HD_LABEL,calendar_weekdayrow_class : CAL_WDAYROW,calendar_weekday_class : CAL_WDAY,calendar_row_class : CAL_ROW,calendar_day_class : CAL_DAY,calendar_dayanchor_class : CAL_ANCHOR,calendar_pane_class : CAL_PANE,calendar_right_grid_class : CAL_RIGHT_GRID,calendar_left_grid_class : CAL_LEFT_GRID,calendar_status_class : CAL_STATUS},/*ARIA_STATUS_TEMPLATE: '<div role="status" aria-atomic="true" class="{calendar_status_class}"></div>',AriaStatus : null,updateStatus : function (statusString) {if (!CalendarBase.AriaStatus) {CalendarBase.AriaStatus = create(substitute (CalendarBase.ARIA_STATUS_TEMPLATE,CalendarBase.CALENDAR_STRINGS));Y.one("body").append(CalendarBase.AriaStatus);}CalendarBase.AriaStatus.set("text", statusString);},*//*** The main content template for calendar.* @property CONTENT_TEMPLATE* @type String* @protected* @static*/CONTENT_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +'{header_template}' +'<div class="yui3-u-1">' +'{calendar_grid_template}' +'</div>' +'</div>',/*** A single pane template for calendar (same as default CONTENT_TEMPLATE)* @property ONE_PANE_TEMPLATE* @type String* @protected* @readOnly* @static*/ONE_PANE_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +'{header_template}' +'<div class="yui3-u-1">' +'{calendar_grid_template}' +'</div>' +'</div>',/*** A two pane template for calendar.* @property TWO_PANE_TEMPLATE* @type String* @protected* @readOnly* @static*/TWO_PANE_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +'{header_template}' +'<div class="yui3-u-1-2">'+'<div class = "{calendar_left_grid_class}">' +'{calendar_grid_template}' +'</div>' +'</div>' +'<div class="yui3-u-1-2">' +'<div class = "{calendar_right_grid_class}">' +'{calendar_grid_template}' +'</div>' +'</div>' +'</div>',/*** A three pane template for calendar.* @property THREE_PANE_TEMPLATE* @type String* @protected* @readOnly* @static*/THREE_PANE_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +'{header_template}' +'<div class="yui3-u-1-3">' +'<div class="{calendar_left_grid_class}">' +'{calendar_grid_template}' +'</div>' +'</div>' +'<div class="yui3-u-1-3">' +'{calendar_grid_template}' +'</div>' +'<div class="yui3-u-1-3">' +'<div class="{calendar_right_grid_class}">' +'{calendar_grid_template}' +'</div>' +'</div>' +'</div>',/*** A template for the calendar grid.* @property CALENDAR_GRID_TEMPLATE* @type String* @protected* @static*/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}">' +'<thead>' +'{weekday_row_template}' +'</thead>' +'<tbody>' +'{body_template}' +'</tbody>' +'</table>',/*** A template for the calendar header.* @property HEADER_TEMPLATE* @type String* @protected* @static*/HEADER_TEMPLATE: '<div class="yui3-g {calendar_hd_class}">' +'<div class="yui3-u {calendar_hd_label_class}" id="{calendar_id}_header" aria-role="heading">' +'{calheader}' +'</div>' +'</div>',/*** A template for the row of weekday names.* @property WEEKDAY_ROW_TEMPLATE* @type String* @protected* @static*/WEEKDAY_ROW_TEMPLATE: '<tr class="{calendar_weekdayrow_class}" role="row">' +'{weekday_row}' +'</tr>',/*** A template for a single row of calendar days.* @property CALDAY_ROW_TEMPLATE* @type String* @protected* @static*/CALDAY_ROW_TEMPLATE: '<tr class="{calendar_row_class}" role="row">' +'{calday_row}' +'</tr>',/*** A template for a single cell with a weekday name.* @property WEEKDAY_TEMPLATE* @type String* @protected* @static*/WEEKDAY_TEMPLATE: '<th class="{calendar_weekday_class}" role="columnheader" aria-label="{weekdayname}">{short_weekdayname}</th>',/*** A template for a single cell with a calendar day.* @property CALDAY_TEMPLATE* @type String* @protected* @static*/CALDAY_TEMPLATE: '<td class="{calendar_col_class} {calendar_day_class} {calendar_col_visibility_class}" id="{calendar_day_id}" ' +'role="gridcell" tabindex="-1">' +'{day_content}' +'</td>',/*** The identity of the widget.** @property NAME* @type String* @default 'calendarBase'* @readOnly* @protected* @static*/NAME: 'calendarBase',/*** Static property used to define the default attribute configuration of* the Widget.** @property ATTRS* @type {Object}* @protected* @static*/ATTRS: {tabIndex: {value: 1},/*** The date corresponding to the current calendar view. Always* normalized to the first of the month that contains the date* at assignment time. Used as the first date visible in the* calendar.** @attribute date* @type Date* @default The first of the month containing today's date, as* set on the end user's system.*/date: {value: new Date(),setter: function (val) {var newDate = this._normalizeDate(val);if (ydate.areEqual(newDate, this.get('date'))) {return this.get('date');} else {return newDate;}}},/*** A setting specifying whether to shows days from the previous* month in the visible month's grid, if there are empty preceding* cells available.** @attribute showPrevMonth* @type boolean* @default false*/showPrevMonth: {value: false},/*** A setting specifying whether to shows days from the next* month in the visible month's grid, if there are empty* cells available at the end.** @attribute showNextMonth* @type boolean* @default false*/showNextMonth: {value: false},/*** Strings and properties derived from the internationalization packages* for the calendar.** @attribute strings* @type Object* @protected*/strings : {valueFn: function() { return Y.Intl.get("calendar-base"); }},/*** Custom header renderer for the calendar.** @attribute headerRenderer* @type String | Function*/headerRenderer: {value: "%B %Y"},/*** The name of the rule which all enabled dates should match.* Either disabledDatesRule or enabledDatesRule should be specified,* or neither, but not both.** @attribute enabledDatesRule* @type String* @default null*/enabledDatesRule: {value: null},/*** The name of the rule which all disabled dates should match.* Either disabledDatesRule or enabledDatesRule should be specified,* or neither, but not both.** @attribute disabledDatesRule* @type String* @default null*/disabledDatesRule: {value: null},/*** A read-only attribute providing a list of currently selected dates.** @attribute selectedDates* @readOnly* @type Array*/selectedDates : {readOnly: true,getter: function () {return (this._getSelectedDatesList());}},/*** An object of the form {rules:Object, filterFunction:Function},* providing set of rules and a custom rendering function for* customizing specific calendar cells.** @attribute customRenderer* @type Object* @default {}*/customRenderer : {lazyAdd: false,value: {},setter: function (val) {this._rules = val.rules;this._filterFunction = val.filterFunction;}}}});}, '3.18.1', {"requires": ["widget","datatype-date","datatype-date-math","cssgrids"],"lang": ["de","en","es","es-AR","fr","hu","it","ja","nb-NO","nl","pt-BR","ru","zh-Hans","zh-Hans-CN","zh-Hant","zh-Hant-HK","zh-HANT-TW"],"skinnable": true});