PageSourceSearch

https://www.nmtl.gov.tw/js/airdatepicker/datepicker.min.js

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

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