1;(function (window, $, undefined) { ;(function () { 2 var VERSION = '2.2.3', 3 pluginName = 'datepicker', 4 autoInitSelector = '.datepicker-here', 5 $body, $datepickersContainer, 6 containerBuilt = false, 7 baseTemplate = '' + 8 '<div class="datepicker">' + 9 '<i class="datepicker--pointer"></i>' + 10 '<nav class="datepicker--nav"></nav>' + 11 '<div class="datepicker--content"></div>' + 12 '</div>', 13 defaults = { 14 classes: '', 15 inline: false, 16 language: 'ru', 17 startDate: new Date(), 18 firstDay: '', 19 weekends: [6, 0], 20 dateFormat: '', 21 altField: '', 22 altFieldDateFormat: '@', 23 toggleSelected: true, 24 keyboardNav: true, 25 26 position: 'bottom left', 27 offset: 12, 28 29 view: 'days', 30 minView: 'days', 31 32 showOtherMonths: true, 33 selectOtherMonths: true, 34 moveToOtherMonthsOnSelect: true, 35 36 showOtherYears: true, 37 selectOtherYears: true, 38 moveToOtherYearsOnSelect: true, 39 40 minDate: '', 41 maxDate: '', 42 disableNavWhenOutOfRange: true, 43 44 multipleDates: false, // Boolean or Number 45 multipleDatesSeparator: ',', 46 range: false, 47 48 todayButton: false,
49 clearButton: false, 50 51 showEvent: 'focus', 52 autoClose: false, 53 54 // navigation 55 monthsField: 'monthsShort', 56 prevHtml: '<svg><path d="M 17,12 l -5,5 l 5,5"></path></svg>', 57 nextHtml: '<svg><path d="M 14,12 l 5,5 l -5,5"></path></svg>', 58 navTitles: { 59 days: 'MM, <i>yyyy</i>', 60 months: 'yyyy', 61 years: 'yyyy1 - yyyy2' 62 }, 63 64 // timepicker 65 timepicker: false, 66 onlyTimepicker: false, 67 dateTimeSeparator: ' ', 68 timeFormat: '', 69 minHours: 0, 70 maxHours: 24, 71 minMinutes: 0, 72 maxMinutes: 59, 73 hoursStep: 1, 74 minutesStep: 1, 75 76 // events 77 onSelect: '', 78 onShow: '', 79 onHide: '', 80 onChangeMonth: '', 81 onChangeYear: '', 82 onChangeDecade: '', 83 onChangeView: '', 84 onRenderCell: '' 85 }, 86 hotKeys = { 87 'ctrlRight': [17, 39], 88 'ctrlUp': [17, 38], 89 'ctrlLeft': [17, 37], 90 'ctrlDown': [17, 40], 91 'shiftRight': [16, 39], 92 'shiftUp': [16, 38], 93 'shiftLeft': [16, 37], 94 'shiftDown': [16, 40], 95 'altUp': [18, 38], 96 'altRight': [18, 39], 97 'altLeft': [18, 37], 98 'altDown': [18, 40], 99 'ctrlShiftUp': [16, 17, 38] 100 }, 101 datepicker; 102 103 var Datepicker = function (el, options) { 104 this.el = el; 105 this.$el = $(el); 106 107 this.opts = $.extend(true, {}, defaults, options, this.$el.data()); 108 109 if ($body == undefined) { 110 $body = $('body'); 111 } 112 113 if (!this.opts.startDate) { 114 this.opts.startDate = new Date(); 115 } 116 117 if (this.el.nodeName == 'INPUT') { 118 this.elIsInput = true; 119 } 120 121 if (this.opts.altField) { 122 this.$altField = typeof this.opts.altField == 'string' ? $(this.opts.altField) : this.opts.altField; 123 } 124 125 this.inited = false; 126 this.visible = false; 127 this.silent = false; // Need to prevent unnecessary rendering 128 129 this.currentDate = this.opts.startDate; 130 this.currentView = this.opts.view; 131 this._createShortCuts(); 132 this.selectedDates = []; 133 this.views = {}; 134 this.keys = []; 135 this.minRange = ''; 136 this.maxRange = ''; 137 this._prevOnSelectValue = ''; 138 139 this.init() 140 }; 141 142 datepicker = Datepicker; 143 144 datepicker.prototype = { 145 VERSION: VERSION, 146 viewIndexes: ['days', 'months', 'years'], 147 148 init: function () { 149 if (!containerBuilt && !this.opts.inline && this.elIsInput) { 150 this._buildDatepickersContainer(); 151 } 152 this._buildBaseHtml(); 153 this._defineLocale(this.opts.language); 154 this._syncWithMinMaxDates(); 155 156 if (this.elIsInput) { 157 if (!this.opts.inline) { 158 // Set extra classes for proper transitions 159 this._setPositionClasses(this.opts.position); 160 this._bindEvents() 161 } 162 if (this.opts.keyboardNav && !this.opts.onlyTimepicker) { 163 this._bindKeyboardEvents(); 164 } 165 this.$datepicker.on('mousedown', this._onMouseDownDatepicker.bind(this)); 166 this.$datepicker.on('mouseup', this._onMouseUpDatepicker.bind(this)); 167 } 168 169 if (this.opts.classes) { 170 this.$datepicker.addClass(this.opts.classes) 171 } 172 173 if (this.opts.timepicker) { 174 this.timepicker = new $.fn.datepicker.Timepicker(this, this.opts); 175 this._bindTimepickerEvents(); 176 } 177 178 if (this.opts.onlyTimepicker) { 179 this.$datepicker.addClass('-only-timepicker-'); 180 } 181 182 this.views[this.currentView] = new $.fn.datepicker.Body(this, this.currentView, this.opts); 183 this.views[this.currentView].show(); 184 this.nav = new $.fn.datepicker.Navigation(this, this.opts); 185 this.view = this.currentView; 186 187 this.$el.on('clickCell.adp', this._onClickCell.bind(this)); 188 this.$datepicker.on('mouseenter', '.datepicker--cell', this._onMouseEnterCell.bind(this)); 189 this.$datepicker.on('mouseleave', '.datepicker--cell', this._onMouseLeaveCell.bind(this)); 190 191 this.inited = true; 192 }, 193 194 _createShortCuts: function () { 195 this.minDate = this.opts.minDate ? this.opts.minDate : new Date(-8639999913600000); 196 this.maxDate = this.opts.maxDate ? this.opts.maxDate : new Date(8639999913600000); 197 }, 198 199 _bindEvents : function () { 200 this.$el.on(this.opts.showEvent + '.adp', this._onShowEvent.bind(this));
201 this.$el.on('mouseup.adp', this._onMouseUpEl.bind(this)); 202 this.$el.on('blur.adp', this._onBlur.bind(this)); 203 this.$el.on('keyup.adp', this._onKeyUpGeneral.bind(this)); 204 $(window).on('resize.adp', this._onResize.bind(this)); 205 $('body').on('mouseup.adp', this._onMouseUpBody.bind(this)); 206 }, 207 208 _bindKeyboardEvents: function () { 209 this.$el.on('keydown.adp', this._onKeyDown.bind(this)); 210 this.$el.on('keyup.adp', this._onKeyUp.bind(this)); 211 this.$el.on('hotKey.adp', this._onHotKey.bind(this)); 212 }, 213 214 _bindTimepickerEvents: function () { 215 this.$el.on('timeChange.adp', this._onTimeChange.bind(this)); 216 }, 217 218 isWeekend: function (day) { 219 return this.opts.weekends.indexOf(day) !== -1; 220 }, 221 222 _defineLocale: function (lang) { 223 if (typeof lang == 'string') { 224 this.loc = $.fn.datepicker.language[lang]; 225 if (!this.loc) { 226 console.warn('Can\'t find language "' + lang + '" in Datepicker.language, will use "ru" instead'); 227 this.loc = $.extend(true, {}, $.fn.datepicker.language.ru) 228 } 229 230 this.loc = $.extend(true, {}, $.fn.datepicker.language.ru, $.fn.datepicker.language[lang]) 231 } else { 232 this.loc = $.extend(true, {}, $.fn.datepicker.language.ru, lang) 233 } 234 235 if (this.opts.dateFormat) { 236 this.loc.dateFormat = this.opts.dateFormat 237 } 238 239 if (this.opts.timeFormat) { 240 this.loc.timeFormat = this.opts.timeFormat 241 } 242 243 if (this.opts.firstDay !== '') { 244 this.loc.firstDay = this.opts.firstDay 245 } 246 247 if (this.opts.timepicker) { 248 this.loc.dateFormat = [this.loc.dateFormat, this.loc.timeFormat].join(this.opts.dateTimeSeparator); 249 } 250 251 if (this.opts.onlyTimepicker) { 252 this.loc.dateFormat = this.loc.timeFormat; 253 } 254 255 var boundary = this._getWordBoundaryRegExp; 256 if (this.loc.timeFormat.match(boundary('aa')) || 257 this.loc.timeFormat.match(boundary('AA')) 258 ) { 259 this.ampm = true; 260 } 261 }, 262 263 _buildDatepickersContainer: function () { 264 containerBuilt = true; 265 $body.append('<div class="datepickers-container" id="datepickers-container"></div>'); 266 $datepickersContainer = $('#datepickers-container'); 267 }, 268 269 _buildBaseHtml: function () { 270 var $appendTarget, 271 $inline = $('<div class="datepicker-inline">'); 272 273 if(this.el.nodeName == 'INPUT') { 274 if (!this.opts.inline) { 275 $appendTarget = $datepickersContainer; 276 } else { 277 $appendTarget = $inline.insertAfter(this.$el) 278 } 279 } else { 280 $appendTarget = $inline.appendTo(this.$el) 281 } 282 283 this.$datepicker = $(baseTemplate).appendTo($appendTarget); 284 this.$content = $('.datepicker--content', this.$datepicker); 285 this.$nav = $('.datepicker--nav', this.$datepicker); 286 }, 287 288 _triggerOnChange: function () { 289 if (!this.selectedDates.length) { 290 // Prevent from triggering multiple onSelect callback with same argument (empty string) in IE10-11 291 if (this._prevOnSelectValue === '') return; 292 this._prevOnSelectValue = ''; 293 return this.opts.onSelect('', '', this); 294 } 295 296 var selectedDates = this.selectedDates, 297 parsedSelected = datepicker.getParsedDate(selectedDates[0]), 298 formattedDates, 299 _this = this, 300 dates = new Date( 301 parsedSelected.year, 302 parsedSelected.month, 303 parsedSelected.date, 304 parsedSelected.hours, 305 parsedSelected.minutes 306 ); 307 308 formattedDates = selectedDates.map(function (date) { 309 return _this.formatDate(_this.loc.dateFormat, date) 310 }).join(this.opts.multipleDatesSeparator); 311 312 // Create new dates array, to separate it from original selectedDates 313 if (this.opts.multipleDates || this.opts.range) { 314 dates = selectedDates.map(function(date) { 315 var parsedDate = datepicker.getParsedDate(date); 316 return new Date( 317 parsedDate.year, 318 parsedDate.month, 319 parsedDate.date, 320 parsedDate.hours, 321 parsedDate.minutes 322 ); 323 }) 324 } 325 326 this._prevOnSelectValue = formattedDates; 327 this.opts.onSelect(formattedDates, dates, this); 328 }, 329 330 next: function () { 331 var d = this.parsedDate, 332 o = this.opts; 333 switch (this.view) { 334 case 'days': 335 this.date = new Date(d.year, d.month + 1, 1); 336 if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year); 337 break; 338 case 'months': 339 this.date = new Date(d.year + 1, d.month, 1); 340 if (o.onChangeYear) o.onChangeYear(this.parsedDate.year); 341 break; 342 case 'years': 343 this.date = new Date(d.year + 10, 0, 1); 344 if (o.onChangeDecade) o.onChangeDecade(this.curDecade); 345 break; 346 } 347 }, 348 349 prev: function () { 350 var d = this.parsedDate, 351 o = this.opts; 352 switch (this.view) { 353 case 'days': 354 this.date = new Date(d.year, d.month - 1, 1); 355 if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year); 356 break; 357 case 'months': 358 this.date = new Date(d.year - 1, d.month, 1); 359 if (o.onChangeYear) o.onChangeYear(this.parsedDate.year); 360 break; 361 case 'years': 362 this.date = new Date(d.year - 10, 0, 1); 363 if (o.onChangeDecade) o.onChangeDecade(this.curDecade); 364 break; 365 } 366 }, 367 368 formatDate: function (string, date) { 369 date = date || this.date; 370 var result = string, 371 boundary = this._getWordBoundaryRegExp, 372 locale = this.loc, 373 leadingZero = datepicker.getLeadingZeroNum, 374 decade = datepicker.getDecade(date), 375 d = datepicker.getParsedDate(date),
376 fullHours = d.fullHours, 377 hours = d.hours, 378 ampm = string.match(boundary('aa')) || string.match(boundary('AA')), 379 dayPeriod = 'am', 380 replacer = this._replacer, 381 validHours; 382 383 if (this.opts.timepicker && this.timepicker && ampm) { 384 validHours = this.timepicker._getValidHoursFromDate(date, ampm); 385 fullHours = leadingZero(validHours.hours); 386 hours = validHours.hours; 387 dayPeriod = validHours.dayPeriod; 388 } 389 390 switch (true) { 391 case /@/.test(result): 392 result = result.replace(/@/, date.getTime()); 393 case /aa/.test(result): 394 result = replacer(result, boundary('aa'), dayPeriod); 395 case /AA/.test(result): 396 result = replacer(result, boundary('AA'), dayPeriod.toUpperCase()); 397 case /dd/.test(result): 398 result = replacer(result, boundary('dd'), d.fullDate); 399 case /d/.test(result): 400 result = replacer(result, boundary('d'), d.date); 401 case /DD/.test(result): 402 result = replacer(result, boundary('DD'), locale.days[d.day]); 403 case /D/.test(result): 404 result = replacer(result, boundary('D'), locale.daysShort[d.day]); 405 case /mm/.test(result): 406 result = replacer(result, boundary('mm'), d.fullMonth); 407 case /m/.test(result): 408 result = replacer(result, boundary('m'), d.month + 1); 409 case /MM/.test(result): 410 result = replacer(result, boundary('MM'), this.loc.months[d.month]); 411 case /M/.test(result): 412 result = replacer(result, boundary('M'), locale.monthsShort[d.month]); 413 case /ii/.test(result): 414 result = replacer(result, boundary('ii'), d.fullMinutes); 415 case /i/.test(result): 416 result = replacer(result, boundary('i'), d.minutes); 417 case /hh/.test(result): 418 result = replacer(result, boundary('hh'), fullHours); 419 case /h/.test(result): 420 result = replacer(result, boundary('h'), hours); 421 case /yyyy/.test(result): 422 result = replacer(result, boundary('yyyy'), d.year); 423 case /rrr/.test(result): 424 result = replacer(result, boundary('rrr'), d.year - 1911); //oka 425 case /yyyy1/.test(result): 426 result = replacer(result, boundary('yyyy1'), decade[0]); 427 case /rrr1/.test(result): 428 result = replacer(result, boundary('rrr1'), decade[0] - 1911); //oka 429 case /yyyy2/.test(result): 430 result = replacer(result, boundary('yyyy2'), decade[1]); 431 case /rrr2/.test(result): 432 result = replacer(result, boundary('rrr2'), decade[1] - 1911); //oka 433 case /yy/.test(result): 434 result = replacer(result, boundary('yy'), d.year.toString().slice(-2)); 435 } 436 437 return result; 438 }, 439 440 _replacer: function (str, reg, data) { 441 return str.replace(reg, function (match, p1,p2,p3) { 442 return p1 + data + p3; 443 }) 444 }, 445 446 _getWordBoundaryRegExp: function (sign) { 447 var symbols = '\\s|\\.|-|/|\\\\|,|\\$|\\!|\\?|:|;'; 448 449 return new RegExp('(^|>|' + symbols + ')(' + sign + ')($|<|' + symbols + ')', 'g'); 450 }, 451 452 453 selectDate: function (date) { 454 var _this = this, 455 opts = _this.opts, 456 d = _this.parsedDate, 457 selectedDates = _this.selectedDates, 458 len = selectedDates.length, 459 newDate = ''; 460 461 if (Array.isArray(date)) { 462 date.forEach(function (d) { 463 _this.selectDate(d) 464 }); 465 return; 466 } 467 468 if (!(date instanceof Date)) return; 469 470 this.lastSelectedDate = date; 471 472 // Set new time values from Date 473 if (this.timepicker) { 474 this.timepicker._setTime(date); 475 } 476
477 // On this step timepicker will set valid values in it's instance 478 _this._trigger('selectDate', date); 479 480 // Set correct time values after timepicker's validation 481 // Prevent from setting hours or minutes which values are lesser then `min` value or 482 // greater then `max` value 483 if (this.timepicker) { 484 date.setHours(this.timepicker.hours); 485 date.setMinutes(this.timepicker.minutes) 486 } 487 488 if (_this.view == 'days') { 489 if (date.getMonth() != d.month && opts.moveToOtherMonthsOnSelect) { 490 newDate = new Date(date.getFullYear(), date.getMonth(), 1); 491 } 492 } 493 494 if (_this.view == 'years') { 495 if (date.getFullYear() != d.year && opts.moveToOtherYearsOnSelect) { 496 newDate = new Date(date.getFullYear(), 0, 1); 497 } 498 } 499 500 if (newDate) { 501 _this.silent = true; 502 _this.date = newDate; 503 _this.silent = false; 504 _this.nav._render() 505 } 506 507 if (opts.multipleDates && !opts.range) { // Set priority to range functionality 508 if (len === opts.multipleDates) return; 509 if (!_this._isSelected(date)) { 510 _this.selectedDates.push(date); 511 } 512 } else if (opts.range) { 513 if (len == 2) { 514 _this.selectedDates = [date]; 515 _this.minRange = date; 516 _this.maxRange = ''; 517 } else if (len == 1) { 518 _this.selectedDates.push(date); 519 if (!_this.maxRange){ 520 _this.maxRange = date; 521 } else { 522 _this.minRange = date; 523 } 524 // Swap dates if they were selected via dp.selectDate() and second date was smaller then first 525 if (datepicker.bigger(_this.maxRange, _this.minRange)) { 526 _this.maxRange = _this.minRange; 527 _this.minRange = date; 528 } 529 _this.selectedDates = [_this.minRange, _this.maxRange] 530 531 } else { 532 _this.selectedDates = [date]; 533 _this.minRange = date; 534 } 535 } else { 536 _this.selectedDates = [date]; 537 } 538 539 _this._setInputValue(); 540 541 if (opts.onSelect) { 542 _this._triggerOnChange(); 543 } 544 545 if (opts.autoClose && !this.timepickerIsActive) { 546 if (!opts.multipleDates && !opts.range) { 547 _this.hide(); 548 } else if (opts.range && _this.selectedDates.length == 2) { 549 _this.hide(); 550 } 551 } 552 553 _this.views[this.currentView]._render() 554 }, 555 556 removeDate: function (date) { 557 var selected = this.selectedDates, 558 _this = this; 559 560 if (!(date instanceof Date)) return; 561 562 return selected.some(function (curDate, i) { 563 if (datepicker.isSame(curDate, date)) { 564 selected.splice(i, 1); 565 566 if (!_this.selectedDates.length) { 567 _this.minRange = ''; 568 _this.maxRange = ''; 569 _this.lastSelectedDate = ''; 570 } else { 571 _this.lastSelectedDate = _this.selectedDates[_this.selectedDates.length - 1]; 572 } 573 574 _this.views[_this.currentView]._render(); 575 _this._setInputValue(); 576 577 if (_this.opts.onSelect) { 578 _this._triggerOnChange(); 579 } 580 581 return true 582 } 583 }) 584 }, 585 586 today: function () { 587 this.silent = true; 588 this.view = this.opts.minView; 589 this.silent = false;
590 this.date = new Date(); 591 592 if (this.opts.todayButton instanceof Date) { 593 this.selectDate(this.opts.todayButton) 594 } 595 }, 596 597 clear: function () { 598 this.selectedDates = []; 599 this.minRange = ''; 600 this.maxRange = ''; 601 this.views[this.currentView]._render(); 602 this._setInputValue(); 603 if (this.opts.onSelect) { 604 this._triggerOnChange() 605 } 606 }, 607 608 /** 609 * Updates datepicker options 610 * @param {String|Object} param - parameter's name to update. If object then it will extend current options 611 * @param {String|Number|Object} [value] - new param value 612 */ 613 update: function (param, value) { 614 var len = arguments.length, 615 lastSelectedDate = this.lastSelectedDate; 616 617 if (len == 2) { 618 this.opts[param] = value; 619 } else if (len == 1 && typeof param == 'object') { 620 this.opts = $.extend(true, this.opts, param) 621 } 622 623 this._createShortCuts(); 624 this._syncWithMinMaxDates(); 625 this._defineLocale(this.opts.language); 626 this.nav._addButtonsIfNeed(); 627 if (!this.opts.onlyTimepicker) this.nav._render(); 628 this.views[this.currentView]._render(); 629 630 if (this.elIsInput && !this.opts.inline) { 631 this._setPositionClasses(this.opts.position); 632 if (this.visible) { 633 this.setPosition(this.opts.position) 634 } 635 } 636 637 if (this.opts.classes) { 638 this.$datepicker.addClass(this.opts.classes) 639 } 640 641 if (this.opts.onlyTimepicker) { 642 this.$datepicker.addClass('-only-timepicker-'); 643 } 644 645 if (this.opts.timepicker) { 646 if (lastSelectedDate) this.timepicker._handleDate(lastSelectedDate); 647 this.timepicker._updateRanges(); 648 this.timepicker._updateCurrentTime(); 649 // Change hours and minutes if it's values have been changed through min/max hours/minutes 650 if (lastSelectedDate) { 651 lastSelectedDate.setHours(this.timepicker.hours); 652 lastSelectedDate.setMinutes(this.timepicker.minutes); 653 } 654 } 655 656 this._setInputValue(); 657 658 return this; 659 }, 660 661 _syncWithMinMaxDates: function () { 662 var curTime = this.date.getTime(); 663 this.silent = true; 664 if (this.minTime > curTime) { 665 this.date = this.minDate; 666 } 667 668 if (this.maxTime < curTime) { 669 this.date = this.maxDate; 670 } 671 this.silent = false; 672 }, 673 674 _isSelected: function (checkDate, cellType) { 675 var res = false; 676 this.selectedDates.some(function (date) { 677 if (datepicker.isSame(date, checkDate, cellType)) { 678 res = date; 679 return true; 680 } 681 }); 682 return res; 683 }, 684 685 _setInputValue: function () { 686 var _this = this, 687 opts = _this.opts, 688 format = _this.loc.dateFormat, 689 altFormat = opts.altFieldDateFormat, 690 value = _this.selectedDates.map(function (date) { 691 return _this.formatDate(format, date) 692 }), 693 altValues; 694 695 if (opts.altField && _this.$altField.length) { 696 altValues = this.selectedDates.map(function (date) { 697 return _this.formatDate(altFormat, date) 698 }); 699 altValues = altValues.join(this.opts.multipleDatesSeparator); 700 this.$altField.val(altValues); 701 } 702 703 value = value.join(this.opts.multipleDatesSeparator); 704 705 this.$el.val(value) 706 }, 707 708 /** 709 * Check if date is between minDate and maxDate 710 * @param date {object} - date object 711 * @param type {string} - cell type 712 * @returns {boolean} 713 * @private 714 */ 715 _isInRange: function (date, type) { 716 var time = date.getTime(), 717 d = datepicker.getParsedDate(date),
718 min = datepicker.getParsedDate(this.minDate), 719 max = datepicker.getParsedDate(this.maxDate), 720 dMinTime = new Date(d.year, d.month, min.date).getTime(), 721 dMaxTime = new Date(d.year, d.month, max.date).getTime(), 722 types = { 723 day: time >= this.minTime && time <= this.maxTime, 724 month: dMinTime >= this.minTime && dMaxTime <= this.maxTime, 725 year: d.year >= min.year && d.year <= max.year 726 }; 727 return type ? types[type] : types.day 728 }, 729 730 _getDimensions: function ($el) { 731 var offset = $el.offset(); 732 733 return { 734 width: $el.outerWidth(), 735 height: $el.outerHeight(), 736 left: offset.left, 737 top: offset.top 738 } 739 }, 740 741 _getDateFromCell: function (cell) { 742 var curDate = this.parsedDate, 743 year = cell.data('year') || curDate.year, 744 month = cell.data('month') == undefined ? curDate.month : cell.data('month'), 745 date = cell.data('date') || 1; 746 747 return new Date(year, month, date); 748 }, 749 750 _setPositionClasses: function (pos) { 751 pos = pos.split(' '); 752 var main = pos[0], 753 sec = pos[1], 754 classes = 'datepicker -' + main + '-' + sec + '- -from-' + main + '-'; 755 756 if (this.visible) classes += ' active'; 757 758 this.$datepicker 759 .removeAttr('class')
760 .addClass(classes); 761 }, 762 763 setPosition: function (position) { 764 position = position || this.opts.position; 765 766 var dims = this._getDimensions(this.$el), 767 selfDims = this._getDimensions(this.$datepicker), 768 pos = position.split(' '), 769 top, left, 770 offset = this.opts.offset, 771 main = pos[0], 772 secondary = pos[1]; 773 774 switch (main) { 775 case 'top': 776 top = dims.top - selfDims.height - offset; 777 break; 778 case 'right': 779 left = dims.left + dims.width + offset; 780 break; 781 case 'bottom': 782 top = dims.top + dims.height + offset; 783 break; 784 case 'left': 785 left = dims.left - selfDims.width - offset; 786 break; 787 } 788 789 switch(secondary) { 790 case 'top': 791 top = dims.top; 792 break; 793 case 'right': 794 left = dims.left + dims.width - selfDims.width; 795 break; 796 case 'bottom': 797 top = dims.top + dims.height - selfDims.height; 798 break; 799 case 'left': 800 left = dims.left; 801 break; 802 case 'center': 803 if (/left|right/.test(main)) { 804 top = dims.top + dims.height/2 - selfDims.height/2; 805 } else { 806 left = dims.left + dims.width/2 - selfDims.width/2; 807 } 808 } 809 810 this.$datepicker 811 .css({ 812 left: left, 813 top: top 814 }) 815 }, 816 817 show: function () { 818 var onShow = this.opts.onShow; 819 820 this.setPosition(this.opts.position); 821 this.$datepicker.addClass('active'); 822 this.visible = true; 823 824 if (onShow) { 825 this._bindVisionEvents(onShow) 826 } 827 }, 828 829 hide: function () { 830 var onHide = this.opts.onHide; 831 832 this.$datepicker 833 .removeClass('active') 834 .css({ 835 left: '-100000px' 836 }); 837 838 this.focused = ''; 839 this.keys = []; 840 841 this.inFocus = false; 842 this.visible = false; 843 this.$el.blur(); 844 845 if (onHide) { 846 this._bindVisionEvents(onHide) 847 } 848 }, 849 850 down: function (date) { 851 this._changeView(date, 'down'); 852 }, 853 854 up: function (date) { 855 this._changeView(date, 'up'); 856 }, 857 858 _bindVisionEvents: function (event) { 859 this.$datepicker.off('transitionend.dp'); 860 event(this, false); 861 this.$datepicker.one('transitionend.dp', event.bind(this, this, true)) 862 }, 863 864 _changeView: function (date, dir) { 865 date = date || this.focused || this.date; 866 867 var nextView = dir == 'up' ? this.viewIndex + 1 : this.viewIndex - 1; 868 if (nextView > 2) nextView = 2; 869 if (nextView < 0) nextView = 0; 870 871 this.silent = true; 872 this.date = new Date(date.getFullYear(), date.getMonth(), 1); 873 this.silent = false; 874 this.view = this.viewIndexes[nextView]; 875 876 }, 877 878 _handleHotKey: function (key) { 879 var date = datepicker.getParsedDate(this._getFocusedDate()), 880 focusedParsed, 881 o = this.opts, 882 newDate, 883 totalDaysInNextMonth, 884 monthChanged = false, 885 yearChanged = false, 886 decadeChanged = false, 887 y = date.year, 888 m = date.month, 889 d = date.date; 890 891 switch (key) { 892 case 'ctrlRight': 893 case 'ctrlUp': 894 m += 1; 895 monthChanged = true; 896 break; 897 case 'ctrlLeft': 898 case 'ctrlDown': 899 m -= 1; 900 monthChanged = true; 901 break; 902 case 'shiftRight': 903 case 'shiftUp': 904 yearChanged = true; 905 y += 1; 906 break; 907 case 'shiftLeft': 908 case 'shiftDown': 909 yearChanged = true; 910 y -= 1; 911 break; 912 case 'altRight': 913 case 'altUp': 914 decadeChanged = true; 915 y += 10; 916 break; 917 case 'altLeft': 918 case 'altDown': 919 decadeChanged = true; 920 y -= 10; 921 break; 922 case 'ctrlShiftUp': 923 this.up(); 924 break; 925 } 926 927 totalDaysInNextMonth = datepicker.getDaysCount(new Date(y,m)); 928 newDate = new Date(y,m,d); 929 930 // If next month has less days than current, set date to total days in that month 931 if (totalDaysInNextMonth < d) d = totalDaysInNextMonth; 932 933 // Check if newDate is in valid range 934 if (newDate.getTime() < this.minTime) { 935 newDate = this.minDate; 936 } else if (newDate.getTime() > this.maxTime) { 937 newDate = this.maxDate; 938 } 939 940 this.focused = newDate; 941 942 focusedParsed = datepicker.getParsedDate(newDate);
943 if (monthChanged && o.onChangeMonth) { 944 o.onChangeMonth(focusedParsed.month, focusedParsed.year) 945 } 946 if (yearChanged && o.onChangeYear) { 947 o.onChangeYear(focusedParsed.year) 948 } 949 if (decadeChanged && o.onChangeDecade) { 950 o.onChangeDecade(this.curDecade) 951 } 952 }, 953 954 _registerKey: function (key) { 955 var exists = this.keys.some(function (curKey) { 956 return curKey == key; 957 }); 958 959 if (!exists) { 960 this.keys.push(key) 961 } 962 }, 963 964 _unRegisterKey: function (key) { 965 var index = this.keys.indexOf(key); 966 967 this.keys.splice(index, 1); 968 }, 969 970 _isHotKeyPressed: function () { 971 var currentHotKey, 972 found = false, 973 _this = this, 974 pressedKeys = this.keys.sort(); 975 976 for (var hotKey in hotKeys) { 977 currentHotKey = hotKeys[hotKey]; 978 if (pressedKeys.length != currentHotKey.length) continue; 979 980 if (currentHotKey.every(function (key, i) { return key == pressedKeys[i]})) { 981 _this._trigger('hotKey', hotKey); 982 found = true; 983 } 984 } 985 986 return found; 987 }, 988 989 _trigger: function (event, args) { 990 this.$el.trigger(event, args) 991 }, 992 993 _focusNextCell: function (keyCode, type) { 994 type = type || this.cellType; 995 996 var date = datepicker.getParsedDate(this._getFocusedDate()), 997 y = date.year, 998 m = date.month, 999 d = date.date; 1000 1001 if (this._isHotKeyPressed()){ 1002 return; 1003 } 1004 1005 switch(keyCode) { 1006 case 37: // left 1007 type == 'day' ? (d -= 1) : ''; 1008 type == 'month' ? (m -= 1) : ''; 1009 type == 'year' ? (y -= 1) : ''; 1010 break; 1011 case 38: // up 1012 type == 'day' ? (d -= 7) : ''; 1013 type == 'month' ? (m -= 3) : ''; 1014 type == 'year' ? (y -= 4) : ''; 1015 break; 1016 case 39: // right 1017 type == 'day' ? (d += 1) : ''; 1018 type == 'month' ? (m += 1) : ''; 1019 type == 'year' ? (y += 1) : ''; 1020 break; 1021 case 40: // down 1022 type == 'day' ? (d += 7) : ''; 1023 type == 'month' ? (m += 3) : ''; 1024 type == 'year' ? (y += 4) : ''; 1025 break; 1026 } 1027 1028 var nd = new Date(y,m,d); 1029 if (nd.getTime() < this.minTime) { 1030 nd = this.minDate; 1031 } else if (nd.getTime() > this.maxTime) { 1032 nd = this.maxDate; 1033 } 1034 1035 this.focused = nd; 1036 1037 }, 1038 1039 _getFocusedDate: function () { 1040 var focused = this.focused || this.selectedDates[this.selectedDates.length - 1], 1041 d = this.parsedDate; 1042 1043 if (!focused) { 1044 switch (this.view) { 1045 case 'days': 1046 focused = new Date(d.year, d.month, new Date().getDate()); 1047 break; 1048 case 'months': 1049 focused = new Date(d.year, d.month, 1); 1050 break; 1051 case 'years': 1052 focused = new Date(d.year, 0, 1); 1053 break; 1054 } 1055 } 1056 1057 return focused; 1058 }, 1059 1060 _getCell: function (date, type) { 1061 type = type || this.cellType; 1062 1063 var d = datepicker.getParsedDate(date), 1064 selector = '.datepicker--cell[data-year="' + d.year + '"]', 1065 $cell; 1066 1067 switch (type) { 1068 case 'month': 1069 selector = '[data-month="' + d.month + '"]'; 1070 break; 1071 case 'day': 1072 selector += '[data-month="' + d.month + '"][data-date="' + d.date + '"]'; 1073 break; 1074 } 1075 $cell = this.views[this.currentView].$el.find(selector); 1076 1077 return $cell.length ? $cell : $(''); 1078 }, 1079 1080 destroy: function () { 1081 var _this = this; 1082 _this.$el 1083 .off('.adp') 1084 .data('datepicker', ''); 1085 1086 _this.selectedDates = []; 1087 _this.focused = ''; 1088 _this.views = {}; 1089 _this.keys = []; 1090 _this.minRange = ''; 1091 _this.maxRange = ''; 1092 1093 if (_this.opts.inline || !_this.elIsInput) { 1094 _this.$datepicker.closest('.datepicker-inline').remove(); 1095 } else { 1096 _this.$datepicker.remove(); 1097 } 1098 }, 1099 1100 _handleAlreadySelectedDates: function (alreadySelected, selectedDate) { 1101 if (this.opts.range) { 1102 if (!this.opts.toggleSelected) { 1103 // Add possibility to select same date when range is true 1104 if (this.selectedDates.length != 2) { 1105 this._trigger('clickCell', selectedDate); 1106 } 1107 } else { 1108 this.removeDate(selectedDate); 1109 } 1110 } else if (this.opts.toggleSelected){ 1111 this.removeDate(selectedDate); 1112 } 1113 1114 // Change last selected date to be able to change time when clicking on this cell 1115 if (!this.opts.toggleSelected) { 1116 this.lastSelectedDate = alreadySelected; 1117 if (this.opts.timepicker) { 1118 this.timepicker._setTime(alreadySelected);
1119 this.timepicker.update(); 1120 } 1121 } 1122 }, 1123 1124 _onShowEvent: function (e) { 1125 if (!this.visible) { 1126 this.show(); 1127 } 1128 }, 1129 1130 _onBlur: function () { 1131 if (!this.inFocus && this.visible) { 1132 this.hide(); 1133 } 1134 }, 1135 1136 _onMouseDownDatepicker: function (e) { 1137 this.inFocus = true; 1138 }, 1139 1140 _onMouseUpDatepicker: function (e) { 1141 this.inFocus = false; 1142 e.originalEvent.inFocus = true; 1143 if (!e.originalEvent.timepickerFocus) this.$el.focus(); 1144 }, 1145 1146 _onKeyUpGeneral: function (e) { 1147 var val = this.$el.val(); 1148 1149 if (!val) { 1150 this.clear(); 1151 } 1152 }, 1153 1154 _onResize: function () { 1155 if (this.visible) { 1156 this.setPosition(); 1157 } 1158 }, 1159 1160 _onMouseUpBody: function (e) { 1161 if (e.originalEvent.inFocus) return; 1162 1163 if (this.visible && !this.inFocus) { 1164 this.hide(); 1165 } 1166 }, 1167 1168 _onMouseUpEl: function (e) { 1169 e.originalEvent.inFocus = true; 1170 setTimeout(this._onKeyUpGeneral.bind(this),4); 1171 }, 1172 1173 _onKeyDown: function (e) { 1174 var code = e.which; 1175 this._registerKey(code); 1176 1177 // Arrows 1178 if (code >= 37 && code <= 40) { 1179 e.preventDefault(); 1180 this._focusNextCell(code); 1181 } 1182 1183 // Enter 1184 if (code == 13) { 1185 if (this.focused) { 1186 if (this._getCell(this.focused).hasClass('-disabled-')) return; 1187 if (this.view != this.opts.minView) { 1188 this.down() 1189 } else { 1190 var alreadySelected = this._isSelected(this.focused, this.cellType); 1191 1192 if (!alreadySelected) { 1193 if (this.timepicker) { 1194 this.focused.setHours(this.timepicker.hours); 1195 this.focused.setMinutes(this.timepicker.minutes); 1196 } 1197 this.selectDate(this.focused); 1198 return; 1199 } 1200 this._handleAlreadySelectedDates(alreadySelected, this.focused) 1201 } 1202 } 1203 } 1204 1205 // Esc 1206 if (code == 27) { 1207 this.hide(); 1208 } 1209 }, 1210 1211 _onKeyUp: function (e) { 1212 var code = e.which; 1213 this._unRegisterKey(code); 1214 }, 1215 1216 _onHotKey: function (e, hotKey) { 1217 this._handleHotKey(hotKey); 1218 }, 1219 1220 _onMouseEnterCell: function (e) { 1221 var $cell = $(e.target).closest('.datepicker--cell'), 1222 date = this._getDateFromCell($cell); 1223 1224 // Prevent from unnecessary rendering and setting new currentDate 1225 this.silent = true; 1226 1227 if (this.focused) { 1228 this.focused = '' 1229 } 1230 1231 $cell.addClass('-focus-'); 1232 1233 this.focused = date; 1234 this.silent = false; 1235 1236 if (this.opts.range && this.selectedDates.length == 1) { 1237 this.minRange = this.selectedDates[0]; 1238 this.maxRange = ''; 1239 if (datepicker.less(this.minRange, this.focused)) { 1240 this.maxRange = this.minRange; 1241 this.minRange = ''; 1242 } 1243 this.views[this.currentView]._update(); 1244 } 1245 }, 1246 1247 _onMouseLeaveCell: function (e) { 1248 var $cell = $(e.target).closest('.datepicker--cell'); 1249 1250 $cell.removeClass('-focus-'); 1251 1252 this.silent = true; 1253 this.focused = ''; 1254 this.silent = false; 1255 }, 1256 1257 _onTimeChange: function (e, h, m) { 1258 var date = new Date(), 1259 selectedDates = this.selectedDates, 1260 selected = false;
1261 1262 if (selectedDates.length) { 1263 selected = true; 1264 date = this.lastSelectedDate; 1265 } 1266 1267 date.setHours(h); 1268 date.setMinutes(m); 1269 1270 if (!selected && !this._getCell(date).hasClass('-disabled-')) { 1271 this.selectDate(date); 1272 } else { 1273 this._setInputValue(); 1274 if (this.opts.onSelect) { 1275 this._triggerOnChange(); 1276 } 1277 } 1278 }, 1279 1280 _onClickCell: function (e, date) { 1281 if (this.timepicker) { 1282 date.setHours(this.timepicker.hours); 1283 date.setMinutes(this.timepicker.minutes); 1284 } 1285 this.selectDate(date); 1286 }, 1287 1288 set focused(val) { 1289 if (!val && this.focused) { 1290 var $cell = this._getCell(this.focused); 1291 1292 if ($cell.length) { 1293 $cell.removeClass('-focus-') 1294 } 1295 } 1296 this._focused = val; 1297 if (this.opts.range && this.selectedDates.length == 1) { 1298 this.minRange = this.selectedDates[0]; 1299 this.maxRange = ''; 1300 if (datepicker.less(this.minRange, this._focused)) { 1301 this.maxRange = this.minRange; 1302 this.minRange = ''; 1303 } 1304 } 1305 if (this.silent) return; 1306 this.date = val; 1307 }, 1308 1309 get focused() { 1310 return this._focused; 1311 }, 1312 1313 get parsedDate() { 1314 return datepicker.getParsedDate(this.date); 1315 }, 1316 1317 set date (val) { 1318 if (!(val instanceof Date)) return; 1319 1320 this.currentDate = val; 1321 1322 if (this.inited && !this.silent) { 1323 this.views[this.view]._render(); 1324 this.nav._render(); 1325 if (this.visible && this.elIsInput) { 1326 this.setPosition(); 1327 } 1328 } 1329 return val; 1330 }, 1331 1332 get date () { 1333 return this.currentDate 1334 }, 1335 1336 set view (val) { 1337 this.viewIndex = this.viewIndexes.indexOf(val); 1338 1339 if (this.viewIndex < 0) { 1340 return; 1341 } 1342 1343 this.prevView = this.currentView; 1344 this.currentView = val; 1345 1346 if (this.inited) { 1347 if (!this.views[val]) { 1348 this.views[val] = new $.fn.datepicker.Body(this, val, this.opts) 1349 } else { 1350 this.views[val]._render(); 1351 } 1352 1353 this.views[this.prevView].hide(); 1354 this.views[val].show(); 1355 this.nav._render(); 1356 1357 if (this.opts.onChangeView) { 1358 this.opts.onChangeView(val) 1359 } 1360 if (this.elIsInput && this.visible) this.setPosition(); 1361 } 1362 1363 return val 1364 }, 1365 1366 get view() { 1367 return this.currentView; 1368 }, 1369 1370 get cellType() { 1371 return this.view.substring(0, this.view.length - 1) 1372 }, 1373 1374 get minTime() { 1375 var min = datepicker.getParsedDate(this.minDate); 1376 return new Date(min.year, min.month, min.date).getTime() 1377 }, 1378 1379 get maxTime() { 1380 var max = datepicker.getParsedDate(this.maxDate); 1381 return new Date(max.year, max.month, max.date).getTime() 1382 }, 1383 1384 get curDecade() { 1385 return datepicker.getDecade(this.date) 1386 } 1387 }; 1388 1389 // Utils 1390 // ------------------------------------------------- 1391 1392 datepicker.getDaysCount = function (date) { 1393 return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); 1394 }; 1395 1396 datepicker.getParsedDate = function (date) { 1397 return { 1398 year: date.getFullYear(), 1399 month: date.getMonth(), 1400 fullMonth: (date.getMonth() + 1) < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1, // One based 1401 date: date.getDate(), 1402 fullDate: date.getDate() < 10 ? '0' + date.getDate() : date.getDate(), 1403 day: date.getDay(), 1404 hours: date.getHours(), 1405 fullHours: date.getHours() < 10 ? '0' + date.getHours() : date.getHours() , 1406 minutes: date.getMinutes(), 1407 fullMinutes: date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() 1408 } 1409 }; 1410 1411 datepicker.getDecade = function (date) { 1412 var firstYear = Math.floor(date.getFullYear() / 10) * 10; 1413 1414 return [firstYear, firstYear + 9]; 1415 }; 1416 1417 datepicker.template = function (str, data) { 1418 return str.replace(/#\{([\w]+)\}/g, function (source, match) { 1419 if (data[match] || data[match] === 0) { 1420 return data[match] 1421 } 1422 }); 1423 }; 1424 1425 datepicker.isSame = function (date1, date2, type) { 1426 if (!date1 || !date2) return false;
1427 var d1 = datepicker.getParsedDate(date1), 1428 d2 = datepicker.getParsedDate(date2), 1429 _type = type ? type : 'day', 1430 1431 conditions = { 1432 day: d1.date == d2.date && d1.month == d2.month && d1.year == d2.year, 1433 month: d1.month == d2.month && d1.year == d2.year, 1434 year: d1.year == d2.year 1435 }; 1436 1437 return conditions[_type]; 1438 }; 1439 1440 datepicker.less = function (dateCompareTo, date, type) { 1441 if (!dateCompareTo || !date) return false; 1442 return date.getTime() < dateCompareTo.getTime(); 1443 }; 1444 1445 datepicker.bigger = function (dateCompareTo, date, type) { 1446 if (!dateCompareTo || !date) return false; 1447 return date.getTime() > dateCompareTo.getTime(); 1448 }; 1449 1450 datepicker.getLeadingZeroNum = function (num) { 1451 return parseInt(num) < 10 ? '0' + num : num; 1452 }; 1453 1454 /** 1455 * Returns copy of date with hours and minutes equals to 0 1456 * @param date {Date} 1457 */ 1458 datepicker.resetTime = function (date) { 1459 if (typeof date != 'object') return; 1460 date = datepicker.getParsedDate(date); 1461 return new Date(date.year, date.month, date.date) 1462 }; 1463 1464 $.fn.datepicker = function ( options ) { 1465 return this.each(function () { 1466 if (!$.data(this, pluginName)) { 1467 $.data(this, pluginName, 1468 new Datepicker( this, options )); 1469 } else { 1470 var _this = $.data(this, pluginName); 1471 1472 _this.opts = $.extend(true, _this.opts, options); 1473 _this.update(); 1474 } 1475 }); 1476 }; 1477 1478 $.fn.datepicker.Constructor = Datepicker; 1479 1480 $.fn.datepicker.language = { 1481 ru: { 1482 days: ['ÐоÑкÑеÑенÑе', 'ÐонеделÑник', 'ÐÑоÑник', 'СÑеда', 'ЧеÑвеÑг', 'ÐÑÑниÑа', 'СÑббоÑа'], 1483 daysShort: ['ÐоÑ','Ðон','ÐÑо','СÑе','ЧеÑ','ÐÑÑ','СÑб'], 1484 daysMin: ['ÐÑ','Ðн','ÐÑ','СÑ','ЧÑ','ÐÑ','Сб'], 1485 months: ['ЯнваÑÑ', 'ФевÑалÑ', 'ÐаÑÑ', 'ÐпÑелÑ', 'Ðай', 'ÐÑнÑ', 'ÐÑлÑ', 'ÐвгÑÑÑ', 'СенÑÑбÑÑ', 'ÐкÑÑбÑÑ', 'ÐоÑбÑÑ', 'ÐекабÑÑ'], 1486 monthsShort: ['Янв', 'Фев', 'ÐаÑ', 'ÐпÑ', 'Ðай', 'ÐÑн', 'ÐÑл', 'Ðвг', 'Сен', 'ÐкÑ', 'ÐоÑ', 'Ðек'], 1487 today: 'СегоднÑ', 1488 clear: 'ÐÑиÑÑиÑÑ', 1489 dateFormat: 'dd.mm.yyyy', 1490 timeFormat: 'hh:ii', 1491 firstDay: 1 1492 } 1493 }; 1494 1495 $(function () { 1496 $(autoInitSelector).datepicker(); 1497 }) 1498 1499})(); 1500 1501;(function () { 1502 var templates = { 1503 days:'' + 1504 '<div class="datepicker--days datepicker--body">' + 1505 '<div class="datepicker--days-names"></div>' + 1506 '<div class="datepicker--cells datepicker--cells-days"></div>' + 1507 '</div>', 1508 months: '' + 1509 '<div class="datepicker--months datepicker--body">' + 1510 '<div class="datepicker--cells datepicker--cells-months"></div>' + 1511 '</div>', 1512 years: '' + 1513 '<div class="datepicker--years datepicker--body">' + 1514 '<div class="datepicker--cells datepicker--cells-years"></div>' + 1515 '</div>' 1516 }, 1517 datepicker = $.fn.datepicker, 1518 dp = datepicker.Constructor; 1519 1520 datepicker.Body = function (d, type, opts) { 1521 this.d = d; 1522 this.type = type; 1523 this.opts = opts; 1524 this.$el = $(''); 1525 1526 if (this.opts.onlyTimepicker) return; 1527 this.init(); 1528 }; 1529 1530 datepicker.Body.prototype = { 1531 init: function () { 1532 this._buildBaseHtml(); 1533 this._render(); 1534 1535 this._bindEvents(); 1536 }, 1537 1538 _bindEvents: function () { 1539 this.$el.on('click', '.datepicker--cell', $.proxy(this._onClickCell, this)); 1540 }, 1541 1542 _buildBaseHtml: function () { 1543 this.$el = $(templates[this.type]).appendTo(this.d.$content); 1544 this.$names = $('.datepicker--days-names', this.$el); 1545 this.$cells = $('.datepicker--cells', this.$el); 1546 }, 1547 1548 _getDayNamesHtml: function (firstDay, curDay, html, i) { 1549 curDay = curDay != undefined ? curDay : firstDay; 1550 html = html ? html : ''; 1551 i = i != undefined ? i : 0; 1552 1553 if (i > 7) return html; 1554 if (curDay == 7) return this._getDayNamesHtml(firstDay, 0, html, ++i); 1555 1556 html += '<div class="datepicker--day-name' + (this.d.isWeekend(curDay) ? " -weekend-" : "") + '">' + this.d.loc.daysMin[curDay] + '</div>'; 1557 1558 return this._getDayNamesHtml(firstDay, ++curDay, html, ++i); 1559 }, 1560 1561 _getCellContents: function (date, type) { 1562 var classes = "datepicker--cell datepicker--cell-" + type, 1563 currentDate = new Date(), 1564 parent = this.d, 1565 minRange = dp.resetTime(parent.minRange), 1566 maxRange = dp.resetTime(parent.maxRange), 1567 opts = parent.opts, 1568 d = dp.getParsedDate(date),
1569 render = {}, 1570 html = d.date; 1571 1572 switch (type) { 1573 case 'day': 1574 if (parent.isWeekend(d.day)) classes += " -weekend-"; 1575 if (d.month != this.d.parsedDate.month) { 1576 classes += " -other-month-"; 1577 if (!opts.selectOtherMonths) { 1578 classes += " -disabled-"; 1579 } 1580 if (!opts.showOtherMonths) html = ''; 1581 } 1582 break; 1583 case 'month': 1584 html = parent.loc[parent.opts.monthsField][d.month]; 1585 break; 1586 case 'year': 1587 var decade = parent.curDecade; 1588 html = d.year; 1589 if (d.year < decade[0] || d.year > decade[1]) { 1590 classes += ' -other-decade-'; 1591 if (!opts.selectOtherYears) { 1592 classes += " -disabled-"; 1593 } 1594 if (!opts.showOtherYears) html = ''; 1595 } 1596 break; 1597 } 1598 1599 if (opts.onRenderCell) { 1600 render = opts.onRenderCell(date, type) || {}; 1601 html = render.html ? render.html : html; 1602 classes += render.classes ? ' ' + render.classes : ''; 1603 } 1604 1605 if (opts.range) { 1606 if (dp.isSame(minRange, date, type)) classes += ' -range-from-'; 1607 if (dp.isSame(maxRange, date, type)) classes += ' -range-to-'; 1608 1609 if (parent.selectedDates.length == 1 && parent.focused) { 1610 if ( 1611 (dp.bigger(minRange, date) && dp.less(parent.focused, date)) || 1612 (dp.less(maxRange, date) && dp.bigger(parent.focused, date))) 1613 { 1614 classes += ' -in-range-' 1615 } 1616 1617 if (dp.less(maxRange, date) && dp.isSame(parent.focused, date)) { 1618 classes += ' -range-from-' 1619 } 1620 if (dp.bigger(minRange, date) && dp.isSame(parent.focused, date)) { 1621 classes += ' -range-to-' 1622 } 1623 1624 } else if (parent.selectedDates.length == 2) { 1625 if (dp.bigger(minRange, date) && dp.less(maxRange, date)) { 1626 classes += ' -in-range-' 1627 } 1628 } 1629 } 1630 1631 1632 if (dp.isSame(currentDate, date, type)) classes += ' -current-'; 1633 if (parent.focused && dp.isSame(date, parent.focused, type)) classes += ' -focus-'; 1634 if (parent._isSelected(date, type)) classes += ' -selected-'; 1635 if (!parent._isInRange(date, type) || render.disabled) classes += ' -disabled-'; 1636 1637 return { 1638 html: html, 1639 classes: classes 1640 } 1641 }, 1642 1643 /** 1644 * Calculates days number to render. Generates days html and returns it. 1645 * @param {object} date - Date object 1646 * @returns {string} 1647 * @private 1648 */ 1649 _getDaysHtml: function (date) { 1650 var totalMonthDays = dp.getDaysCount(date), 1651 firstMonthDay = new Date(date.getFullYear(), date.getMonth(), 1).getDay(), 1652 lastMonthDay = new Date(date.getFullYear(), date.getMonth(), totalMonthDays).getDay(), 1653 daysFromPevMonth = firstMonthDay - this.d.loc.firstDay, 1654 daysFromNextMonth = 6 - lastMonthDay + this.d.loc.firstDay; 1655 1656 daysFromPevMonth = daysFromPevMonth < 0 ? daysFromPevMonth + 7 : daysFromPevMonth; 1657 daysFromNextMonth = daysFromNextMonth > 6 ? daysFromNextMonth - 7 : daysFromNextMonth; 1658 1659 var startDayIndex = -daysFromPevMonth + 1, 1660 m, y, 1661 html = ''; 1662 1663 for (var i = startDayIndex, max = totalMonthDays + daysFromNextMonth; i <= max; i++) { 1664 y = date.getFullYear(); 1665 m = date.getMonth(); 1666 1667 html += this._getDayHtml(new Date(y, m, i)) 1668 } 1669 1670 return html; 1671 }, 1672 1673 _getDayHtml: function (date) { 1674 var content = this._getCellContents(date, 'day'); 1675 1676 return '<div class="' + content.classes + '" ' + 1677 'data-date="' + date.getDate() + '" ' + 1678 'data-month="' + date.getMonth() + '" ' + 1679 'data-year="' + date.getFullYear() + '">' + content.html + '</div>'; 1680 }, 1681 1682 /** 1683 * Generates months html 1684 * @param {object} date - date instance 1685 * @returns {string} 1686 * @private 1687 */ 1688 _getMonthsHtml: function (date) { 1689 var html = '', 1690 d = dp.getParsedDate(date),
1691 i = 0; 1692 1693 while(i < 12) { 1694 html += this._getMonthHtml(new Date(d.year, i)); 1695 i++ 1696 } 1697 1698 return html; 1699 }, 1700 1701 _getMonthHtml: function (date) { 1702 var content = this._getCellContents(date, 'month'); 1703 1704 return '<div class="' + content.classes + '" data-month="' + date.getMonth() + '">' + content.html + '</div>' 1705 }, 1706 1707 _getYearsHtml: function (date) { 1708 var d = dp.getParsedDate(date), 1709 decade = dp.getDecade(date), 1710 firstYear = decade[0] - 1, 1711 html = '', 1712 i = firstYear; 1713 1714 for (i; i <= decade[1] + 1; i++) { 1715 html += this._getYearHtml(new Date(i , 0)); 1716 } 1717 1718 return html; 1719 }, 1720 1721 _getYearHtml: function (date) { 1722 var content = this._getCellContents(date, 'year'); 1723 1724 var html = content.html; //oka 1725 1726 if(this.opts.language === "zh-tw" && this.opts.navTitles.years.search('rrr1') > -1) { html -= 1911; } //oka 1727 1728 return '<div class="' + content.classes + '" data-year="' + date.getFullYear() + '">' + html + '</div>' //oka 1729 1730 // return '<div class="' + content.classes + '" data-year="' + date.getFullYear() + '">' + content.html + '</div>' 1731 }, 1732 1733 _renderTypes: { 1734 days: function () { 1735 var dayNames = this._getDayNamesHtml(this.d.loc.firstDay), 1736 days = this._getDaysHtml(this.d.currentDate); 1737 1738 this.$cells.html(days); 1739 this.$names.html(dayNames) 1740 }, 1741 months: function () { 1742 var html = this._getMonthsHtml(this.d.currentDate); 1743 1744 this.$cells.html(html) 1745 }, 1746 years: function () { 1747 var html = this._getYearsHtml(this.d.currentDate); 1748 1749 this.$cells.html(html) 1750 } 1751 }, 1752 1753 _render: function () { 1754 if (this.opts.onlyTimepicker) return; 1755 this._renderTypes[this.type].bind(this)(); 1756 }, 1757 1758 _update: function () { 1759 var $cells = $('.datepicker--cell', this.$cells), 1760 _this = this, 1761 classes, 1762 $cell, 1763 date; 1764 $cells.each(function (cell, i) { 1765 $cell = $(this); 1766 date = _this.d._getDateFromCell($(this)); 1767 classes = _this._getCellContents(date, _this.d.cellType); 1768 $cell.attr('class',classes.classes) 1769 }); 1770 }, 1771 1772 show: function () { 1773 if (this.opts.onlyTimepicker) return; 1774 this.$el.addClass('active'); 1775 this.acitve = true; 1776 }, 1777 1778 hide: function () { 1779 this.$el.removeClass('active'); 1780 this.active = false; 1781 }, 1782 1783 // Events 1784 // ------------------------------------------------- 1785 1786 _handleClick: function (el) { 1787 var date = el.data('date') || 1, 1788 month = el.data('month') || 0, 1789 year = el.data('year') || this.d.parsedDate.year, 1790 dp = this.d; 1791 // Change view if min view does not reach yet 1792 if (dp.view != this.opts.minView) { 1793 dp.down(new Date(year, month, date)); 1794 return; 1795 } 1796 // Select date if min view is reached 1797 var selectedDate = new Date(year, month, date), 1798 alreadySelected = this.d._isSelected(selectedDate, this.d.cellType); 1799 1800 if (!alreadySelected) { 1801 dp._trigger('clickCell', selectedDate); 1802 return; 1803 } 1804 1805 dp._handleAlreadySelectedDates.bind(dp, alreadySelected, selectedDate)(); 1806 1807 }, 1808 1809 _onClickCell: function (e) { 1810 var $el = $(e.target).closest('.datepicker--cell'); 1811 1812 if ($el.hasClass('-disabled-')) return; 1813 1814 this._handleClick.bind(this)($el); 1815 } 1816 }; 1817})(); 1818 1819;(function () { 1820 var template = '' + 1821 '<div class="datepicker--nav-action" data-action="prev">#{prevHtml}</div>' + 1822 '<div class="datepicker--nav-title">#{title}</div>' + 1823 '<div class="datepicker--nav-action" data-action="next">#{nextHtml}</div>', 1824 buttonsContainerTemplate = '<div class="datepicker--buttons"></div>', 1825 button = '<span class="datepicker--button" data-action="#{action}">#{label}</span>', 1826 datepicker = $.fn.datepicker, 1827 dp = datepicker.Constructor; 1828 1829 datepicker.Navigation = function (d, opts) { 1830 this.d = d; 1831 this.opts = opts; 1832 1833 this.$buttonsContainer = ''; 1834 1835 this.init(); 1836 }; 1837 1838 datepicker.Navigation.prototype = { 1839 init: function () { 1840 this._buildBaseHtml(); 1841 this._bindEvents(); 1842 }, 1843 1844 _bindEvents: function () { 1845 this.d.$nav.on('click', '.datepicker--nav-action', $.proxy(this._onClickNavButton, this)); 1846 this.d.$nav.on('click', '.datepicker--nav-title', $.proxy(this._onClickNavTitle, this)); 1847 this.d.$datepicker.on('click', '.datepicker--button', $.proxy(this._onClickNavButton, this)); 1848 }, 1849 1850 _buildBaseHtml: function () { 1851 if (!this.opts.onlyTimepicker) { 1852 this._render(); 1853 } 1854 this._addButtonsIfNeed(); 1855 }, 1856 1857 _addButtonsIfNeed: function () { 1858 if (this.opts.todayButton) { 1859 this._addButton('today') 1860 } 1861 if (this.opts.clearButton) { 1862 this._addButton('clear') 1863 } 1864 }, 1865 1866 _render: function () { 1867 var title = this._getTitle(this.d.currentDate), 1868 html = dp.template(template, $.extend({title: title}, this.opts)); 1869 this.d.$nav.html(html); 1870 if (this.d.view == 'years') { 1871 $('.datepicker--nav-title', this.d.$nav).addClass('-disabled-'); 1872 } 1873 this.setNavStatus(); 1874 }, 1875 1876 _getTitle: function (date) { 1877 return this.d.formatDate(this.opts.navTitles[this.d.view], date) 1878 }, 1879 1880 _addButton: function (type) { 1881 if (!this.$buttonsContainer.length) { 1882 this._addButtonsContainer(); 1883 } 1884 1885 var data = { 1886 action: type, 1887 label: this.d.loc[type] 1888 }, 1889 html = dp.template(button, data); 1890 1891 if ($('[data-action=' + type + ']', this.$buttonsContainer).length) return; 1892 this.$buttonsContainer.append(html); 1893 }, 1894 1895 _addButtonsContainer: function () { 1896 this.d.$datepicker.append(buttonsContainerTemplate); 1897 this.$buttonsContainer = $('.datepicker--buttons', this.d.$datepicker); 1898 }, 1899 1900 setNavStatus: function () { 1901 if (!(this.opts.minDate || this.opts.maxDate) || !this.opts.disableNavWhenOutOfRange) return; 1902 1903 var date = this.d.parsedDate, 1904 m = date.month, 1905 y = date.year, 1906 d = date.date; 1907 1908 switch (this.d.view) { 1909 case 'days': 1910 if (!this.d._isInRange(new Date(y, m-1, 1), 'month')) { 1911 this._disableNav('prev') 1912 } 1913 if (!this.d._isInRange(new Date(y, m+1, 1), 'month')) { 1914 this._disableNav('next') 1915 } 1916 break; 1917 case 'months': 1918 if (!this.d._isInRange(new Date(y-1, m, d), 'year')) { 1919 this._disableNav('prev') 1920 } 1921 if (!this.d._isInRange(new Date(y+1, m, d), 'year')) { 1922 this._disableNav('next') 1923 } 1924 break; 1925 case 'years': 1926 var decade = dp.getDecade(this.d.date); 1927 if (!this.d._isInRange(new Date(decade[0] - 1, 0, 1), 'year')) { 1928 this._disableNav('prev') 1929 } 1930 if (!this.d._isInRange(new Date(decade[1] + 1, 0, 1), 'year')) { 1931 this._disableNav('next') 1932 } 1933 break; 1934 } 1935 }, 1936 1937 _disableNav: function (nav) { 1938 $('[data-action="' + nav + '"]', this.d.$nav).addClass('-disabled-') 1939 }, 1940 1941 _activateNav: function (nav) { 1942 $('[data-action="' + nav + '"]', this.d.$nav).removeClass('-disabled-') 1943 }, 1944 1945 _onClickNavButton: function (e) { 1946 var $el = $(e.target).closest('[data-action]'), 1947 action = $el.data('action'); 1948 1949 this.d[action](); 1950 }, 1951 1952 _onClickNavTitle: function (e) { 1953 if ($(e.target).hasClass('-disabled-')) return; 1954 1955 if (this.d.view == 'days') { 1956 return this.d.view = 'months' 1957 } 1958 1959 this.d.view = 'years'; 1960 } 1961 } 1962 1963})(); 1964 1965;(function () { 1966 var template = '<div class="datepicker--time">' + 1967 '<div class="datepicker--time-current">' + 1968 ' <span class="datepicker--time-current-hours">#{hourVisible}</span>' + 1969 ' <span class="datepicker--time-current-colon">:</span>' + 1970 ' <span class="datepicker--time-current-minutes">#{minValue}</span>' + 1971 '</div>' + 1972 '<div class="datepicker--time-sliders">' + 1973 ' <div class="datepicker--time-row">' + 1974 ' <input type="range" name="hours" value="#{hourValue}" min="#{hourMin}" max="#{hourMax}" step="#{hourStep}"/>' + 1975 ' </div>' + 1976 ' <div class="datepicker--time-row">' + 1977 ' <input type="range" name="minutes" value="#{minValue}" min="#{minMin}" max="#{minMax}" step="#{minStep}"/>' + 1978 ' </div>' + 1979 '</div>' + 1980 '</div>', 1981 datepicker = $.fn.datepicker, 1982 dp = datepicker.Constructor; 1983 1984 datepicker.Timepicker = function (inst, opts) { 1985 this.d = inst; 1986 this.opts = opts; 1987 1988 this.init(); 1989 }; 1990 1991 datepicker.Timepicker.prototype = { 1992 init: function () { 1993 var input = 'input'; 1994 this._setTime(this.d.date); 1995 this._buildHTML(); 1996
1997 if (navigator.userAgent.match(/trident/gi)) { 1998 input = 'change'; 1999 } 2000 2001 this.d.$el.on('selectDate', this._onSelectDate.bind(this)); 2002 this.$ranges.on(input, this._onChangeRange.bind(this)); 2003 this.$ranges.on('mouseup', this._onMouseUpRange.bind(this)); 2004 this.$ranges.on('mousemove focus ', this._onMouseEnterRange.bind(this)); 2005 this.$ranges.on('mouseout blur', this._onMouseOutRange.bind(this)); 2006 }, 2007 2008 _setTime: function (date) { 2009 var _date = dp.getParsedDate(date); 2010 2011 this._handleDate(date); 2012 this.hours = _date.hours < this.minHours ? this.minHours : _date.hours; 2013 this.minutes = _date.minutes < this.minMinutes ? this.minMinutes : _date.minutes; 2014 }, 2015 2016 /** 2017 * Sets minHours and minMinutes from date (usually it's a minDate) 2018 * Also changes minMinutes if current hours are bigger then @date hours 2019 * @param date {Date} 2020 * @private 2021 */ 2022 _setMinTimeFromDate: function (date) { 2023 this.minHours = date.getHours(); 2024 this.minMinutes = date.getMinutes(); 2025 2026 // If, for example, min hours are 10, and current hours are 12, 2027 // update minMinutes to default value, to be able to choose whole range of values 2028 if (this.d.lastSelectedDate) { 2029 if (this.d.lastSelectedDate.getHours() > date.getHours()) { 2030 this.minMinutes = this.opts.minMinutes; 2031 } 2032 } 2033 }, 2034 2035 _setMaxTimeFromDate: function (date) { 2036 this.maxHours = date.getHours(); 2037 this.maxMinutes = date.getMinutes(); 2038 2039 if (this.d.lastSelectedDate) { 2040 if (this.d.lastSelectedDate.getHours() < date.getHours()) { 2041 this.maxMinutes = this.opts.maxMinutes; 2042 } 2043 } 2044 }, 2045 2046 _setDefaultMinMaxTime: function () { 2047 var maxHours = 23, 2048 maxMinutes = 59, 2049 opts = this.opts; 2050 2051 this.minHours = opts.minHours < 0 || opts.minHours > maxHours ? 0 : opts.minHours; 2052 this.minMinutes = opts.minMinutes < 0 || opts.minMinutes > maxMinutes ? 0 : opts.minMinutes; 2053 this.maxHours = opts.maxHours < 0 || opts.maxHours > maxHours ? maxHours : opts.maxHours; 2054 this.maxMinutes = opts.maxMinutes < 0 || opts.maxMinutes > maxMinutes ? maxMinutes : opts.maxMinutes; 2055 }, 2056 2057 /** 2058 * Looks for min/max hours/minutes and if current values 2059 * are out of range sets valid values. 2060 * @private 2061 */ 2062 _validateHoursMinutes: function (date) { 2063 if (this.hours < this.minHours) { 2064 this.hours = this.minHours; 2065 } else if (this.hours > this.maxHours) { 2066 this.hours = this.maxHours; 2067 } 2068 2069 if (this.minutes < this.minMinutes) { 2070 this.minutes = this.minMinutes; 2071 } else if (this.minutes > this.maxMinutes) { 2072 this.minutes = this.maxMinutes; 2073 } 2074 }, 2075 2076 _buildHTML: function () { 2077 var lz = dp.getLeadingZeroNum, 2078 data = { 2079 hourMin: this.minHours, 2080 hourMax: lz(this.maxHours), 2081 hourStep: this.opts.hoursStep, 2082 hourValue: this.hours, 2083 hourVisible: lz(this.displayHours), 2084 minMin: this.minMinutes, 2085 minMax: lz(this.maxMinutes), 2086 minStep: this.opts.minutesStep, 2087 minValue: lz(this.minutes) 2088 }, 2089 _template = dp.template(template, data); 2090 2091 this.$timepicker = $(_template).appendTo(this.d.$datepicker); 2092 this.$ranges = $('[type="range"]', this.$timepicker); 2093 this.$hours = $('[name="hours"]', this.$timepicker); 2094 this.$minutes = $('[name="minutes"]', this.$timepicker); 2095 this.$hoursText = $('.datepicker--time-current-hours', this.$timepicker); 2096 this.$minutesText = $('.datepicker--time-current-minutes', this.$timepicker); 2097 2098 if (this.d.ampm) { 2099 this.$ampm = $('<span class="datepicker--time-current-ampm">') 2100 .appendTo($('.datepicker--time-current', this.$timepicker)) 2101 .html(this.dayPeriod); 2102 2103 this.$timepicker.addClass('-am-pm-'); 2104 } 2105 }, 2106 2107 _updateCurrentTime: function () { 2108 var h = dp.getLeadingZeroNum(this.displayHours), 2109 m = dp.getLeadingZeroNum(this.minutes); 2110 2111 this.$hoursText.html(h); 2112 this.$minutesText.html(m); 2113 2114 if (this.d.ampm) { 2115 this.$ampm.html(this.dayPeriod); 2116 } 2117 }, 2118 2119 _updateRanges: function () { 2120 this.$hours.attr({ 2121 min: this.minHours, 2122 max: this.maxHours 2123 }).val(this.hours); 2124 2125 this.$minutes.attr({ 2126 min: this.minMinutes, 2127 max: this.maxMinutes 2128 }).val(this.minutes) 2129 }, 2130 2131 /** 2132 * Sets minHours, minMinutes etc. from date. If date is not passed, than sets 2133 * values from options 2134 * @param [date] {object} - Date object, to get values from 2135 * @private 2136 */ 2137 _handleDate: function (date) { 2138 this._setDefaultMinMaxTime(); 2139 if (date) { 2140 if (dp.isSame(date, this.d.opts.minDate)) { 2141 this._setMinTimeFromDate(this.d.opts.minDate); 2142 } else if (dp.isSame(date, this.d.opts.maxDate)) { 2143 this._setMaxTimeFromDate(this.d.opts.maxDate); 2144 } 2145 } 2146
2147 this._validateHoursMinutes(date); 2148 }, 2149 2150 update: function () { 2151 this._updateRanges(); 2152 this._updateCurrentTime(); 2153 }, 2154 2155 /** 2156 * Calculates valid hour value to display in text input and datepicker's body. 2157 * @param date {Date|Number} - date or hours 2158 * @param [ampm] {Boolean} - 12 hours mode 2159 * @returns {{hours: *, dayPeriod: string}} 2160 * @private 2161 */ 2162 _getValidHoursFromDate: function (date, ampm) { 2163 var d = date, 2164 hours = date; 2165 2166 if (date instanceof Date) { 2167 d = dp.getParsedDate(date); 2168 hours = d.hours; 2169 } 2170 2171 var _ampm = ampm || this.d.ampm, 2172 dayPeriod = 'am'; 2173 2174 if (_ampm) { 2175 switch(true) { 2176 case hours == 0: 2177 hours = 12; 2178 break; 2179 case hours == 12: 2180 dayPeriod = 'pm'; 2181 break; 2182 case hours > 11: 2183 hours = hours - 12; 2184 dayPeriod = 'pm'; 2185 break; 2186 default: 2187 break; 2188 } 2189 } 2190 2191 return { 2192 hours: hours, 2193 dayPeriod: dayPeriod 2194 } 2195 }, 2196 2197 set hours (val) { 2198 this._hours = val; 2199 2200 var displayHours = this._getValidHoursFromDate(val); 2201 2202 this.displayHours = displayHours.hours; 2203 this.dayPeriod = displayHours.dayPeriod; 2204 }, 2205 2206 get hours() { 2207 return this._hours; 2208 }, 2209 2210 // Events 2211 // ------------------------------------------------- 2212 2213 _onChangeRange: function (e) { 2214 var $target = $(e.target), 2215 name = $target.attr('name'); 2216 2217 this.d.timepickerIsActive = true; 2218 2219 this[name] = $target.val(); 2220 this._updateCurrentTime(); 2221 this.d._trigger('timeChange', [this.hours, this.minutes]); 2222 2223 this._handleDate(this.d.lastSelectedDate); 2224 this.update() 2225 }, 2226 2227 _onSelectDate: function (e, data) { 2228 this._handleDate(data); 2229 this.update(); 2230 }, 2231 2232 _onMouseEnterRange: function (e) { 2233 var name = $(e.target).attr('name'); 2234 $('.datepicker--time-current-' + name, this.$timepicker).addClass('-focus-'); 2235 }, 2236 2237 _onMouseOutRange: function (e) { 2238 var name = $(e.target).attr('name'); 2239 if (this.d.inFocus) return; // Prevent removing focus when mouse out of range slider 2240 $('.datepicker--time-current-' + name, this.$timepicker).removeClass('-focus-'); 2241 }, 2242 2243 _onMouseUpRange: function (e) { 2244 this.d.timepickerIsActive = false; 2245 } 2246 }; 2247})(); 2248 })(window, jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.