PageSourceSearch

https://cinemaportuguesmemoriale.pt/DesktopModules/MVC/AlgoritmiaA…/air-datepicker/js/datepicker.js?cdv=615

js cinemaportuguesmemoriale.pt collected 2026-10-02 06:18:31 UTC 79,369 bytes, 2,247 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.