You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1721 lines
59 KiB
1721 lines
59 KiB
2 years ago
|
/*
|
||
|
YUI 3.17.2 (build 9c3c78e)
|
||
|
Copyright 2014 Yahoo! Inc. All rights reserved.
|
||
|
Licensed under the BSD License.
|
||
|
http://yuilibrary.com/license/
|
||
|
*/
|
||
|
|
||
|
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) {
|
||
|
// Get a list of short weekdays from the internationalization package, or else use default English ones.
|
||
|
var shortWeekDays = this.get('strings.very_short_weekdays') || ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
|
||
|
weekDays = Y.Intl.get('datatype-date-format').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 date
|
||
|
daysInMonth = ydate.daysInMonth(baseDate),
|
||
|
// Initialize the array of individual row HTML strings
|
||
|
row_array = ['','','','','',''],
|
||
|
// Initialize the partial templates object
|
||
|
partials = {},
|
||
|
|
||
|
day,
|
||
|
row,
|
||
|
column,
|
||
|
date,
|
||
|
id_date,
|
||
|
calendar_day_class,
|
||
|
column_visibility,
|
||
|
output;
|
||
|
|
||
|
// Initialize the partial template for the weekday row cells.
|
||
|
partials.weekday_row = '';
|
||
|
|
||
|
// Populate the partial template for the weekday row cells with weekday names
|
||
|
for (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 cells
|
||
|
partials.weekday_row_template = substitute(CalendarBase.WEEKDAY_ROW_TEMPLATE, partials);
|
||
|
|
||
|
// Populate the array of individual row HTML strings
|
||
|
for (row = 0; row <= 5; row++) {
|
||
|
|
||
|
for (column = 0; column <= 12; column++) {
|
||
|
|
||
|
// Compute the value of the date that needs to populate the cell
|
||
|
date = 7*row - 5 + column;
|
||
|
|
||
|
// Compose the value of the unique id of the current calendar cell
|
||
|
id_date = pane_id + "_" + column + "_" + date;
|
||
|
|
||
|
// Set the calendar day class to one of three possible values
|
||
|
calendar_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 month
|
||
|
if (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 string
|
||
|
row_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 template
|
||
|
partials.body_template = '';
|
||
|
|
||
|
// Populate the body template with the rows templates
|
||
|
arrayEach (row_array, function (v) {
|
||
|
partials.body_template += substitute(CalendarBase.CALDAY_ROW_TEMPLATE, {calday_row: v});
|
||
|
});
|
||
|
|
||
|
// Populate the calendar grid id
|
||
|
partials.calendar_pane_id = pane_id;
|
||
|
|
||
|
// Populate the calendar pane tabindex
|
||
|
partials.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 information
|
||
|
this._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 date
|
||
|
daysInMonth = ydate.daysInMonth(newDate),
|
||
|
// Get pane id for easier reference
|
||
|
paneId = pane.get("id"),
|
||
|
column,
|
||
|
currentColumn,
|
||
|
curCell;
|
||
|
|
||
|
// Hide the pane before making DOM changes to speed them up
|
||
|
pane.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 month
|
||
|
switch(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 properties
|
||
|
this._paneProperties[paneId].cutoffCol = cutoffCol;
|
||
|
this._paneProperties[paneId].daysInMonth = daysInMonth;
|
||
|
this._paneProperties[paneId].paneDate = newDate;
|
||
|
|
||
|
// Bring the pane visibility back after all DOM changes are done
|
||
|
pane.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 holder
|
||
|
var partials = {},
|
||
|
// Counter for iterative template replacement.
|
||
|
counter = 0,
|
||
|
singlePane,
|
||
|
output;
|
||
|
|
||
|
// Generate the template for the header
|
||
|
partials.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 function
|
||
|
function 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 count
|
||
|
this._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.17.2', {
|
||
|
"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
|
||
|
});
|