API Docs for: 3.18.1
Show:

File: calendar/js/calendar-base.js

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