[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |