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