Version 3.17.2
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. // Get a list of short weekdays from the internationalization package, or else use default English ones.
  1059. var shortWeekDays = this.get('strings.very_short_weekdays') || ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
  1060. weekDays = Y.Intl.get('datatype-date-format').A,
  1061. // Get the first day of the week from the internationalization package, or else use Sunday as default.
  1062. firstday = this.get('strings.first_weekday') || 0,
  1063. // Compute the cutoff column of the masked calendar table, based on the start date and the first day of week.
  1064. cutoffCol = this._getCutoffColumn(baseDate, firstday),
  1065. // Compute the number of days in the month based on starting date
  1066. daysInMonth = ydate.daysInMonth(baseDate),
  1067. // Initialize the array of individual row HTML strings
  1068. row_array = ['','','','','',''],
  1069. // Initialize the partial templates object
  1070. partials = {},
  1071.  
  1072. day,
  1073. row,
  1074. column,
  1075. date,
  1076. id_date,
  1077. calendar_day_class,
  1078. column_visibility,
  1079. output;
  1080.  
  1081. // Initialize the partial template for the weekday row cells.
  1082. partials.weekday_row = '';
  1083.  
  1084. // Populate the partial template for the weekday row cells with weekday names
  1085. for (day = firstday; day <= firstday + 6; day++) {
  1086. partials.weekday_row +=
  1087. substitute(CalendarBase.WEEKDAY_TEMPLATE, {
  1088. short_weekdayname: shortWeekDays[day%7],
  1089. weekdayname: weekDays[day%7]
  1090. });
  1091. }
  1092.  
  1093. // Populate the partial template for the weekday row container with the weekday row cells
  1094. partials.weekday_row_template = substitute(CalendarBase.WEEKDAY_ROW_TEMPLATE, partials);
  1095.  
  1096. // Populate the array of individual row HTML strings
  1097. for (row = 0; row <= 5; row++) {
  1098.  
  1099. for (column = 0; column <= 12; column++) {
  1100.  
  1101. // Compute the value of the date that needs to populate the cell
  1102. date = 7*row - 5 + column;
  1103.  
  1104. // Compose the value of the unique id of the current calendar cell
  1105. id_date = pane_id + "_" + column + "_" + date;
  1106.  
  1107. // Set the calendar day class to one of three possible values
  1108. calendar_day_class = CAL_DAY;
  1109.  
  1110. if (date < 1) {
  1111. calendar_day_class = CAL_PREVMONTH_DAY;
  1112. } else if (date > daysInMonth) {
  1113. calendar_day_class = CAL_NEXTMONTH_DAY;
  1114. }
  1115.  
  1116. // Cut off dates that fall before the first and after the last date of the month
  1117. if (date < 1 || date > daysInMonth) {
  1118. date = "&nbsp;";
  1119. }
  1120.  
  1121. // Decide on whether a column in the masked table is visible or not based on the value of the cutoff column.
  1122. column_visibility = (column >= cutoffCol && column < (cutoffCol + 7)) ? '' : CAL_COL_HIDDEN;
  1123.  
  1124. // Substitute the values into the partial calendar day template and add it to the current row HTML string
  1125. row_array[row] += substitute (CalendarBase.CALDAY_TEMPLATE, {
  1126. day_content: date,
  1127. calendar_col_class: "calendar_col" + column,
  1128. calendar_col_visibility_class: column_visibility,
  1129. calendar_day_class: calendar_day_class,
  1130. calendar_day_id: id_date
  1131. });
  1132. }
  1133. }
  1134.  
  1135. // Instantiate the partial calendar pane body template
  1136. partials.body_template = '';
  1137.  
  1138. // Populate the body template with the rows templates
  1139. arrayEach (row_array, function (v) {
  1140. partials.body_template += substitute(CalendarBase.CALDAY_ROW_TEMPLATE, {calday_row: v});
  1141. });
  1142.  
  1143. // Populate the calendar grid id
  1144. partials.calendar_pane_id = pane_id;
  1145.  
  1146. // Populate the calendar pane tabindex
  1147. partials.calendar_pane_tabindex = this.get("tabIndex");
  1148. partials.pane_arialabel = ydate.format(baseDate, { format: "%B %Y" });
  1149.  
  1150.  
  1151. // Generate final output by substituting class names.
  1152. output = substitute(substitute (CalendarBase.CALENDAR_GRID_TEMPLATE, partials),
  1153. CalendarBase.CALENDAR_STRINGS);
  1154.  
  1155. // Store the initialized pane information
  1156. this._paneProperties[pane_id] = {cutoffCol: cutoffCol, daysInMonth: daysInMonth, paneDate: baseDate};
  1157.  
  1158. return output;
  1159. },
  1160.  
  1161. /**
  1162. * A rendering assist method that rerenders a specified calendar pane, based
  1163. * on a new Date.
  1164. * @method _rerenderCalendarPane
  1165. * @param {Date} newDate The date corresponding to the month of the given
  1166. * calendar pane.
  1167. * @param {Node} pane The node corresponding to the calendar pane to be rerenders.
  1168. * @private
  1169. */
  1170. _rerenderCalendarPane : function (newDate, pane) {
  1171.  
  1172. // Get the first day of the week from the internationalization package, or else use Sunday as default.
  1173. var firstday = this.get('strings.first_weekday') || 0,
  1174. // Compute the cutoff column of the masked calendar table, based on the start date and the first day of week.
  1175. cutoffCol = this._getCutoffColumn(newDate, firstday),
  1176. // Compute the number of days in the month based on starting date
  1177. daysInMonth = ydate.daysInMonth(newDate),
  1178. // Get pane id for easier reference
  1179. paneId = pane.get("id"),
  1180. column,
  1181. currentColumn,
  1182. curCell;
  1183.  
  1184. // Hide the pane before making DOM changes to speed them up
  1185. pane.setStyle("visibility", "hidden");
  1186. pane.setAttribute("aria-label", ydate.format(newDate, {format:"%B %Y"}));
  1187.  
  1188. // Go through all columns, and flip their visibility setting based on whether they are within the unmasked range.
  1189. for (column = 0; column <= 12; column++) {
  1190. currentColumn = pane.all("." + "calendar_col" + column);
  1191. currentColumn.removeClass(CAL_COL_HIDDEN);
  1192.  
  1193. if (column < cutoffCol || column >= (cutoffCol + 7)) {
  1194. currentColumn.addClass(CAL_COL_HIDDEN);
  1195. } else {
  1196. // Clean up dates in visible columns to account for the correct number of days in a month
  1197. switch(column) {
  1198. case 0:
  1199. curCell = pane.one("#" + paneId + "_0_30");
  1200. if (daysInMonth >= 30) {
  1201. curCell.set("text", "30");
  1202. curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);
  1203. } else {
  1204. curCell.setContent("&nbsp;");
  1205. curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);
  1206. }
  1207. break;
  1208. case 1:
  1209. curCell = pane.one("#" + paneId + "_1_31");
  1210. if (daysInMonth >= 31) {
  1211. curCell.set("text", "31");
  1212. curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);
  1213. } else {
  1214. curCell.setContent("&nbsp;");
  1215. curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);
  1216. }
  1217. break;
  1218. case 6:
  1219. curCell = pane.one("#" + paneId + "_6_29");
  1220. if (daysInMonth >= 29) {
  1221. curCell.set("text", "29");
  1222. curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);
  1223. } else {
  1224. curCell.setContent("&nbsp;");
  1225. curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);
  1226. }
  1227. break;
  1228. case 7:
  1229. curCell = pane.one("#" + paneId + "_7_30");
  1230. if (daysInMonth >= 30) {
  1231. curCell.set("text", "30");
  1232. curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);
  1233. } else {
  1234. curCell.setContent("&nbsp;");
  1235. curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);
  1236. }
  1237. break;
  1238. case 8:
  1239. curCell = pane.one("#" + paneId + "_8_31");
  1240. if (daysInMonth >= 31) {
  1241. curCell.set("text", "31");
  1242. curCell.removeClass(CAL_NEXTMONTH_DAY).addClass(CAL_DAY);
  1243. } else {
  1244. curCell.setContent("&nbsp;");
  1245. curCell.removeClass(CAL_DAY).addClass(CAL_NEXTMONTH_DAY);
  1246. }
  1247. break;
  1248. }
  1249. }
  1250. }
  1251.  
  1252. // Update stored pane properties
  1253. this._paneProperties[paneId].cutoffCol = cutoffCol;
  1254. this._paneProperties[paneId].daysInMonth = daysInMonth;
  1255. this._paneProperties[paneId].paneDate = newDate;
  1256.  
  1257. // Bring the pane visibility back after all DOM changes are done
  1258. pane.setStyle("visibility", "inherit");
  1259.  
  1260. },
  1261.  
  1262. /**
  1263. * A rendering assist method that updates the calendar header based
  1264. * on a given date and potentially the provided headerRenderer.
  1265. * @method _updateCalendarHeader
  1266. * @param {Date} baseDate The date with which to update the calendar header.
  1267. * @private
  1268. */
  1269. _updateCalendarHeader : function (baseDate) {
  1270. var headerString = "",
  1271. headerRenderer = this.get("headerRenderer");
  1272.  
  1273. if (Y.Lang.isString(headerRenderer)) {
  1274. headerString = ydate.format(baseDate, {format:headerRenderer});
  1275. } else if (headerRenderer instanceof Function) {
  1276. headerString = headerRenderer.call(this, baseDate);
  1277. }
  1278.  
  1279. return headerString;
  1280. },
  1281.  
  1282. /**
  1283. * A rendering assist method that initializes the calendar header HTML
  1284. * based on a given date and potentially the provided headerRenderer.
  1285. * @method _initCalendarHeader
  1286. * @param {Date} baseDate The date with which to initialize the calendar header.
  1287. * @private
  1288. */
  1289. _initCalendarHeader : function (baseDate) {
  1290. return substitute(substitute(CalendarBase.HEADER_TEMPLATE, {
  1291. calheader: this._updateCalendarHeader(baseDate),
  1292. calendar_id: this._calendarId
  1293. }), CalendarBase.CALENDAR_STRINGS );
  1294. },
  1295.  
  1296. /**
  1297. * A rendering assist method that initializes the calendar HTML
  1298. * based on a given date.
  1299. * @method _initCalendarHTML
  1300. * @param {Date} baseDate The date with which to initialize the calendar.
  1301. * @private
  1302. */
  1303. _initCalendarHTML : function (baseDate) {
  1304. // Instantiate the partials holder
  1305. var partials = {},
  1306. // Counter for iterative template replacement.
  1307. counter = 0,
  1308. singlePane,
  1309. output;
  1310.  
  1311. // Generate the template for the header
  1312. partials.header_template = this._initCalendarHeader(baseDate);
  1313. partials.calendar_id = this._calendarId;
  1314.  
  1315. partials.body_template = substitute(substitute (CalendarBase.CONTENT_TEMPLATE, partials),
  1316. CalendarBase.CALENDAR_STRINGS);
  1317.  
  1318. // Instantiate the iterative template replacer function
  1319. function paneReplacer () {
  1320. singlePane = this._initCalendarPane(ydate.addMonths(baseDate, counter), partials.calendar_id + "_pane_" + counter);
  1321. counter++;
  1322. return singlePane;
  1323. }
  1324.  
  1325. // Go through all occurrences of the calendar_grid_template token and replace it with an appropriate calendar grid.
  1326. output = partials.body_template.replace(/\{calendar_grid_template\}/g, Y.bind(paneReplacer, this));
  1327.  
  1328. // Update the paneNumber count
  1329. this._paneNumber = counter;
  1330.  
  1331. return output;
  1332. }
  1333. }, {
  1334.  
  1335. /**
  1336. * The CSS classnames for the calendar templates.
  1337. * @property CALENDAR_STRINGS
  1338. * @type Object
  1339. * @readOnly
  1340. * @protected
  1341. * @static
  1342. */
  1343. CALENDAR_STRINGS: {
  1344. calendar_grid_class : CAL_GRID,
  1345. calendar_body_class : CAL_BODY,
  1346. calendar_hd_class : CAL_HD,
  1347. calendar_hd_label_class : CAL_HD_LABEL,
  1348. calendar_weekdayrow_class : CAL_WDAYROW,
  1349. calendar_weekday_class : CAL_WDAY,
  1350. calendar_row_class : CAL_ROW,
  1351. calendar_day_class : CAL_DAY,
  1352. calendar_dayanchor_class : CAL_ANCHOR,
  1353. calendar_pane_class : CAL_PANE,
  1354. calendar_right_grid_class : CAL_RIGHT_GRID,
  1355. calendar_left_grid_class : CAL_LEFT_GRID,
  1356. calendar_status_class : CAL_STATUS
  1357. },
  1358.  
  1359. /*
  1360.  
  1361. ARIA_STATUS_TEMPLATE: '<div role="status" aria-atomic="true" class="{calendar_status_class}"></div>',
  1362.  
  1363. AriaStatus : null,
  1364.  
  1365. updateStatus : function (statusString) {
  1366.  
  1367. if (!CalendarBase.AriaStatus) {
  1368. CalendarBase.AriaStatus = create(
  1369. substitute (CalendarBase.ARIA_STATUS_TEMPLATE,
  1370. CalendarBase.CALENDAR_STRINGS));
  1371. Y.one("body").append(CalendarBase.AriaStatus);
  1372. }
  1373.  
  1374. CalendarBase.AriaStatus.set("text", statusString);
  1375. },
  1376.  
  1377. */
  1378.  
  1379. /**
  1380. * The main content template for calendar.
  1381. * @property CONTENT_TEMPLATE
  1382. * @type String
  1383. * @protected
  1384. * @static
  1385. */
  1386. CONTENT_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +
  1387. '{header_template}' +
  1388. '<div class="yui3-u-1">' +
  1389. '{calendar_grid_template}' +
  1390. '</div>' +
  1391. '</div>',
  1392.  
  1393. /**
  1394. * A single pane template for calendar (same as default CONTENT_TEMPLATE)
  1395. * @property ONE_PANE_TEMPLATE
  1396. * @type String
  1397. * @protected
  1398. * @readOnly
  1399. * @static
  1400. */
  1401. ONE_PANE_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +
  1402. '{header_template}' +
  1403. '<div class="yui3-u-1">' +
  1404. '{calendar_grid_template}' +
  1405. '</div>' +
  1406. '</div>',
  1407.  
  1408. /**
  1409. * A two pane template for calendar.
  1410. * @property TWO_PANE_TEMPLATE
  1411. * @type String
  1412. * @protected
  1413. * @readOnly
  1414. * @static
  1415. */
  1416. TWO_PANE_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +
  1417. '{header_template}' +
  1418. '<div class="yui3-u-1-2">'+
  1419. '<div class = "{calendar_left_grid_class}">' +
  1420. '{calendar_grid_template}' +
  1421. '</div>' +
  1422. '</div>' +
  1423. '<div class="yui3-u-1-2">' +
  1424. '<div class = "{calendar_right_grid_class}">' +
  1425. '{calendar_grid_template}' +
  1426. '</div>' +
  1427. '</div>' +
  1428. '</div>',
  1429. /**
  1430. * A three pane template for calendar.
  1431. * @property THREE_PANE_TEMPLATE
  1432. * @type String
  1433. * @protected
  1434. * @readOnly
  1435. * @static
  1436. */
  1437. THREE_PANE_TEMPLATE: '<div class="yui3-g {calendar_pane_class}" id="{calendar_id}">' +
  1438. '{header_template}' +
  1439. '<div class="yui3-u-1-3">' +
  1440. '<div class="{calendar_left_grid_class}">' +
  1441. '{calendar_grid_template}' +
  1442. '</div>' +
  1443. '</div>' +
  1444. '<div class="yui3-u-1-3">' +
  1445. '{calendar_grid_template}' +
  1446. '</div>' +
  1447. '<div class="yui3-u-1-3">' +
  1448. '<div class="{calendar_right_grid_class}">' +
  1449. '{calendar_grid_template}' +
  1450. '</div>' +
  1451. '</div>' +
  1452. '</div>',
  1453. /**
  1454. * A template for the calendar grid.
  1455. * @property CALENDAR_GRID_TEMPLATE
  1456. * @type String
  1457. * @protected
  1458. * @static
  1459. */
  1460. CALENDAR_GRID_TEMPLATE: '<table class="{calendar_grid_class}" id="{calendar_pane_id}" role="grid" aria-readonly="true" ' +
  1461. 'aria-label="{pane_arialabel}" tabindex="{calendar_pane_tabindex}">' +
  1462. '<thead>' +
  1463. '{weekday_row_template}' +
  1464. '</thead>' +
  1465. '<tbody>' +
  1466. '{body_template}' +
  1467. '</tbody>' +
  1468. '</table>',
  1469.  
  1470. /**
  1471. * A template for the calendar header.
  1472. * @property HEADER_TEMPLATE
  1473. * @type String
  1474. * @protected
  1475. * @static
  1476. */
  1477. HEADER_TEMPLATE: '<div class="yui3-g {calendar_hd_class}">' +
  1478. '<div class="yui3-u {calendar_hd_label_class}" id="{calendar_id}_header" aria-role="heading">' +
  1479. '{calheader}' +
  1480. '</div>' +
  1481. '</div>',
  1482.  
  1483. /**
  1484. * A template for the row of weekday names.
  1485. * @property WEEKDAY_ROW_TEMPLATE
  1486. * @type String
  1487. * @protected
  1488. * @static
  1489. */
  1490. WEEKDAY_ROW_TEMPLATE: '<tr class="{calendar_weekdayrow_class}" role="row">' +
  1491. '{weekday_row}' +
  1492. '</tr>',
  1493.  
  1494. /**
  1495. * A template for a single row of calendar days.
  1496. * @property CALDAY_ROW_TEMPLATE
  1497. * @type String
  1498. * @protected
  1499. * @static
  1500. */
  1501. CALDAY_ROW_TEMPLATE: '<tr class="{calendar_row_class}" role="row">' +
  1502. '{calday_row}' +
  1503. '</tr>',
  1504.  
  1505. /**
  1506. * A template for a single cell with a weekday name.
  1507. * @property WEEKDAY_TEMPLATE
  1508. * @type String
  1509. * @protected
  1510. * @static
  1511. */
  1512. WEEKDAY_TEMPLATE: '<th class="{calendar_weekday_class}" role="columnheader" aria-label="{weekdayname}">{short_weekdayname}</th>',
  1513.  
  1514. /**
  1515. * A template for a single cell with a calendar day.
  1516. * @property CALDAY_TEMPLATE
  1517. * @type String
  1518. * @protected
  1519. * @static
  1520. */
  1521. CALDAY_TEMPLATE: '<td class="{calendar_col_class} {calendar_day_class} {calendar_col_visibility_class}" id="{calendar_day_id}" ' +
  1522. 'role="gridcell" tabindex="-1">' +
  1523. '{day_content}' +
  1524. '</td>',
  1525.  
  1526. /**
  1527. * The identity of the widget.
  1528. *
  1529. * @property NAME
  1530. * @type String
  1531. * @default 'calendarBase'
  1532. * @readOnly
  1533. * @protected
  1534. * @static
  1535. */
  1536. NAME: 'calendarBase',
  1537.  
  1538. /**
  1539. * Static property used to define the default attribute configuration of
  1540. * the Widget.
  1541. *
  1542. * @property ATTRS
  1543. * @type {Object}
  1544. * @protected
  1545. * @static
  1546. */
  1547. ATTRS: {
  1548. tabIndex: {
  1549. value: 1
  1550. },
  1551. /**
  1552. * The date corresponding to the current calendar view. Always
  1553. * normalized to the first of the month that contains the date
  1554. * at assignment time. Used as the first date visible in the
  1555. * calendar.
  1556. *
  1557. * @attribute date
  1558. * @type Date
  1559. * @default The first of the month containing today's date, as
  1560. * set on the end user's system.
  1561. */
  1562. date: {
  1563. value: new Date(),
  1564. setter: function (val) {
  1565. var newDate = this._normalizeDate(val);
  1566. if (ydate.areEqual(newDate, this.get('date'))) {
  1567. return this.get('date');
  1568. } else {
  1569. return newDate;
  1570. }
  1571. }
  1572. },
  1573.  
  1574. /**
  1575. * A setting specifying whether to shows days from the previous
  1576. * month in the visible month's grid, if there are empty preceding
  1577. * cells available.
  1578. *
  1579. * @attribute showPrevMonth
  1580. * @type boolean
  1581. * @default false
  1582. */
  1583. showPrevMonth: {
  1584. value: false
  1585. },
  1586.  
  1587. /**
  1588. * A setting specifying whether to shows days from the next
  1589. * month in the visible month's grid, if there are empty
  1590. * cells available at the end.
  1591. *
  1592. * @attribute showNextMonth
  1593. * @type boolean
  1594. * @default false
  1595. */
  1596. showNextMonth: {
  1597. value: false
  1598. },
  1599.  
  1600. /**
  1601. * Strings and properties derived from the internationalization packages
  1602. * for the calendar.
  1603. *
  1604. * @attribute strings
  1605. * @type Object
  1606. * @protected
  1607. */
  1608. strings : {
  1609. valueFn: function() { return Y.Intl.get("calendar-base"); }
  1610. },
  1611.  
  1612. /**
  1613. * Custom header renderer for the calendar.
  1614. *
  1615. * @attribute headerRenderer
  1616. * @type String | Function
  1617. */
  1618. headerRenderer: {
  1619. value: "%B %Y"
  1620. },
  1621.  
  1622. /**
  1623. * The name of the rule which all enabled dates should match.
  1624. * Either disabledDatesRule or enabledDatesRule should be specified,
  1625. * or neither, but not both.
  1626. *
  1627. * @attribute enabledDatesRule
  1628. * @type String
  1629. * @default null
  1630. */
  1631. enabledDatesRule: {
  1632. value: null
  1633. },
  1634.  
  1635. /**
  1636. * The name of the rule which all disabled dates should match.
  1637. * Either disabledDatesRule or enabledDatesRule should be specified,
  1638. * or neither, but not both.
  1639. *
  1640. * @attribute disabledDatesRule
  1641. * @type String
  1642. * @default null
  1643. */
  1644. disabledDatesRule: {
  1645. value: null
  1646. },
  1647.  
  1648. /**
  1649. * A read-only attribute providing a list of currently selected dates.
  1650. *
  1651. * @attribute selectedDates
  1652. * @readOnly
  1653. * @type Array
  1654. */
  1655. selectedDates : {
  1656. readOnly: true,
  1657. getter: function () {
  1658. return (this._getSelectedDatesList());
  1659. }
  1660. },
  1661.  
  1662. /**
  1663. * An object of the form {rules:Object, filterFunction:Function},
  1664. * providing set of rules and a custom rendering function for
  1665. * customizing specific calendar cells.
  1666. *
  1667. * @attribute customRenderer
  1668. * @type Object
  1669. * @default {}
  1670. */
  1671. customRenderer : {
  1672. lazyAdd: false,
  1673. value: {},
  1674. setter: function (val) {
  1675. this._rules = val.rules;
  1676. this._filterFunction = val.filterFunction;
  1677. }
  1678. }
  1679. }
  1680.  
  1681. });
  1682.