Advertisement
Guest User

Untitled

a guest
May 3rd, 2019
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
jQuery 77.16 KB | None | 0 0
  1. ;(function (window, $, undefined) { ;(function () {
  2.     var VERSION = '2.2.3',
  3.         pluginName = 'datepicker',
  4.         autoInitSelector = '.datepicker-here',
  5.         $body, $datepickersContainer,
  6.         containerBuilt = false,
  7.         baseTemplate = '' +
  8.             '<div class="datepicker">' +
  9.             '<i class="datepicker--pointer"></i>' +
  10.             '<nav class="datepicker--nav"></nav>' +
  11.             '<div class="datepicker--content"></div>' +
  12.             '</div>',
  13.         defaults = {
  14.             classes: '',
  15.             inline: false,
  16.             language: 'ru',
  17.             startDate: new Date(),
  18.             firstDay: '',
  19.             weekends: [6, 0],
  20.             dateFormat: '',
  21.             altField: '',
  22.             altFieldDateFormat: '@',
  23.             toggleSelected: true,
  24.             keyboardNav: true,
  25.  
  26.             position: 'bottom left',
  27.             offset: 12,
  28.  
  29.             view: 'days',
  30.             minView: 'days',
  31.  
  32.             showOtherMonths: true,
  33.             selectOtherMonths: true,
  34.             moveToOtherMonthsOnSelect: true,
  35.  
  36.             showOtherYears: true,
  37.             selectOtherYears: true,
  38.             moveToOtherYearsOnSelect: true,
  39.  
  40.             minDate: '',
  41.             maxDate: '',
  42.             disableNavWhenOutOfRange: true,
  43.  
  44.             multipleDates: false, // Boolean or Number
  45.             multipleDatesSeparator: ',',
  46.             range: false,
  47.  
  48.             todayButton: false,
  49.             clearButton: false,
  50.  
  51.             showEvent: 'focus',
  52.             autoClose: false,
  53.  
  54.             // navigation
  55.             monthsField: 'monthsShort',
  56.             prevHtml: '<svg><path d="M 17,12 l -5,5 l 5,5"></path></svg>',
  57.             nextHtml: '<svg><path d="M 14,12 l 5,5 l -5,5"></path></svg>',
  58.             navTitles: {
  59.                 days: 'MM, <i>yyyy</i>',
  60.                 months: 'yyyy',
  61.                 years: 'yyyy1 - yyyy2'
  62.             },
  63.  
  64.             // timepicker
  65.             timepicker: false,
  66.             onlyTimepicker: false,
  67.             dateTimeSeparator: ' ',
  68.             timeFormat: '',
  69.             minHours: 0,
  70.             maxHours: 24,
  71.             minMinutes: 0,
  72.             maxMinutes: 59,
  73.             hoursStep: 1,
  74.             minutesStep: 1,
  75.  
  76.             // events
  77.             onSelect: '',
  78.             onShow: '',
  79.             onHide: '',
  80.             onChangeMonth: '',
  81.             onChangeYear: '',
  82.             onChangeDecade: '',
  83.             onChangeView: '',
  84.             onRenderCell: ''
  85.         },
  86.         hotKeys = {
  87.             'ctrlRight': [17, 39],
  88.             'ctrlUp': [17, 38],
  89.             'ctrlLeft': [17, 37],
  90.             'ctrlDown': [17, 40],
  91.             'shiftRight': [16, 39],
  92.             'shiftUp': [16, 38],
  93.             'shiftLeft': [16, 37],
  94.             'shiftDown': [16, 40],
  95.             'altUp': [18, 38],
  96.             'altRight': [18, 39],
  97.             'altLeft': [18, 37],
  98.             'altDown': [18, 40],
  99.             'ctrlShiftUp': [16, 17, 38]
  100.         },
  101.         datepicker;
  102.  
  103.     var Datepicker  = function (el, options) {
  104.         this.el = el;
  105.         this.$el = $(el);
  106.  
  107.         this.opts = $.extend(true, {}, defaults, options, this.$el.data());
  108.  
  109.         if ($body == undefined) {
  110.             $body = $('body');
  111.         }
  112.  
  113.         if (!this.opts.startDate) {
  114.             this.opts.startDate = new Date();
  115.         }
  116.  
  117.         if (this.el.nodeName == 'INPUT') {
  118.             this.elIsInput = true;
  119.         }
  120.  
  121.         if (this.opts.altField) {
  122.             this.$altField = typeof this.opts.altField == 'string' ? $(this.opts.altField) : this.opts.altField;
  123.         }
  124.  
  125.         this.inited = false;
  126.         this.visible = false;
  127.         this.silent = false; // Need to prevent unnecessary rendering
  128.  
  129.         this.currentDate = this.opts.startDate;
  130.         this.currentView = this.opts.view;
  131.         this._createShortCuts();
  132.         this.selectedDates = [];
  133.         this.views = {};
  134.         this.keys = [];
  135.         this.minRange = '';
  136.         this.maxRange = '';
  137.         this._prevOnSelectValue = '';
  138.  
  139.         this.init()
  140.     };
  141.  
  142.     datepicker = Datepicker;
  143.  
  144.     datepicker.prototype = {
  145.         VERSION: VERSION,
  146.         viewIndexes: ['days', 'months', 'years'],
  147.  
  148.         init: function () {
  149.             if (!containerBuilt && !this.opts.inline && this.elIsInput) {
  150.                 this._buildDatepickersContainer();
  151.             }
  152.             this._buildBaseHtml();
  153.             this._defineLocale(this.opts.language);
  154.             this._syncWithMinMaxDates();
  155.  
  156.             if (this.elIsInput) {
  157.                 if (!this.opts.inline) {
  158.                     // Set extra classes for proper transitions
  159.                     this._setPositionClasses(this.opts.position);
  160.                     this._bindEvents()
  161.                 }
  162.                 if (this.opts.keyboardNav && !this.opts.onlyTimepicker) {
  163.                     this._bindKeyboardEvents();
  164.                 }
  165.                 this.$datepicker.on('mousedown', this._onMouseDownDatepicker.bind(this));
  166.                 this.$datepicker.on('mouseup', this._onMouseUpDatepicker.bind(this));
  167.             }
  168.  
  169.             if (this.opts.classes) {
  170.                 this.$datepicker.addClass(this.opts.classes)
  171.             }
  172.  
  173.             if (this.opts.timepicker) {
  174.                 this.timepicker = new $.fn.datepicker.Timepicker(this, this.opts);
  175.                 this._bindTimepickerEvents();
  176.             }
  177.  
  178.             if (this.opts.onlyTimepicker) {
  179.                 this.$datepicker.addClass('-only-timepicker-');
  180.             }
  181.  
  182.             this.views[this.currentView] = new $.fn.datepicker.Body(this, this.currentView, this.opts);
  183.             this.views[this.currentView].show();
  184.             this.nav = new $.fn.datepicker.Navigation(this, this.opts);
  185.             this.view = this.currentView;
  186.  
  187.             this.$el.on('clickCell.adp', this._onClickCell.bind(this));
  188.             this.$datepicker.on('mouseenter', '.datepicker--cell', this._onMouseEnterCell.bind(this));
  189.             this.$datepicker.on('mouseleave', '.datepicker--cell', this._onMouseLeaveCell.bind(this));
  190.  
  191.             this.inited = true;
  192.         },
  193.  
  194.         _createShortCuts: function () {
  195.             this.minDate = this.opts.minDate ? this.opts.minDate : new Date(-8639999913600000);
  196.             this.maxDate = this.opts.maxDate ? this.opts.maxDate : new Date(8639999913600000);
  197.         },
  198.  
  199.         _bindEvents : function () {
  200.             this.$el.on(this.opts.showEvent + '.adp', this._onShowEvent.bind(this));
  201.             this.$el.on('mouseup.adp', this._onMouseUpEl.bind(this));
  202.             this.$el.on('blur.adp', this._onBlur.bind(this));
  203.             this.$el.on('keyup.adp', this._onKeyUpGeneral.bind(this));
  204.             $(window).on('resize.adp', this._onResize.bind(this));
  205.             $('body').on('mouseup.adp', this._onMouseUpBody.bind(this));
  206.         },
  207.  
  208.         _bindKeyboardEvents: function () {
  209.             this.$el.on('keydown.adp', this._onKeyDown.bind(this));
  210.             this.$el.on('keyup.adp', this._onKeyUp.bind(this));
  211.             this.$el.on('hotKey.adp', this._onHotKey.bind(this));
  212.         },
  213.  
  214.         _bindTimepickerEvents: function () {
  215.             this.$el.on('timeChange.adp', this._onTimeChange.bind(this));
  216.         },
  217.  
  218.         isWeekend: function (day) {
  219.             return this.opts.weekends.indexOf(day) !== -1;
  220.         },
  221.  
  222.         _defineLocale: function (lang) {
  223.             if (typeof lang == 'string') {
  224.                 this.loc = $.fn.datepicker.language[lang];
  225.                 if (!this.loc) {
  226.                     console.warn('Can\'t find language "' + lang + '" in Datepicker.language, will use "ru" instead');
  227.                     this.loc = $.extend(true, {}, $.fn.datepicker.language.ru)
  228.                 }
  229.  
  230.                 this.loc = $.extend(true, {}, $.fn.datepicker.language.ru, $.fn.datepicker.language[lang])
  231.             } else {
  232.                 this.loc = $.extend(true, {}, $.fn.datepicker.language.ru, lang)
  233.             }
  234.  
  235.             if (this.opts.dateFormat) {
  236.                 this.loc.dateFormat = this.opts.dateFormat
  237.             }
  238.  
  239.             if (this.opts.timeFormat) {
  240.                 this.loc.timeFormat = this.opts.timeFormat
  241.             }
  242.  
  243.             if (this.opts.firstDay !== '') {
  244.                 this.loc.firstDay = this.opts.firstDay
  245.             }
  246.  
  247.             if (this.opts.timepicker) {
  248.                 this.loc.dateFormat = [this.loc.dateFormat, this.loc.timeFormat].join(this.opts.dateTimeSeparator);
  249.             }
  250.  
  251.             if (this.opts.onlyTimepicker) {
  252.                 this.loc.dateFormat = this.loc.timeFormat;
  253.             }
  254.  
  255.             var boundary = this._getWordBoundaryRegExp;
  256.             if (this.loc.timeFormat.match(boundary('aa')) ||
  257.                 this.loc.timeFormat.match(boundary('AA'))
  258.             ) {
  259.                this.ampm = true;
  260.             }
  261.         },
  262.  
  263.         _buildDatepickersContainer: function () {
  264.             containerBuilt = true;
  265.             $body.append('<div class="datepickers-container" id="datepickers-container"></div>');
  266.             $datepickersContainer = $('#datepickers-container');
  267.         },
  268.  
  269.         _buildBaseHtml: function () {
  270.             var $appendTarget,
  271.                 $inline = $('<div class="datepicker-inline">');
  272.  
  273.             if(this.el.nodeName == 'INPUT') {
  274.                 if (!this.opts.inline) {
  275.                     $appendTarget = $datepickersContainer;
  276.                 } else {
  277.                     $appendTarget = $inline.insertAfter(this.$el)
  278.                 }
  279.             } else {
  280.                 $appendTarget = $inline.appendTo(this.$el)
  281.             }
  282.  
  283.             this.$datepicker = $(baseTemplate).appendTo($appendTarget);
  284.             this.$content = $('.datepicker--content', this.$datepicker);
  285.             this.$nav = $('.datepicker--nav', this.$datepicker);
  286.         },
  287.  
  288.         _triggerOnChange: function () {
  289.             if (!this.selectedDates.length) {
  290.                 // Prevent from triggering multiple onSelect callback with same argument (empty string) in IE10-11
  291.                 if (this._prevOnSelectValue === '') return;
  292.                 this._prevOnSelectValue = '';
  293.                 return this.opts.onSelect('', '', this);
  294.             }
  295.  
  296.             var selectedDates = this.selectedDates,
  297.                 parsedSelected = datepicker.getParsedDate(selectedDates[0]),
  298.                 formattedDates,
  299.                 _this = this,
  300.                 dates = new Date(
  301.                     parsedSelected.year,
  302.                     parsedSelected.month,
  303.                     parsedSelected.date,
  304.                     parsedSelected.hours,
  305.                     parsedSelected.minutes
  306.                 );
  307.  
  308.                 formattedDates = selectedDates.map(function (date) {
  309.                     return _this.formatDate(_this.loc.dateFormat, date)
  310.                 }).join(this.opts.multipleDatesSeparator);
  311.  
  312.             // Create new dates array, to separate it from original selectedDates
  313.             if (this.opts.multipleDates || this.opts.range) {
  314.                 dates = selectedDates.map(function(date) {
  315.                     var parsedDate = datepicker.getParsedDate(date);
  316.                     return new Date(
  317.                         parsedDate.year,
  318.                         parsedDate.month,
  319.                         parsedDate.date,
  320.                         parsedDate.hours,
  321.                         parsedDate.minutes
  322.                     );
  323.                 })
  324.             }
  325.  
  326.             this._prevOnSelectValue = formattedDates;
  327.             this.opts.onSelect(formattedDates, dates, this);
  328.         },
  329.  
  330.         next: function () {
  331.             var d = this.parsedDate,
  332.                 o = this.opts;
  333.             switch (this.view) {
  334.                 case 'days':
  335.                     this.date = new Date(d.year, d.month + 1, 1);
  336.                     if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year);
  337.                     break;
  338.                 case 'months':
  339.                     this.date = new Date(d.year + 1, d.month, 1);
  340.                     if (o.onChangeYear) o.onChangeYear(this.parsedDate.year);
  341.                     break;
  342.                 case 'years':
  343.                     this.date = new Date(d.year + 10, 0, 1);
  344.                     if (o.onChangeDecade) o.onChangeDecade(this.curDecade);
  345.                     break;
  346.             }
  347.         },
  348.  
  349.         prev: function () {
  350.             var d = this.parsedDate,
  351.                 o = this.opts;
  352.             switch (this.view) {
  353.                 case 'days':
  354.                     this.date = new Date(d.year, d.month - 1, 1);
  355.                     if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year);
  356.                     break;
  357.                 case 'months':
  358.                     this.date = new Date(d.year - 1, d.month, 1);
  359.                     if (o.onChangeYear) o.onChangeYear(this.parsedDate.year);
  360.                     break;
  361.                 case 'years':
  362.                     this.date = new Date(d.year - 10, 0, 1);
  363.                     if (o.onChangeDecade) o.onChangeDecade(this.curDecade);
  364.                     break;
  365.             }
  366.         },
  367.  
  368.         formatDate: function (string, date) {
  369.             date = date || this.date;
  370.             var result = string,
  371.                 boundary = this._getWordBoundaryRegExp,
  372.                 locale = this.loc,
  373.                 leadingZero = datepicker.getLeadingZeroNum,
  374.                 decade = datepicker.getDecade(date),
  375.                 d = datepicker.getParsedDate(date),
  376.                 fullHours = d.fullHours,
  377.                 hours = d.hours,
  378.                 ampm = string.match(boundary('aa')) || string.match(boundary('AA')),
  379.                 dayPeriod = 'am',
  380.                 replacer = this._replacer,
  381.                 validHours;
  382.  
  383.             if (this.opts.timepicker && this.timepicker && ampm) {
  384.                 validHours = this.timepicker._getValidHoursFromDate(date, ampm);
  385.                 fullHours = leadingZero(validHours.hours);
  386.                 hours = validHours.hours;
  387.                 dayPeriod = validHours.dayPeriod;
  388.             }
  389.  
  390.             switch (true) {
  391.                 case /@/.test(result):
  392.                     result = result.replace(/@/, date.getTime());
  393.                 case /aa/.test(result):
  394.                     result = replacer(result, boundary('aa'), dayPeriod);
  395.                 case /AA/.test(result):
  396.                     result = replacer(result, boundary('AA'), dayPeriod.toUpperCase());
  397.                 case /dd/.test(result):
  398.                     result = replacer(result, boundary('dd'), d.fullDate);
  399.                 case /d/.test(result):
  400.                     result = replacer(result, boundary('d'), d.date);
  401.                 case /DD/.test(result):
  402.                     result = replacer(result, boundary('DD'), locale.days[d.day]);
  403.                 case /D/.test(result):
  404.                     result = replacer(result, boundary('D'), locale.daysShort[d.day]);
  405.                 case /mm/.test(result):
  406.                     result = replacer(result, boundary('mm'), d.fullMonth);
  407.                 case /m/.test(result):
  408.                     result = replacer(result, boundary('m'), d.month + 1);
  409.                 case /MM/.test(result):
  410.                     result = replacer(result, boundary('MM'), this.loc.months[d.month]);
  411.                 case /M/.test(result):
  412.                     result = replacer(result, boundary('M'), locale.monthsShort[d.month]);
  413.                 case /ii/.test(result):
  414.                     result = replacer(result, boundary('ii'), d.fullMinutes);
  415.                 case /i/.test(result):
  416.                     result = replacer(result, boundary('i'), d.minutes);
  417.                 case /hh/.test(result):
  418.                     result = replacer(result, boundary('hh'), fullHours);
  419.                 case /h/.test(result):
  420.                     result = replacer(result, boundary('h'), hours);
  421.                 case /yyyy/.test(result):
  422.                     result = replacer(result, boundary('yyyy'), d.year);
  423.                 case /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.     $.fn.datepicker.language = {
  1475.         ru: {
  1476.             days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
  1477.             daysShort: ['Вос','Пон','Вто','Сре','Чет','Пят','Суб'],
  1478.             daysMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],
  1479.             months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
  1480.             monthsShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
  1481.             today: 'Сегодня',
  1482.             clear: 'Очистить',
  1483.             dateFormat: 'dd.mm.yyyy',
  1484.             timeFormat: 'hh:ii',
  1485.             firstDay: 1
  1486.         }
  1487.     };
  1488.  
  1489.     $(function () {
  1490.         $(autoInitSelector).datepicker();
  1491.     })
  1492.  
  1493. })();
  1494.  
  1495. ;(function () {
  1496.     var templates = {
  1497.         days:'' +
  1498.         '<div class="datepicker--days datepicker--body">' +
  1499.         '<div class="datepicker--days-names"></div>' +
  1500.         '<div class="datepicker--cells datepicker--cells-days"></div>' +
  1501.         '</div>',
  1502.         months: '' +
  1503.         '<div class="datepicker--months datepicker--body">' +
  1504.         '<div class="datepicker--cells datepicker--cells-months"></div>' +
  1505.         '</div>',
  1506.         years: '' +
  1507.         '<div class="datepicker--years datepicker--body">' +
  1508.         '<div class="datepicker--cells datepicker--cells-years"></div>' +
  1509.         '</div>'
  1510.         },
  1511.         datepicker = $.fn.datepicker,
  1512.         dp = datepicker.Constructor;
  1513.  
  1514.     datepicker.Body = function (d, type, opts) {
  1515.         this.d = d;
  1516.         this.type = type;
  1517.         this.opts = opts;
  1518.         this.$el = $('');
  1519.  
  1520.         if (this.opts.onlyTimepicker) return;
  1521.         this.init();
  1522.     };
  1523.  
  1524.     datepicker.Body.prototype = {
  1525.         init: function () {
  1526.             this._buildBaseHtml();
  1527.             this._render();
  1528.  
  1529.             this._bindEvents();
  1530.         },
  1531.  
  1532.         _bindEvents: function () {
  1533.             this.$el.on('click', '.datepicker--cell', $.proxy(this._onClickCell, this));
  1534.         },
  1535.  
  1536.         _buildBaseHtml: function () {
  1537.             this.$el = $(templates[this.type]).appendTo(this.d.$content);
  1538.             this.$names = $('.datepicker--days-names', this.$el);
  1539.             this.$cells = $('.datepicker--cells', this.$el);
  1540.         },
  1541.  
  1542.         _getDayNamesHtml: function (firstDay, curDay, html, i) {
  1543.             curDay = curDay != undefined ? curDay : firstDay;
  1544.             html = html ? html : '';
  1545.             i = i != undefined ? i : 0;
  1546.  
  1547.             if (i > 7) return html;
  1548.             if (curDay == 7) return this._getDayNamesHtml(firstDay, 0, html, ++i);
  1549.  
  1550.             html += '<div class="datepicker--day-name' + (this.d.isWeekend(curDay) ? " -weekend-" : "") + '">' + this.d.loc.daysMin[curDay] + '</div>';
  1551.  
  1552.             return this._getDayNamesHtml(firstDay, ++curDay, html, ++i);
  1553.         },
  1554.  
  1555.         _getCellContents: function (date, type) {
  1556.             var classes = "datepicker--cell datepicker--cell-" + type,
  1557.                 currentDate = new Date(),
  1558.                 parent = this.d,
  1559.                 minRange = dp.resetTime(parent.minRange),
  1560.                 maxRange = dp.resetTime(parent.maxRange),
  1561.                 opts = parent.opts,
  1562.                 d = dp.getParsedDate(date),
  1563.                 render = {},
  1564.                 html = d.date;
  1565.  
  1566.             switch (type) {
  1567.                 case 'day':
  1568.                     if (parent.isWeekend(d.day)) classes += " -weekend-";
  1569.                     if (d.month != this.d.parsedDate.month) {
  1570.                         classes += " -other-month-";
  1571.                         if (!opts.selectOtherMonths) {
  1572.                             classes += " -disabled-";
  1573.                         }
  1574.                         if (!opts.showOtherMonths) html = '';
  1575.                     }
  1576.                     break;
  1577.                 case 'month':
  1578.                     html = parent.loc[parent.opts.monthsField][d.month];
  1579.                     break;
  1580.                 case 'year':
  1581.                     var decade = parent.curDecade;
  1582.                     html = d.year;
  1583.                     if (d.year < decade[0] || d.year > decade[1]) {
  1584.                         classes += ' -other-decade-';
  1585.                         if (!opts.selectOtherYears) {
  1586.                             classes += " -disabled-";
  1587.                         }
  1588.                         if (!opts.showOtherYears) html = '';
  1589.                     }
  1590.                     break;
  1591.             }
  1592.  
  1593.             if (opts.onRenderCell) {
  1594.                 render = opts.onRenderCell(date, type) || {};
  1595.                 html = render.html ? render.html : html;
  1596.                 classes += render.classes ? ' ' + render.classes : '';
  1597.             }
  1598.  
  1599.             if (opts.range) {
  1600.                 if (dp.isSame(minRange, date, type)) classes += ' -range-from-';
  1601.                 if (dp.isSame(maxRange, date, type)) classes += ' -range-to-';
  1602.  
  1603.                 if (parent.selectedDates.length == 1 && parent.focused) {
  1604.                     if (
  1605.                         (dp.bigger(minRange, date) && dp.less(parent.focused, date)) ||
  1606.                         (dp.less(maxRange, date) && dp.bigger(parent.focused, date)))
  1607.                     {
  1608.                         classes += ' -in-range-'
  1609.                     }
  1610.  
  1611.                     if (dp.less(maxRange, date) && dp.isSame(parent.focused, date)) {
  1612.                         classes += ' -range-from-'
  1613.                     }
  1614.                     if (dp.bigger(minRange, date) && dp.isSame(parent.focused, date)) {
  1615.                         classes += ' -range-to-'
  1616.                     }
  1617.  
  1618.                 } else if (parent.selectedDates.length == 2) {
  1619.                     if (dp.bigger(minRange, date) && dp.less(maxRange, date)) {
  1620.                         classes += ' -in-range-'
  1621.                     }
  1622.                 }
  1623.             }
  1624.  
  1625.  
  1626.             if (dp.isSame(currentDate, date, type)) classes += ' -current-';
  1627.             if (parent.focused && dp.isSame(date, parent.focused, type)) classes += ' -focus-';
  1628.             if (parent._isSelected(date, type)) classes += ' -selected-';
  1629.             if (!parent._isInRange(date, type) || render.disabled) classes += ' -disabled-';
  1630.  
  1631.             return {
  1632.                 html: html,
  1633.                 classes: classes
  1634.             }
  1635.         },
  1636.  
  1637.         /**
  1638.          * Calculates days number to render. Generates days html and returns it.
  1639.          * @param {object} date - Date object
  1640.          * @returns {string}
  1641.          * @private
  1642.          */
  1643.         _getDaysHtml: function (date) {
  1644.             var totalMonthDays = dp.getDaysCount(date),
  1645.                 firstMonthDay = new Date(date.getFullYear(), date.getMonth(), 1).getDay(),
  1646.                 lastMonthDay = new Date(date.getFullYear(), date.getMonth(), totalMonthDays).getDay(),
  1647.                 daysFromPevMonth = firstMonthDay - this.d.loc.firstDay,
  1648.                 daysFromNextMonth = 6 - lastMonthDay + this.d.loc.firstDay;
  1649.  
  1650.             daysFromPevMonth = daysFromPevMonth < 0 ? daysFromPevMonth + 7 : daysFromPevMonth;
  1651.             daysFromNextMonth = daysFromNextMonth > 6 ? daysFromNextMonth - 7 : daysFromNextMonth;
  1652.  
  1653.             var startDayIndex = -daysFromPevMonth + 1,
  1654.                 m, y,
  1655.                 html = '';
  1656.  
  1657.             for (var i = startDayIndex, max = totalMonthDays + daysFromNextMonth; i <= max; i++) {
  1658.                 y = date.getFullYear();
  1659.                 m = date.getMonth();
  1660.  
  1661.                 html += this._getDayHtml(new Date(y, m, i))
  1662.             }
  1663.  
  1664.             return html;
  1665.         },
  1666.  
  1667.         _getDayHtml: function (date) {
  1668.            var content = this._getCellContents(date, 'day');
  1669.  
  1670.             return '<div class="' + content.classes + '" ' +
  1671.                 'data-date="' + date.getDate() + '" ' +
  1672.                 'data-month="' + date.getMonth() + '" ' +
  1673.                 'data-year="' + date.getFullYear() + '">' + content.html + '</div>';
  1674.         },
  1675.  
  1676.         /**
  1677.          * Generates months html
  1678.          * @param {object} date - date instance
  1679.          * @returns {string}
  1680.          * @private
  1681.          */
  1682.         _getMonthsHtml: function (date) {
  1683.             var html = '',
  1684.                 d = dp.getParsedDate(date),
  1685.                 i = 0;
  1686.  
  1687.             while(i < 12) {
  1688.                 html += this._getMonthHtml(new Date(d.year, i));
  1689.                 i++
  1690.             }
  1691.  
  1692.             return html;
  1693.         },
  1694.  
  1695.         _getMonthHtml: function (date) {
  1696.             var content = this._getCellContents(date, 'month');
  1697.  
  1698.             return '<div class="' + content.classes + '" data-month="' + date.getMonth() + '">' + content.html + '</div>'
  1699.         },
  1700.  
  1701.         _getYearsHtml: function (date) {
  1702.             var d = dp.getParsedDate(date),
  1703.                 decade = dp.getDecade(date),
  1704.                 firstYear = decade[0] - 1,
  1705.                 html = '',
  1706.                 i = firstYear;
  1707.  
  1708.             for (i; i <= decade[1] + 1; i++) {
  1709.                 html += this._getYearHtml(new Date(i , 0));
  1710.             }
  1711.  
  1712.             return html;
  1713.         },
  1714.  
  1715.         _getYearHtml: function (date) {
  1716.             var content = this._getCellContents(date, 'year');
  1717.  
  1718.             return '<div class="' + content.classes + '" data-year="' + date.getFullYear() + '">' + content.html + '</div>'
  1719.         },
  1720.  
  1721.         _renderTypes: {
  1722.             days: function () {
  1723.                 var dayNames = this._getDayNamesHtml(this.d.loc.firstDay),
  1724.                     days = this._getDaysHtml(this.d.currentDate);
  1725.  
  1726.                 this.$cells.html(days);
  1727.                 this.$names.html(dayNames)
  1728.             },
  1729.             months: function () {
  1730.                 var html = this._getMonthsHtml(this.d.currentDate);
  1731.  
  1732.                 this.$cells.html(html)
  1733.             },
  1734.             years: function () {
  1735.                 var html = this._getYearsHtml(this.d.currentDate);
  1736.  
  1737.                 this.$cells.html(html)
  1738.             }
  1739.         },
  1740.  
  1741.         _render: function () {
  1742.             if (this.opts.onlyTimepicker) return;
  1743.             this._renderTypes[this.type].bind(this)();
  1744.         },
  1745.  
  1746.         _update: function () {
  1747.             var $cells = $('.datepicker--cell', this.$cells),
  1748.                 _this = this,
  1749.                 classes,
  1750.                 $cell,
  1751.                 date;
  1752.             $cells.each(function (cell, i) {
  1753.                 $cell = $(this);
  1754.                 date = _this.d._getDateFromCell($(this));
  1755.                 classes = _this._getCellContents(date, _this.d.cellType);
  1756.                 $cell.attr('class',classes.classes)
  1757.             });
  1758.         },
  1759.  
  1760.         show: function () {
  1761.             if (this.opts.onlyTimepicker) return;
  1762.             this.$el.addClass('active');
  1763.             this.acitve = true;
  1764.         },
  1765.  
  1766.         hide: function () {
  1767.             this.$el.removeClass('active');
  1768.             this.active = false;
  1769.         },
  1770.  
  1771.         //  Events
  1772.         // -------------------------------------------------
  1773.  
  1774.         _handleClick: function (el) {
  1775.             var date = el.data('date') || 1,
  1776.                 month = el.data('month') || 0,
  1777.                 year = el.data('year') || this.d.parsedDate.year,
  1778.                 dp = this.d;
  1779.             // Change view if min view does not reach yet
  1780.             if (dp.view != this.opts.minView) {
  1781.                 dp.down(new Date(year, month, date));
  1782.                 return;
  1783.             }
  1784.             // Select date if min view is reached
  1785.             var selectedDate = new Date(year, month, date),
  1786.                 alreadySelected = this.d._isSelected(selectedDate, this.d.cellType);
  1787.  
  1788.             if (!alreadySelected) {
  1789.                 dp._trigger('clickCell', selectedDate);
  1790.                 return;
  1791.             }
  1792.  
  1793.             dp._handleAlreadySelectedDates.bind(dp, alreadySelected, selectedDate)();
  1794.  
  1795.         },
  1796.  
  1797.         _onClickCell: function (e) {
  1798.             var $el = $(e.target).closest('.datepicker--cell');
  1799.  
  1800.             if ($el.hasClass('-disabled-')) return;
  1801.  
  1802.             this._handleClick.bind(this)($el);
  1803.         }
  1804.     };
  1805. })();
  1806.  
  1807. ;(function () {
  1808.     var template = '' +
  1809.         '<div class="datepicker--nav-action" data-action="prev">#{prevHtml}</div>' +
  1810.         '<div class="datepicker--nav-title">#{title}</div>' +
  1811.         '<div class="datepicker--nav-action" data-action="next">#{nextHtml}</div>',
  1812.         buttonsContainerTemplate = '<div class="datepicker--buttons"></div>',
  1813.         button = '<span class="datepicker--button" data-action="#{action}">#{label}</span>',
  1814.         datepicker = $.fn.datepicker,
  1815.         dp = datepicker.Constructor;
  1816.  
  1817.     datepicker.Navigation = function (d, opts) {
  1818.         this.d = d;
  1819.         this.opts = opts;
  1820.  
  1821.         this.$buttonsContainer = '';
  1822.  
  1823.         this.init();
  1824.     };
  1825.  
  1826.     datepicker.Navigation.prototype = {
  1827.         init: function () {
  1828.             this._buildBaseHtml();
  1829.             this._bindEvents();
  1830.         },
  1831.  
  1832.         _bindEvents: function () {
  1833.             this.d.$nav.on('click', '.datepicker--nav-action', $.proxy(this._onClickNavButton, this));
  1834.             this.d.$nav.on('click', '.datepicker--nav-title', $.proxy(this._onClickNavTitle, this));
  1835.             this.d.$datepicker.on('click', '.datepicker--button', $.proxy(this._onClickNavButton, this));
  1836.         },
  1837.  
  1838.         _buildBaseHtml: function () {
  1839.             if (!this.opts.onlyTimepicker) {
  1840.                 this._render();
  1841.             }
  1842.             this._addButtonsIfNeed();
  1843.         },
  1844.  
  1845.         _addButtonsIfNeed: function () {
  1846.             if (this.opts.todayButton) {
  1847.                 this._addButton('today')
  1848.             }
  1849.             if (this.opts.clearButton) {
  1850.                 this._addButton('clear')
  1851.             }
  1852.         },
  1853.  
  1854.         _render: function () {
  1855.             var title = this._getTitle(this.d.currentDate),
  1856.                 html = dp.template(template, $.extend({title: title}, this.opts));
  1857.             this.d.$nav.html(html);
  1858.             if (this.d.view == 'years') {
  1859.                 $('.datepicker--nav-title', this.d.$nav).addClass('-disabled-');
  1860.             }
  1861.             this.setNavStatus();
  1862.         },
  1863.  
  1864.         _getTitle: function (date) {
  1865.             return this.d.formatDate(this.opts.navTitles[this.d.view], date)
  1866.         },
  1867.  
  1868.         _addButton: function (type) {
  1869.             if (!this.$buttonsContainer.length) {
  1870.                 this._addButtonsContainer();
  1871.             }
  1872.  
  1873.             var data = {
  1874.                     action: type,
  1875.                     label: this.d.loc[type]
  1876.                 },
  1877.                 html = dp.template(button, data);
  1878.  
  1879.             if ($('[data-action=' + type + ']', this.$buttonsContainer).length) return;
  1880.             this.$buttonsContainer.append(html);
  1881.         },
  1882.  
  1883.         _addButtonsContainer: function () {
  1884.             this.d.$datepicker.append(buttonsContainerTemplate);
  1885.             this.$buttonsContainer = $('.datepicker--buttons', this.d.$datepicker);
  1886.         },
  1887.  
  1888.         setNavStatus: function () {
  1889.             if (!(this.opts.minDate || this.opts.maxDate) || !this.opts.disableNavWhenOutOfRange) return;
  1890.  
  1891.             var date = this.d.parsedDate,
  1892.                 m = date.month,
  1893.                 y = date.year,
  1894.                 d = date.date;
  1895.  
  1896.             switch (this.d.view) {
  1897.                 case 'days':
  1898.                     if (!this.d._isInRange(new Date(y, m-1, 1), 'month')) {
  1899.                         this._disableNav('prev')
  1900.                     }
  1901.                     if (!this.d._isInRange(new Date(y, m+1, 1), 'month')) {
  1902.                         this._disableNav('next')
  1903.                     }
  1904.                     break;
  1905.                 case 'months':
  1906.                     if (!this.d._isInRange(new Date(y-1, m, d), 'year')) {
  1907.                         this._disableNav('prev')
  1908.                     }
  1909.                     if (!this.d._isInRange(new Date(y+1, m, d), 'year')) {
  1910.                         this._disableNav('next')
  1911.                     }
  1912.                     break;
  1913.                 case 'years':
  1914.                     var decade = dp.getDecade(this.d.date);
  1915.                     if (!this.d._isInRange(new Date(decade[0] - 1, 0, 1), 'year')) {
  1916.                         this._disableNav('prev')
  1917.                     }
  1918.                     if (!this.d._isInRange(new Date(decade[1] + 1, 0, 1), 'year')) {
  1919.                         this._disableNav('next')
  1920.                     }
  1921.                     break;
  1922.             }
  1923.         },
  1924.  
  1925.         _disableNav: function (nav) {
  1926.             $('[data-action="' + nav + '"]', this.d.$nav).addClass('-disabled-')
  1927.         },
  1928.  
  1929.         _activateNav: function (nav) {
  1930.             $('[data-action="' + nav + '"]', this.d.$nav).removeClass('-disabled-')
  1931.         },
  1932.  
  1933.         _onClickNavButton: function (e) {
  1934.             var $el = $(e.target).closest('[data-action]'),
  1935.                 action = $el.data('action');
  1936.  
  1937.             this.d[action]();
  1938.         },
  1939.  
  1940.         _onClickNavTitle: function (e) {
  1941.             if ($(e.target).hasClass('-disabled-')) return;
  1942.  
  1943.             if (this.d.view == 'days') {
  1944.                 return this.d.view = 'months'
  1945.             }
  1946.  
  1947.             this.d.view = 'years';
  1948.         }
  1949.     }
  1950.  
  1951. })();
  1952.  
  1953. ;(function () {
  1954.     var template = '<div class="datepicker--time">' +
  1955.         '<div class="datepicker--time-current">' +
  1956.         '   <span class="datepicker--time-current-hours">#{hourVisible}</span>' +
  1957.         '   <span class="datepicker--time-current-colon">:</span>' +
  1958.         '   <span class="datepicker--time-current-minutes">#{minValue}</span>' +
  1959.         '</div>' +
  1960.         '<div class="datepicker--time-sliders">' +
  1961.         '   <div class="datepicker--time-row">' +
  1962.         '      <input type="range" name="hours" value="#{hourValue}" min="#{hourMin}" max="#{hourMax}" step="#{hourStep}"/>' +
  1963.         '   </div>' +
  1964.         '   <div class="datepicker--time-row">' +
  1965.         '      <input type="range" name="minutes" value="#{minValue}" min="#{minMin}" max="#{minMax}" step="#{minStep}"/>' +
  1966.         '   </div>' +
  1967.         '</div>' +
  1968.         '</div>',
  1969.         datepicker = $.fn.datepicker,
  1970.         dp = datepicker.Constructor;
  1971.  
  1972.     datepicker.Timepicker = function (inst, opts) {
  1973.         this.d = inst;
  1974.         this.opts = opts;
  1975.  
  1976.         this.init();
  1977.     };
  1978.  
  1979.     datepicker.Timepicker.prototype = {
  1980.         init: function () {
  1981.             var input = 'input';
  1982.             this._setTime(this.d.date);
  1983.             this._buildHTML();
  1984.  
  1985.             if (navigator.userAgent.match(/trident/gi)) {
  1986.                 input = 'change';
  1987.             }
  1988.  
  1989.             this.d.$el.on('selectDate', this._onSelectDate.bind(this));
  1990.             this.$ranges.on(input, this._onChangeRange.bind(this));
  1991.             this.$ranges.on('mouseup', this._onMouseUpRange.bind(this));
  1992.             this.$ranges.on('mousemove focus ', this._onMouseEnterRange.bind(this));
  1993.             this.$ranges.on('mouseout blur', this._onMouseOutRange.bind(this));
  1994.         },
  1995.  
  1996.         _setTime: function (date) {
  1997.             var _date = dp.getParsedDate(date);
  1998.  
  1999.             this._handleDate(date);
  2000.             this.hours = _date.hours < this.minHours ? this.minHours : _date.hours;
  2001.             this.minutes = _date.minutes < this.minMinutes ? this.minMinutes : _date.minutes;
  2002.         },
  2003.  
  2004.         /**
  2005.          * Sets minHours and minMinutes from date (usually it's a minDate)
  2006.          * Also changes minMinutes if current hours are bigger then @date hours
  2007.          * @param date {Date}
  2008.          * @private
  2009.          */
  2010.         _setMinTimeFromDate: function (date) {
  2011.             this.minHours = date.getHours();
  2012.             this.minMinutes = date.getMinutes();
  2013.  
  2014.             // If, for example, min hours are 10, and current hours are 12,
  2015.             // update minMinutes to default value, to be able to choose whole range of values
  2016.             if (this.d.lastSelectedDate) {
  2017.                 if (this.d.lastSelectedDate.getHours() > date.getHours()) {
  2018.                     this.minMinutes = this.opts.minMinutes;
  2019.                 }
  2020.             }
  2021.         },
  2022.  
  2023.         _setMaxTimeFromDate: function (date) {
  2024.             this.maxHours = date.getHours();
  2025.             this.maxMinutes = date.getMinutes();
  2026.  
  2027.             if (this.d.lastSelectedDate) {
  2028.                 if (this.d.lastSelectedDate.getHours() < date.getHours()) {
  2029.                     this.maxMinutes = this.opts.maxMinutes;
  2030.                 }
  2031.             }
  2032.         },
  2033.  
  2034.         _setDefaultMinMaxTime: function () {
  2035.             var maxHours = 23,
  2036.                 maxMinutes = 59,
  2037.                 opts = this.opts;
  2038.  
  2039.             this.minHours = opts.minHours < 0 || opts.minHours > maxHours ? 0 : opts.minHours;
  2040.             this.minMinutes = opts.minMinutes < 0 || opts.minMinutes > maxMinutes ? 0 : opts.minMinutes;
  2041.             this.maxHours = opts.maxHours < 0 || opts.maxHours > maxHours ? maxHours : opts.maxHours;
  2042.             this.maxMinutes = opts.maxMinutes < 0 || opts.maxMinutes > maxMinutes ? maxMinutes : opts.maxMinutes;
  2043.         },
  2044.  
  2045.         /**
  2046.          * Looks for min/max hours/minutes and if current values
  2047.          * are out of range sets valid values.
  2048.          * @private
  2049.          */
  2050.         _validateHoursMinutes: function (date) {
  2051.             if (this.hours < this.minHours) {
  2052.                 this.hours = this.minHours;
  2053.             } else if (this.hours > this.maxHours) {
  2054.                 this.hours = this.maxHours;
  2055.             }
  2056.  
  2057.             if (this.minutes < this.minMinutes) {
  2058.                 this.minutes = this.minMinutes;
  2059.             } else if (this.minutes > this.maxMinutes) {
  2060.                 this.minutes = this.maxMinutes;
  2061.             }
  2062.         },
  2063.  
  2064.         _buildHTML: function () {
  2065.             var lz = dp.getLeadingZeroNum,
  2066.                 data = {
  2067.                     hourMin: this.minHours,
  2068.                     hourMax: lz(this.maxHours),
  2069.                     hourStep: this.opts.hoursStep,
  2070.                     hourValue: this.hours,
  2071.                     hourVisible: lz(this.displayHours),
  2072.                     minMin: this.minMinutes,
  2073.                     minMax: lz(this.maxMinutes),
  2074.                     minStep: this.opts.minutesStep,
  2075.                     minValue: lz(this.minutes)
  2076.                 },
  2077.                 _template = dp.template(template, data);
  2078.  
  2079.             this.$timepicker = $(_template).appendTo(this.d.$datepicker);
  2080.             this.$ranges = $('[type="range"]', this.$timepicker);
  2081.             this.$hours = $('[name="hours"]', this.$timepicker);
  2082.             this.$minutes = $('[name="minutes"]', this.$timepicker);
  2083.             this.$hoursText = $('.datepicker--time-current-hours', this.$timepicker);
  2084.             this.$minutesText = $('.datepicker--time-current-minutes', this.$timepicker);
  2085.  
  2086.             if (this.d.ampm) {
  2087.                 this.$ampm = $('<span class="datepicker--time-current-ampm">')
  2088.                     .appendTo($('.datepicker--time-current', this.$timepicker))
  2089.                     .html(this.dayPeriod);
  2090.  
  2091.                 this.$timepicker.addClass('-am-pm-');
  2092.             }
  2093.         },
  2094.  
  2095.         _updateCurrentTime: function () {
  2096.             var h =  dp.getLeadingZeroNum(this.displayHours),
  2097.                 m = dp.getLeadingZeroNum(this.minutes);
  2098.  
  2099.             this.$hoursText.html(h);
  2100.             this.$minutesText.html(m);
  2101.  
  2102.             if (this.d.ampm) {
  2103.                 this.$ampm.html(this.dayPeriod);
  2104.             }
  2105.         },
  2106.  
  2107.         _updateRanges: function () {
  2108.             this.$hours.attr({
  2109.                 min: this.minHours,
  2110.                 max: this.maxHours
  2111.             }).val(this.hours);
  2112.  
  2113.             this.$minutes.attr({
  2114.                 min: this.minMinutes,
  2115.                 max: this.maxMinutes
  2116.             }).val(this.minutes)
  2117.         },
  2118.  
  2119.         /**
  2120.          * Sets minHours, minMinutes etc. from date. If date is not passed, than sets
  2121.          * values from options
  2122.          * @param [date] {object} - Date object, to get values from
  2123.          * @private
  2124.          */
  2125.         _handleDate: function (date) {
  2126.             this._setDefaultMinMaxTime();
  2127.             if (date) {
  2128.                 if (dp.isSame(date, this.d.opts.minDate)) {
  2129.                     this._setMinTimeFromDate(this.d.opts.minDate);
  2130.                 } else if (dp.isSame(date, this.d.opts.maxDate)) {
  2131.                     this._setMaxTimeFromDate(this.d.opts.maxDate);
  2132.                 }
  2133.             }
  2134.  
  2135.             this._validateHoursMinutes(date);
  2136.         },
  2137.  
  2138.         update: function () {
  2139.             this._updateRanges();
  2140.             this._updateCurrentTime();
  2141.         },
  2142.  
  2143.         /**
  2144.          * Calculates valid hour value to display in text input and datepicker's body.
  2145.          * @param date {Date|Number} - date or hours
  2146.          * @param [ampm] {Boolean} - 12 hours mode
  2147.          * @returns {{hours: *, dayPeriod: string}}
  2148.          * @private
  2149.          */
  2150.         _getValidHoursFromDate: function (date, ampm) {
  2151.             var d = date,
  2152.                 hours = date;
  2153.  
  2154.             if (date instanceof Date) {
  2155.                 d = dp.getParsedDate(date);
  2156.                 hours = d.hours;
  2157.             }
  2158.  
  2159.             var _ampm = ampm || this.d.ampm,
  2160.                 dayPeriod = 'am';
  2161.  
  2162.             if (_ampm) {
  2163.                 switch(true) {
  2164.                     case hours == 0:
  2165.                         hours = 12;
  2166.                         break;
  2167.                     case hours == 12:
  2168.                         dayPeriod = 'pm';
  2169.                         break;
  2170.                     case hours > 11:
  2171.                         hours = hours - 12;
  2172.                         dayPeriod = 'pm';
  2173.                         break;
  2174.                     default:
  2175.                         break;
  2176.                 }
  2177.             }
  2178.  
  2179.             return {
  2180.                 hours: hours,
  2181.                 dayPeriod: dayPeriod
  2182.             }
  2183.         },
  2184.  
  2185.         set hours (val) {
  2186.             this._hours = val;
  2187.  
  2188.             var displayHours = this._getValidHoursFromDate(val);
  2189.  
  2190.             this.displayHours = displayHours.hours;
  2191.             this.dayPeriod = displayHours.dayPeriod;
  2192.         },
  2193.  
  2194.         get hours() {
  2195.             return this._hours;
  2196.         },
  2197.  
  2198.         //  Events
  2199.         // -------------------------------------------------
  2200.  
  2201.         _onChangeRange: function (e) {
  2202.             var $target = $(e.target),
  2203.                 name = $target.attr('name');
  2204.            
  2205.             this.d.timepickerIsActive = true;
  2206.  
  2207.             this[name] = $target.val();
  2208.             this._updateCurrentTime();
  2209.             this.d._trigger('timeChange', [this.hours, this.minutes]);
  2210.  
  2211.             this._handleDate(this.d.lastSelectedDate);
  2212.             this.update()
  2213.         },
  2214.  
  2215.         _onSelectDate: function (e, data) {
  2216.             this._handleDate(data);
  2217.             this.update();
  2218.         },
  2219.  
  2220.         _onMouseEnterRange: function (e) {
  2221.             var name = $(e.target).attr('name');
  2222.             $('.datepicker--time-current-' + name, this.$timepicker).addClass('-focus-');
  2223.         },
  2224.  
  2225.         _onMouseOutRange: function (e) {
  2226.             var name = $(e.target).attr('name');
  2227.             if (this.d.inFocus) return; // Prevent removing focus when mouse out of range slider
  2228.             $('.datepicker--time-current-' + name, this.$timepicker).removeClass('-focus-');
  2229.         },
  2230.  
  2231.         _onMouseUpRange: function (e) {
  2232.             this.d.timepickerIsActive = false;
  2233.         }
  2234.     };
  2235. })();
  2236.  })(window, jQuery);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement