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