[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/calendar/ -> calendar.js (source)

   1  /*
   2  YUI 3.17.2 (build 9c3c78e)
   3  Copyright 2014 Yahoo! Inc. All rights reserved.
   4  Licensed under the BSD License.
   5  http://yuilibrary.com/license/
   6  */
   7  
   8  YUI.add('calendar', function (Y, NAME) {
   9  
  10  /**
  11   * The Calendar component is a UI widget that allows users
  12   * to view dates in a two-dimensional month grid, as well as
  13   * to select one or more dates, or ranges of dates. Calendar
  14   * is generated dynamically and relies on the developer to
  15   * provide for a progressive enhancement alternative.
  16   *
  17   *
  18   * @module calendar
  19   */
  20  
  21  var getCN             = Y.ClassNameManager.getClassName,
  22      CALENDAR          = 'calendar',
  23      KEY_DOWN          = 40,
  24      KEY_UP            = 38,
  25      KEY_LEFT          = 37,
  26      KEY_RIGHT         = 39,
  27      KEY_ENTER         = 13,
  28      KEY_SPACE         = 32,
  29      CAL_DAY_SELECTED  = getCN(CALENDAR, 'day-selected'),
  30      CAL_DAY_HILITED   = getCN(CALENDAR, 'day-highlighted'),
  31      CAL_DAY           = getCN(CALENDAR, 'day'),
  32      CAL_PREVMONTH_DAY = getCN(CALENDAR, 'prevmonth-day'),
  33      CAL_NEXTMONTH_DAY = getCN(CALENDAR, 'nextmonth-day'),
  34      CAL_GRID          = getCN(CALENDAR, 'grid'),
  35      ydate             = Y.DataType.Date,
  36      CAL_PANE          = getCN(CALENDAR, 'pane'),
  37      os                = Y.UA.os;
  38  
  39  /** Create a calendar view to represent a single or multiple
  40      * month range of dates, rendered as a grid with date and
  41      * weekday labels.
  42      *
  43      * @class Calendar
  44      * @extends CalendarBase
  45      * @param config {Object} Configuration object (see Configuration attributes)
  46      * @constructor
  47      */
  48  function Calendar() {
  49      Calendar.superclass.constructor.apply ( this, arguments );
  50  }
  51  
  52  Y.Calendar = Y.extend(Calendar, Y.CalendarBase, {
  53  
  54      _keyEvents: [],
  55  
  56      _highlightedDateNode: null,
  57  
  58      /**
  59       * A property tracking the last selected date on the calendar, for the
  60       * purposes of multiple selection.
  61       *
  62       * @property _lastSelectedDate
  63       * @type Date
  64       * @default null
  65       * @private
  66       */
  67      _lastSelectedDate: null,
  68  
  69      /**
  70       * Designated initializer. Activates the navigation plugin for the calendar.
  71       *
  72       * @method initializer
  73       */
  74      initializer : function () {
  75          this.plug(Y.Plugin.CalendarNavigator);
  76  
  77          this._keyEvents = [];
  78          this._highlightedDateNode = null;
  79          this._lastSelectedDate = null;
  80      },
  81  
  82      /**
  83       * Overrides the _bindCalendarEvents placeholder in CalendarBase
  84       * and binds calendar events during bindUI stage.
  85       * @method _bindCalendarEvents
  86       * @protected
  87       */
  88      _bindCalendarEvents : function () {
  89          var contentBox = this.get('contentBox'),
  90              pane       = contentBox.one("." + CAL_PANE);
  91  
  92          pane.on("selectstart", this._preventSelectionStart);
  93          pane.delegate("click", this._clickCalendar, "." + CAL_DAY + ", ." + CAL_PREVMONTH_DAY + ", ." + CAL_NEXTMONTH_DAY, this);
  94          pane.delegate("keydown", this._keydownCalendar, "." + CAL_GRID, this);
  95          pane.delegate("focus", this._focusCalendarGrid, "." + CAL_GRID, this);
  96          pane.delegate("focus", this._focusCalendarCell, "." + CAL_DAY, this);
  97          pane.delegate("blur", this._blurCalendarGrid, "." + CAL_GRID + ",." + CAL_DAY, this);
  98  
  99  
 100          this.after(['minimumDateChange', 'maximumDateChange'], this._afterCustomRendererChange);
 101      },
 102  
 103      /**
 104       * Prevents text selection if it is started within the calendar pane
 105       * @method _preventSelectionStart
 106       * @param event {Event} The selectstart event
 107       * @protected
 108       */
 109      _preventSelectionStart : function (event) {
 110          event.preventDefault();
 111      },
 112  
 113      /**
 114       * Highlights a specific date node with keyboard highlight class
 115       * @method _highlightDateNode
 116       * @param oDate {Date} Date corresponding the node to be highlighted
 117       * @protected
 118       */
 119      _highlightDateNode : function (oDate) {
 120          this._unhighlightCurrentDateNode();
 121          var newNode = this._dateToNode(oDate);
 122          newNode.focus();
 123          newNode.addClass(CAL_DAY_HILITED);
 124      },
 125  
 126      /**
 127       * Unhighlights a specific date node currently highlighted with keyboard highlight class
 128       * @method _unhighlightCurrentDateNode
 129       * @protected
 130       */
 131      _unhighlightCurrentDateNode : function () {
 132          var allHilitedNodes = this.get("contentBox").all("." + CAL_DAY_HILITED);
 133          if (allHilitedNodes) {
 134              allHilitedNodes.removeClass(CAL_DAY_HILITED);
 135          }
 136      },
 137  
 138      /**
 139       * Returns the grid number for a specific calendar grid (for multi-grid templates)
 140       * @method _getGridNumber
 141       * @param gridNode {Node} Node corresponding to a specific grid
 142       * @protected
 143       */
 144      _getGridNumber : function (gridNode) {
 145          var idParts = gridNode.get("id").split("_").reverse();
 146  
 147          return parseInt(idParts[0], 10);
 148      },
 149  
 150      /**
 151       * Handler for loss of focus of calendar grid
 152       * @method _blurCalendarGrid
 153       * @protected
 154       */
 155      _blurCalendarGrid : function () {
 156          this._unhighlightCurrentDateNode();
 157      },
 158  
 159  
 160      /**
 161       * Handler for gain of focus of calendar cell
 162       * @method _focusCalendarCell
 163       * @protected
 164       */
 165      _focusCalendarCell : function (ev) {
 166          this._highlightedDateNode = ev.target;
 167          ev.stopPropagation();
 168      },
 169  
 170      /**
 171       * Handler for gain of focus of calendar grid
 172       * @method _focusCalendarGrid
 173       * @protected
 174       */
 175      _focusCalendarGrid : function () {
 176          this._unhighlightCurrentDateNode();
 177          this._highlightedDateNode = null;
 178      },
 179  
 180      /**
 181       * Handler for keyboard press on a calendar grid
 182       * @method _keydownCalendar
 183       * @protected
 184       */
 185      _keydownCalendar : function (ev) {
 186          var gridNum = this._getGridNumber(ev.target),
 187              curDate = !this._highlightedDateNode ? null : this._nodeToDate(this._highlightedDateNode),
 188              keyCode = ev.keyCode,
 189              dayNum = 0,
 190              dir = '',
 191              selMode,
 192              newDate,
 193              startDate,
 194              endDate,
 195              lastPaneDate;
 196  
 197          switch(keyCode) {
 198              case KEY_DOWN:
 199                  dayNum = 7;
 200                  dir = 's';
 201                  break;
 202              case KEY_UP:
 203                  dayNum = -7;
 204                  dir = 'n';
 205                  break;
 206              case KEY_LEFT:
 207                  dayNum = -1;
 208                  dir = 'w';
 209                  break;
 210              case KEY_RIGHT:
 211                  dayNum = 1;
 212                  dir = 'e';
 213                  break;
 214              case KEY_SPACE: case KEY_ENTER:
 215                  ev.preventDefault();
 216                  if (this._highlightedDateNode) {
 217                      selMode = this.get("selectionMode");
 218                      if (selMode === "single" && !this._highlightedDateNode.hasClass(CAL_DAY_SELECTED)) {
 219                              this._clearSelection(true);
 220                              this._addDateToSelection(curDate);
 221                      } else if (selMode === "multiple" || selMode === "multiple-sticky") {
 222                          if (this._highlightedDateNode.hasClass(CAL_DAY_SELECTED)) {
 223                              this._removeDateFromSelection(curDate);
 224                          } else {
 225                              this._addDateToSelection(curDate);
 226                          }
 227                      }
 228                  }
 229                  break;
 230          }
 231  
 232  
 233          if (keyCode === KEY_DOWN || keyCode === KEY_UP || keyCode === KEY_LEFT || keyCode === KEY_RIGHT) {
 234  
 235              if (!curDate) {
 236                  curDate = ydate.addMonths(this.get("date"), gridNum);
 237                  dayNum = 0;
 238              }
 239  
 240              ev.preventDefault();
 241  
 242              newDate = ydate.addDays(curDate, dayNum);
 243              startDate = this.get("date");
 244              endDate = ydate.addMonths(this.get("date"), this._paneNumber - 1);
 245              lastPaneDate = new Date(endDate);
 246              endDate.setDate(ydate.daysInMonth(endDate));
 247  
 248              if (ydate.isInRange(newDate, startDate, endDate)) {
 249  /*
 250                  var paneShift = (newDate.getMonth() - curDate.getMonth()) % 10;
 251  
 252                  if (paneShift != 0) {
 253                      var newGridNum = gridNum + paneShift,
 254                              contentBox = this.get('contentBox'),
 255                              newPane = contentBox.one("#" + this._calendarId + "_pane_" + newGridNum);
 256                              newPane.focus();
 257                  }
 258  */
 259                  this._highlightDateNode(newDate);
 260              } else if (ydate.isGreater(startDate, newDate)) {
 261                  if (!ydate.isGreaterOrEqual(this.get("minimumDate"), startDate)) {
 262                      this.set("date", ydate.addMonths(startDate, -1));
 263                      this._highlightDateNode(newDate);
 264                  }
 265              } else if (ydate.isGreater(newDate, endDate)) {
 266                  if (!ydate.isGreaterOrEqual(lastPaneDate, this.get("maximumDate"))) {
 267                      this.set("date", ydate.addMonths(startDate, 1));
 268                      this._highlightDateNode(newDate);
 269                  }
 270              }
 271          }
 272      },
 273  
 274      /**
 275       * Handles the calendar clicks based on selection mode.
 276       * @method _clickCalendar
 277       * @param {Event} ev A click event
 278       * @private
 279       */
 280      _clickCalendar : function (ev) {
 281          var clickedCell = ev.currentTarget,
 282              clickedCellIsDay = clickedCell.hasClass(CAL_DAY) &&
 283                                  !clickedCell.hasClass(CAL_PREVMONTH_DAY) &&
 284                                  !clickedCell.hasClass(CAL_NEXTMONTH_DAY),
 285  
 286          clickedCellIsSelected = clickedCell.hasClass(CAL_DAY_SELECTED),
 287          selectedDate;
 288  
 289          switch (this.get("selectionMode")) {
 290              case("single"):
 291                  if (clickedCellIsDay) {
 292                      if (!clickedCellIsSelected) {
 293                          this._clearSelection(true);
 294                          this._addDateToSelection(this._nodeToDate(clickedCell));
 295                      }
 296                  }
 297                  break;
 298              case("multiple-sticky"):
 299                  if (clickedCellIsDay) {
 300                      if (clickedCellIsSelected) {
 301                          this._removeDateFromSelection(this._nodeToDate(clickedCell));
 302                      } else {
 303                          this._addDateToSelection(this._nodeToDate(clickedCell));
 304                      }
 305                  }
 306                  break;
 307              case("multiple"):
 308                  if (clickedCellIsDay) {
 309                      if (!ev.metaKey && !ev.ctrlKey && !ev.shiftKey) {
 310                          this._clearSelection(true);
 311                          this._lastSelectedDate = this._nodeToDate(clickedCell);
 312                          this._addDateToSelection(this._lastSelectedDate);
 313                      } else if (((os === 'macintosh' && ev.metaKey) || (os !== 'macintosh' && ev.ctrlKey)) && !ev.shiftKey) {
 314                          if (clickedCellIsSelected) {
 315                              this._removeDateFromSelection(this._nodeToDate(clickedCell));
 316                              this._lastSelectedDate = null;
 317                          } else {
 318                              this._lastSelectedDate = this._nodeToDate(clickedCell);
 319                              this._addDateToSelection(this._lastSelectedDate);
 320                          }
 321                      } else if (((os === 'macintosh' && ev.metaKey) || (os !== 'macintosh' && ev.ctrlKey)) && ev.shiftKey) {
 322                          if (this._lastSelectedDate) {
 323                              selectedDate = this._nodeToDate(clickedCell);
 324                              this._addDateRangeToSelection(selectedDate, this._lastSelectedDate);
 325                              this._lastSelectedDate = selectedDate;
 326                          } else {
 327                              this._lastSelectedDate = this._nodeToDate(clickedCell);
 328                              this._addDateToSelection(this._lastSelectedDate);
 329                          }
 330                      } else if (ev.shiftKey) {
 331                          if (this._lastSelectedDate) {
 332                              selectedDate = this._nodeToDate(clickedCell);
 333                              this._clearSelection(true);
 334                              this._addDateRangeToSelection(selectedDate, this._lastSelectedDate);
 335                              this._lastSelectedDate = selectedDate;
 336                          } else {
 337                              this._clearSelection(true);
 338                              this._lastSelectedDate = this._nodeToDate(clickedCell);
 339                              this._addDateToSelection(this._lastSelectedDate);
 340                          }
 341                      }
 342                  }
 343                  break;
 344          }
 345  
 346          if (clickedCellIsDay) {
 347              /**
 348              * Fired when a specific date cell in the calendar is clicked. The event carries a
 349              * payload which includes a `cell` property corresponding to the node of the actual
 350              * date cell, and a `date` property, with the `Date` that was clicked.
 351              *
 352              * @event dateClick
 353              */
 354              this.fire("dateClick", {cell: clickedCell, date: this._nodeToDate(clickedCell)});
 355          } else if (clickedCell.hasClass(CAL_PREVMONTH_DAY)) {
 356              /**
 357              * Fired when any of the previous month's days displayed before the calendar grid
 358              * are clicked.
 359              *
 360              * @event prevMonthClick
 361              */
 362              this.fire("prevMonthClick");
 363          } else if (clickedCell.hasClass(CAL_NEXTMONTH_DAY)) {
 364              /**
 365              * Fired when any of the next month's days displayed after the calendar grid
 366              * are clicked.
 367              *
 368              * @event nextMonthClick
 369              */
 370              this.fire("nextMonthClick");
 371          }
 372      },
 373  
 374      /**
 375       * Overrides CalendarBase.prototype._canBeSelected to disable
 376       * nodes earlier than minimumDate and later than maximumDate
 377       * @method _canBeSelected
 378       * @private
 379       */
 380      _canBeSelected : function (date) {
 381          var minDate = this.get('minimumDate'),
 382              maxDate = this.get('maximumDate');
 383  
 384          if ((minDate && !ydate.isGreaterOrEqual(date, minDate)) ||
 385              (maxDate &&  ydate.isGreater(date, maxDate))) {
 386              return false;
 387          }
 388  
 389          return Calendar.superclass._canBeSelected.call(this, date);
 390      },
 391  
 392      /**
 393       * Overrides CalendarBase.prototype._renderCustomRules to disable
 394       * nodes earlier than minimumDate and later than maximumDate
 395       * @method _renderCustomRules
 396       * @private
 397       */
 398      _renderCustomRules: function () {
 399          Calendar.superclass._renderCustomRules.call(this);
 400  
 401          var minDate = this.get('minimumDate'),
 402              maxDate = this.get('maximumDate'),
 403              dates = [],
 404              i, l,
 405              paneDate,
 406              paneNum;
 407  
 408          if (!minDate && !maxDate) {
 409              return;
 410          }
 411  
 412          for (paneNum = 0; paneNum < this._paneNumber; paneNum++) {
 413              paneDate = ydate.addMonths(this.get("date"), paneNum);
 414              dates = dates.concat(ydate.listOfDatesInMonth(paneDate));
 415          }
 416  
 417          if (minDate) {
 418              for (i = 0, l = dates.length; i < l; i++) {
 419                  if (!ydate.isGreaterOrEqual(dates[i], minDate)) {
 420                      this._disableDate(dates[i]);
 421                  } else {
 422                      break;
 423                  }
 424              }
 425          }
 426  
 427          if (maxDate) {
 428              for (i = dates.length - 1; i >= 0; i--) {
 429                  if (ydate.isGreater(dates[i], maxDate)) {
 430                      this._disableDate(dates[i]);
 431                  } else {
 432                      break;
 433                  }
 434              }
 435          }
 436      },
 437  
 438      /**
 439       * Subtracts one month from the current calendar view.
 440       * @method subtractMonth
 441       * @return {Calendar} A reference to this object
 442       * @chainable
 443       */
 444      subtractMonth : function (e) {
 445          this.set("date", ydate.addMonths(this.get("date"), -1));
 446          if (e) {
 447              e.halt();
 448          }
 449          return this;
 450      },
 451  
 452      /**
 453       * Subtracts one year from the current calendar view.
 454       * @method subtractYear
 455       * @return {Calendar} A reference to this object
 456       * @chainable
 457       */
 458      subtractYear : function (e) {
 459          this.set("date", ydate.addYears(this.get("date"), -1));
 460          if (e) {
 461              e.halt();
 462          }
 463          return this;
 464      },
 465  
 466      /**
 467       * Adds one month to the current calendar view.
 468       * @method addMonth
 469       * @return {Calendar} A reference to this object
 470       * @chainable
 471       */
 472      addMonth : function (e) {
 473          this.set("date", ydate.addMonths(this.get("date"), 1));
 474          if (e) {
 475              e.halt();
 476          }
 477          return this;
 478      },
 479  
 480      /**
 481       * Adds one year to the current calendar view.
 482       * @method addYear
 483       * @return {Calendar} A reference to this object
 484       * @chainable
 485       */
 486      addYear : function (e) {
 487          this.set("date", ydate.addYears(this.get("date"), 1));
 488          if (e) {
 489              e.halt();
 490          }
 491          return this;
 492      }
 493  }, {
 494      /**
 495      * The identity of the widget.
 496      *
 497      * @property NAME
 498      * @type String
 499      * @default 'calendar'
 500      * @readOnly
 501      * @protected
 502      * @static
 503      */
 504      NAME: "calendar",
 505  
 506      /**
 507      * Static property used to define the default attribute configuration of
 508      * the Widget.
 509      *
 510      * @property ATTRS
 511      * @type {Object}
 512      * @protected
 513      * @static
 514      */
 515      ATTRS: {
 516  
 517          /**
 518           * A setting specifying the type of selection the calendar allows.
 519           * Possible values include:
 520           * <ul>
 521           *   <li>`single` - One date at a time</li>
 522           *   <li>`multiple-sticky` - Multiple dates, selected one at a time (the dates "stick"). This option
 523           *   is appropriate for mobile devices, where function keys from the keyboard are not available.</li>
 524           *   <li>`multiple` - Multiple dates, selected with Ctrl/Meta keys for additional single
 525           *   dates, and Shift key for date ranges.</li>
 526           *
 527           * @attribute selectionMode
 528           * @type String
 529           * @default single
 530           */
 531          selectionMode: {
 532              value: "single"
 533          },
 534  
 535          /**
 536           * The date corresponding to the current calendar view. Always
 537           * normalized to the first of the month that contains the date
 538           * at assignment time. Used as the first date visible in the
 539           * calendar.
 540           *
 541           * @attribute date
 542           * @type Date
 543           * @default Today's date as set on the user's computer.
 544           */
 545          date: {
 546              value: new Date(),
 547              lazyAdd: false,
 548              setter: function (val) {
 549  
 550                  var newDate    = this._normalizeDate(val),
 551                      newEndDate = ydate.addMonths(newDate, this._paneNumber - 1),
 552                      minDate    = this.get("minimumDate"),
 553                      maxDate    = this.get("maximumDate");
 554  
 555                  if ((!minDate || ydate.isGreaterOrEqual(newDate, minDate)) &&
 556                      (!maxDate || ydate.isGreaterOrEqual(maxDate, newEndDate))
 557                  ) {
 558                      return newDate;
 559                  } else if (minDate && ydate.isGreater(minDate, newDate)) {
 560                      return this._normalizeDate(minDate);
 561                  } else if (maxDate && ydate.isGreater(newEndDate, maxDate)) {
 562                      return ydate.addMonths(this._normalizeDate(maxDate), 1 - this._paneNumber);
 563                  }
 564              }
 565          },
 566  
 567          /**
 568           * Unless minimumDate is null, it will not be possible to display and select dates earlier than this one.
 569           *
 570           * @attribute minimumDate
 571           * @type Date
 572           * @default null
 573           */
 574          minimumDate: {
 575              value: null,
 576              setter: function (val) {
 577                  if (Y.Lang.isDate(val)) {
 578                      var curDate = this.get('date'),
 579                          newMin  = this._normalizeTime(val);
 580                      if (curDate && !ydate.isGreaterOrEqual(curDate, newMin)) {
 581                          this.set('date', val);
 582                      }
 583                      return newMin;
 584                  } else {
 585                      return null;
 586                  }
 587              }
 588          },
 589  
 590          /**
 591           * Unless maximumDate is null, it will not be possible to display and select dates later than this one.
 592           *
 593           * @attribute maximumDate
 594           * @type Date
 595           * @default null
 596           */
 597          maximumDate: {
 598              value: null,
 599              setter: function (val) {
 600                  if (Y.Lang.isDate(val)) {
 601                      var curDate = this.get('date');
 602  
 603                      if (curDate && !ydate.isGreaterOrEqual(val, ydate.addMonths(curDate, this._paneNumber - 1))) {
 604                          this.set('date', ydate.addMonths(this._normalizeDate(val), 1 - this._paneNumber));
 605                      }
 606  
 607                      return this._normalizeTime(val);
 608                  } else {
 609                      return null;
 610                  }
 611              }
 612          }
 613      }
 614  });
 615  
 616  
 617  }, '3.17.2', {"requires": ["calendar-base", "calendarnavigator"], "skinnable": true});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1