;(function (window, $, undefined) { ;(function () { var VERSION = '2.2.3', pluginName = 'datepicker', autoInitSelector = '.datepicker-here', $body, $datepickersContainer, containerBuilt = false, baseTemplate = '' + '
' + '' + '' + '
' + '
', defaults = { classes: '', inline: false, language: 'ru', startDate: new Date(), firstDay: '', weekends: [6, 0], dateFormat: '', altField: '', altFieldDateFormat: '@', toggleSelected: true, keyboardNav: true, position: 'bottom left', offset: 12, view: 'days', minView: 'days', showOtherMonths: true, selectOtherMonths: true, moveToOtherMonthsOnSelect: true, showOtherYears: true, selectOtherYears: true, moveToOtherYearsOnSelect: true, minDate: '', maxDate: '', disableNavWhenOutOfRange: true, multipleDates: false, // Boolean or Number multipleDatesSeparator: ',', range: false, todayButton: false, clearButton: false, showEvent: 'focus', autoClose: false, // navigation monthsField: 'monthsShort', prevHtml: '', nextHtml: '', navTitles: { days: 'MM, yyyy', months: 'yyyy', years: 'yyyy1 - yyyy2' }, // timepicker timepicker: false, onlyTimepicker: false, dateTimeSeparator: ' ', timeFormat: '', minHours: 0, maxHours: 24, minMinutes: 0, maxMinutes: 59, hoursStep: 1, minutesStep: 1, // events onSelect: '', onShow: '', onHide: '', onChangeMonth: '', onChangeYear: '', onChangeDecade: '', onChangeView: '', onRenderCell: '' }, hotKeys = { 'ctrlRight': [17, 39], 'ctrlUp': [17, 38], 'ctrlLeft': [17, 37], 'ctrlDown': [17, 40], 'shiftRight': [16, 39], 'shiftUp': [16, 38], 'shiftLeft': [16, 37], 'shiftDown': [16, 40], 'altUp': [18, 38], 'altRight': [18, 39], 'altLeft': [18, 37], 'altDown': [18, 40], 'ctrlShiftUp': [16, 17, 38] }, datepicker; var Datepicker = function (el, options) { this.el = el; this.$el = $(el); this.opts = $.extend(true, {}, defaults, options, this.$el.data()); if ($body == undefined) { $body = $('body'); } if (!this.opts.startDate) { this.opts.startDate = new Date(); } if (this.el.nodeName == 'INPUT') { this.elIsInput = true; } if (this.opts.altField) { this.$altField = typeof this.opts.altField == 'string' ? $(this.opts.altField) : this.opts.altField; } this.inited = false; this.visible = false; this.silent = false; // Need to prevent unnecessary rendering this.currentDate = this.opts.startDate; this.currentView = this.opts.view; this._createShortCuts(); this.selectedDates = []; this.views = {}; this.keys = []; this.minRange = ''; this.maxRange = ''; this._prevOnSelectValue = ''; this.init() }; datepicker = Datepicker; datepicker.prototype = { VERSION: VERSION, viewIndexes: ['days', 'months', 'years'], init: function () { if (!containerBuilt && !this.opts.inline && this.elIsInput) { this._buildDatepickersContainer(); } this._buildBaseHtml(); this._defineLocale(this.opts.language); this._syncWithMinMaxDates(); if (this.elIsInput) { if (!this.opts.inline) { // Set extra classes for proper transitions this._setPositionClasses(this.opts.position); this._bindEvents() } if (this.opts.keyboardNav && !this.opts.onlyTimepicker) { this._bindKeyboardEvents(); } this.$datepicker.on('mousedown', this._onMouseDownDatepicker.bind(this)); this.$datepicker.on('mouseup', this._onMouseUpDatepicker.bind(this)); } if (this.opts.classes) { this.$datepicker.addClass(this.opts.classes) } if (this.opts.timepicker) { this.timepicker = new $.fn.air_datepicker.Timepicker(this, this.opts); this._bindTimepickerEvents(); } if (this.opts.onlyTimepicker) { this.$datepicker.addClass('-only-timepicker-'); } this.views[this.currentView] = new $.fn.air_datepicker.Body(this, this.currentView, this.opts); this.views[this.currentView].show(); this.nav = new $.fn.air_datepicker.Navigation(this, this.opts); this.view = this.currentView; this.$el.on('clickCell.adp', this._onClickCell.bind(this)); this.$datepicker.on('mouseenter', '.datepicker--cell', this._onMouseEnterCell.bind(this)); this.$datepicker.on('mouseleave', '.datepicker--cell', this._onMouseLeaveCell.bind(this)); this.inited = true; }, _createShortCuts: function () { this.minDate = this.opts.minDate ? this.opts.minDate : new Date(-8639999913600000); this.maxDate = this.opts.maxDate ? this.opts.maxDate : new Date(8639999913600000); }, _bindEvents : function () { this.$el.on(this.opts.showEvent + '.adp', this._onShowEvent.bind(this)); this.$el.on('mouseup.adp', this._onMouseUpEl.bind(this)); this.$el.on('blur.adp', this._onBlur.bind(this)); this.$el.on('keyup.adp', this._onKeyUpGeneral.bind(this)); $(window).on('resize.adp', this._onResize.bind(this)); $('body').on('mouseup.adp', this._onMouseUpBody.bind(this)); }, _bindKeyboardEvents: function () { this.$el.on('keydown.adp', this._onKeyDown.bind(this)); this.$el.on('keyup.adp', this._onKeyUp.bind(this)); this.$el.on('hotKey.adp', this._onHotKey.bind(this)); }, _bindTimepickerEvents: function () { this.$el.on('timeChange.adp', this._onTimeChange.bind(this)); }, isWeekend: function (day) { return this.opts.weekends.indexOf(day) !== -1; }, _defineLocale: function (lang) { if (typeof lang == 'string') { this.loc = $.fn.air_datepicker.language[lang]; if (!this.loc) { console.warn('Can\'t find language "' + lang + '" in Datepicker.language, will use "ru" instead'); this.loc = $.extend(true, {}, $.fn.air_datepicker.language.ru) } this.loc = $.extend(true, {}, $.fn.air_datepicker.language.ru, $.fn.air_datepicker.language[lang]) } else { this.loc = $.extend(true, {}, $.fn.air_datepicker.language.ru, lang) } if (this.opts.dateFormat) { this.loc.dateFormat = this.opts.dateFormat } if (this.opts.timeFormat) { this.loc.timeFormat = this.opts.timeFormat } if (this.opts.firstDay !== '') { this.loc.firstDay = this.opts.firstDay } if (this.opts.timepicker) { this.loc.dateFormat = [this.loc.dateFormat, this.loc.timeFormat].join(this.opts.dateTimeSeparator); } if (this.opts.onlyTimepicker) { this.loc.dateFormat = this.loc.timeFormat; } var boundary = this._getWordBoundaryRegExp; if (this.loc.timeFormat.match(boundary('aa')) || this.loc.timeFormat.match(boundary('AA')) ) { this.ampm = true; } }, _buildDatepickersContainer: function () { containerBuilt = true; $body.append('
'); $datepickersContainer = $('#datepickers-container'); }, _buildBaseHtml: function () { var $appendTarget, $inline = $('
'); if(this.el.nodeName == 'INPUT') { if (!this.opts.inline) { $appendTarget = $datepickersContainer; } else { $appendTarget = $inline.insertAfter(this.$el) } } else { $appendTarget = $inline.appendTo(this.$el) } this.$datepicker = $(baseTemplate).appendTo($appendTarget); this.$content = $('.datepicker--content', this.$datepicker); this.$nav = $('.datepicker--nav', this.$datepicker); }, _triggerOnChange: function () { if (!this.selectedDates.length) { // Prevent from triggering multiple onSelect callback with same argument (empty string) in IE10-11 if (this._prevOnSelectValue === '') return; this._prevOnSelectValue = ''; return this.opts.onSelect('', '', this); } var selectedDates = this.selectedDates, parsedSelected = datepicker.getParsedDate(selectedDates[0]), formattedDates, _this = this, dates = new Date( parsedSelected.year, parsedSelected.month, parsedSelected.date, parsedSelected.hours, parsedSelected.minutes ); formattedDates = selectedDates.map(function (date) { return _this.formatDate(_this.loc.dateFormat, date) }).join(this.opts.multipleDatesSeparator); // Create new dates array, to separate it from original selectedDates if (this.opts.multipleDates || this.opts.range) { dates = selectedDates.map(function(date) { var parsedDate = datepicker.getParsedDate(date); return new Date( parsedDate.year, parsedDate.month, parsedDate.date, parsedDate.hours, parsedDate.minutes ); }) } this._prevOnSelectValue = formattedDates; this.opts.onSelect(formattedDates, dates, this); }, next: function () { var d = this.parsedDate, o = this.opts; switch (this.view) { case 'days': this.date = new Date(d.year, d.month + 1, 1); if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year); break; case 'months': this.date = new Date(d.year + 1, d.month, 1); if (o.onChangeYear) o.onChangeYear(this.parsedDate.year); break; case 'years': this.date = new Date(d.year + 10, 0, 1); if (o.onChangeDecade) o.onChangeDecade(this.curDecade); break; } }, prev: function () { var d = this.parsedDate, o = this.opts; switch (this.view) { case 'days': this.date = new Date(d.year, d.month - 1, 1); if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year); break; case 'months': this.date = new Date(d.year - 1, d.month, 1); if (o.onChangeYear) o.onChangeYear(this.parsedDate.year); break; case 'years': this.date = new Date(d.year - 10, 0, 1); if (o.onChangeDecade) o.onChangeDecade(this.curDecade); break; } }, formatDate: function (string, date) { date = date || this.date; var result = string, boundary = this._getWordBoundaryRegExp, locale = this.loc, leadingZero = datepicker.getLeadingZeroNum, decade = datepicker.getDecade(date), d = datepicker.getParsedDate(date), fullHours = d.fullHours, hours = d.hours, ampm = string.match(boundary('aa')) || string.match(boundary('AA')), dayPeriod = 'am', replacer = this._replacer, validHours; if (this.opts.timepicker && this.timepicker && ampm) { validHours = this.timepicker._getValidHoursFromDate(date, ampm); fullHours = leadingZero(validHours.hours); hours = validHours.hours; dayPeriod = validHours.dayPeriod; } switch (true) { case /@/.test(result): result = result.replace(/@/, date.getTime()); case /aa/.test(result): result = replacer(result, boundary('aa'), dayPeriod); case /AA/.test(result): result = replacer(result, boundary('AA'), dayPeriod.toUpperCase()); case /dd/.test(result): result = replacer(result, boundary('dd'), d.fullDate); case /d/.test(result): result = replacer(result, boundary('d'), d.date); case /DD/.test(result): result = replacer(result, boundary('DD'), locale.days[d.day]); case /D/.test(result): result = replacer(result, boundary('D'), locale.daysShort[d.day]); case /mm/.test(result): result = replacer(result, boundary('mm'), d.fullMonth); case /m/.test(result): result = replacer(result, boundary('m'), d.month + 1); case /MM/.test(result): result = replacer(result, boundary('MM'), this.loc.months[d.month]); case /M/.test(result): result = replacer(result, boundary('M'), locale.monthsShort[d.month]); case /ii/.test(result): result = replacer(result, boundary('ii'), d.fullMinutes); case /i/.test(result): result = replacer(result, boundary('i'), d.minutes); case /hh/.test(result): result = replacer(result, boundary('hh'), fullHours); case /h/.test(result): result = replacer(result, boundary('h'), hours); case /yyyy/.test(result): result = replacer(result, boundary('yyyy'), d.year); case /yyyy1/.test(result): result = replacer(result, boundary('yyyy1'), decade[0]); case /yyyy2/.test(result): result = replacer(result, boundary('yyyy2'), decade[1]); case /yy/.test(result): result = replacer(result, boundary('yy'), d.year.toString().slice(-2)); } return result; }, _replacer: function (str, reg, data) { return str.replace(reg, function (match, p1,p2,p3) { return p1 + data + p3; }) }, _getWordBoundaryRegExp: function (sign) { var symbols = '\\s|\\.|-|/|\\\\|,|\\$|\\!|\\?|:|;'; return new RegExp('(^|>|' + symbols + ')(' + sign + ')($|<|' + symbols + ')', 'g'); }, selectDate: function (date) { var _this = this, opts = _this.opts, d = _this.parsedDate, selectedDates = _this.selectedDates, len = selectedDates.length, newDate = ''; if (Array.isArray(date)) { date.forEach(function (d) { _this.selectDate(d) }); return; } if (!(date instanceof Date)) return; this.lastSelectedDate = date; // Set new time values from Date if (this.timepicker) { this.timepicker._setTime(date); } // On this step timepicker will set valid values in it's instance _this._trigger('selectDate', date); // Set correct time values after timepicker's validation // Prevent from setting hours or minutes which values are lesser then `min` value or // greater then `max` value if (this.timepicker) { date.setHours(this.timepicker.hours); date.setMinutes(this.timepicker.minutes) } if (_this.view == 'days') { if (date.getMonth() != d.month && opts.moveToOtherMonthsOnSelect) { newDate = new Date(date.getFullYear(), date.getMonth(), 1); } } if (_this.view == 'years') { if (date.getFullYear() != d.year && opts.moveToOtherYearsOnSelect) { newDate = new Date(date.getFullYear(), 0, 1); } } if (newDate) { _this.silent = true; _this.date = newDate; _this.silent = false; _this.nav._render() } if (opts.multipleDates && !opts.range) { // Set priority to range functionality if (len === opts.multipleDates) return; if (!_this._isSelected(date)) { _this.selectedDates.push(date); } } else if (opts.range) { if (len == 2) { _this.selectedDates = [date]; _this.minRange = date; _this.maxRange = ''; } else if (len == 1) { _this.selectedDates.push(date); if (!_this.maxRange){ _this.maxRange = date; } else { _this.minRange = date; } // Swap dates if they were selected via dp.selectDate() and second date was smaller then first if (datepicker.bigger(_this.maxRange, _this.minRange)) { _this.maxRange = _this.minRange; _this.minRange = date; } _this.selectedDates = [_this.minRange, _this.maxRange] } else { _this.selectedDates = [date]; _this.minRange = date; } } else { _this.selectedDates = [date]; } _this._setInputValue(); if (opts.onSelect) { _this._triggerOnChange(); } if (opts.autoClose && !this.timepickerIsActive) { if (!opts.multipleDates && !opts.range) { _this.hide(); } else if (opts.range && _this.selectedDates.length == 2) { _this.hide(); } } _this.views[this.currentView]._render() }, removeDate: function (date) { var selected = this.selectedDates, _this = this; if (!(date instanceof Date)) return; return selected.some(function (curDate, i) { if (datepicker.isSame(curDate, date)) { selected.splice(i, 1); if (!_this.selectedDates.length) { _this.minRange = ''; _this.maxRange = ''; _this.lastSelectedDate = ''; } else { _this.lastSelectedDate = _this.selectedDates[_this.selectedDates.length - 1]; } _this.views[_this.currentView]._render(); _this._setInputValue(); if (_this.opts.onSelect) { _this._triggerOnChange(); } return true } }) }, today: function () { this.silent = true; this.view = this.opts.minView; this.silent = false; this.date = new Date(); if (this.opts.todayButton instanceof Date) { this.selectDate(this.opts.todayButton) } }, clear: function () { this.selectedDates = []; this.minRange = ''; this.maxRange = ''; this.views[this.currentView]._render(); this._setInputValue(); if (this.opts.onSelect) { this._triggerOnChange() } }, /** * Updates datepicker options * @param {String|Object} param - parameter's name to update. If object then it will extend current options * @param {String|Number|Object} [value] - new param value */ update: function (param, value) { var len = arguments.length, lastSelectedDate = this.lastSelectedDate; if (len == 2) { this.opts[param] = value; } else if (len == 1 && typeof param == 'object') { this.opts = $.extend(true, this.opts, param) } this._createShortCuts(); this._syncWithMinMaxDates(); this._defineLocale(this.opts.language); this.nav._addButtonsIfNeed(); if (!this.opts.onlyTimepicker) this.nav._render(); this.views[this.currentView]._render(); if (this.elIsInput && !this.opts.inline) { this._setPositionClasses(this.opts.position); if (this.visible) { this.setPosition(this.opts.position) } } if (this.opts.classes) { this.$datepicker.addClass(this.opts.classes) } if (this.opts.onlyTimepicker) { this.$datepicker.addClass('-only-timepicker-'); } if (this.opts.timepicker) { if (lastSelectedDate) this.timepicker._handleDate(lastSelectedDate); this.timepicker._updateRanges(); this.timepicker._updateCurrentTime(); // Change hours and minutes if it's values have been changed through min/max hours/minutes if (lastSelectedDate) { lastSelectedDate.setHours(this.timepicker.hours); lastSelectedDate.setMinutes(this.timepicker.minutes); } } this._setInputValue(); return this; }, _syncWithMinMaxDates: function () { var curTime = this.date.getTime(); this.silent = true; if (this.minTime > curTime) { this.date = this.minDate; } if (this.maxTime < curTime) { this.date = this.maxDate; } this.silent = false; }, _isSelected: function (checkDate, cellType) { var res = false; this.selectedDates.some(function (date) { if (datepicker.isSame(date, checkDate, cellType)) { res = date; return true; } }); return res; }, _setInputValue: function () { var _this = this, opts = _this.opts, format = _this.loc.dateFormat, altFormat = opts.altFieldDateFormat, value = _this.selectedDates.map(function (date) { return _this.formatDate(format, date) }), altValues; if (opts.altField && _this.$altField.length) { altValues = this.selectedDates.map(function (date) { return _this.formatDate(altFormat, date) }); altValues = altValues.join(this.opts.multipleDatesSeparator); this.$altField.val(altValues); } value = value.join(this.opts.multipleDatesSeparator); this.$el.val(value) }, /** * Check if date is between minDate and maxDate * @param date {object} - date object * @param type {string} - cell type * @returns {boolean} * @private */ _isInRange: function (date, type) { var time = date.getTime(), d = datepicker.getParsedDate(date), min = datepicker.getParsedDate(this.minDate), max = datepicker.getParsedDate(this.maxDate), dMinTime = new Date(d.year, d.month, min.date).getTime(), dMaxTime = new Date(d.year, d.month, max.date).getTime(), types = { day: time >= this.minTime && time <= this.maxTime, month: dMinTime >= this.minTime && dMaxTime <= this.maxTime, year: d.year >= min.year && d.year <= max.year }; return type ? types[type] : types.day }, _getDimensions: function ($el) { var offset = $el.offset(); return { width: $el.outerWidth(), height: $el.outerHeight(), left: offset.left, top: offset.top } }, _getDateFromCell: function (cell) { var curDate = this.parsedDate, year = cell.data('year') || curDate.year, month = cell.data('month') == undefined ? curDate.month : cell.data('month'), date = cell.data('date') || 1; return new Date(year, month, date); }, _setPositionClasses: function (pos) { pos = pos.split(' '); var main = pos[0], sec = pos[1], classes = 'datepicker -' + main + '-' + sec + '- -from-' + main + '-'; if (this.visible) classes += ' active'; this.$datepicker .removeAttr('class') .addClass(classes); }, setPosition: function (position) { position = position || this.opts.position; var dims = this._getDimensions(this.$el), selfDims = this._getDimensions(this.$datepicker), pos = position.split(' '), top, left, offset = this.opts.offset, main = pos[0], secondary = pos[1]; switch (main) { case 'top': top = dims.top - selfDims.height - offset; break; case 'right': left = dims.left + dims.width + offset; break; case 'bottom': top = dims.top + dims.height + offset; break; case 'left': left = dims.left - selfDims.width - offset; break; } switch(secondary) { case 'top': top = dims.top; break; case 'right': left = dims.left + dims.width - selfDims.width; break; case 'bottom': top = dims.top + dims.height - selfDims.height; break; case 'left': left = dims.left; break; case 'center': if (/left|right/.test(main)) { top = dims.top + dims.height/2 - selfDims.height/2; } else { left = dims.left + dims.width/2 - selfDims.width/2; } } this.$datepicker .css({ left: left, top: top }) }, show: function () { var onShow = this.opts.onShow; this.setPosition(this.opts.position); this.$datepicker.addClass('active'); this.visible = true; if (onShow) { this._bindVisionEvents(onShow) } }, hide: function () { var onHide = this.opts.onHide; this.$datepicker .removeClass('active') .css({ left: '-100000px' }); this.focused = ''; this.keys = []; this.inFocus = false; this.visible = false; this.$el.blur(); if (onHide) { this._bindVisionEvents(onHide) } }, down: function (date) { this._changeView(date, 'down'); }, up: function (date) { this._changeView(date, 'up'); }, _bindVisionEvents: function (event) { this.$datepicker.off('transitionend.dp'); event(this, false); this.$datepicker.one('transitionend.dp', event.bind(this, this, true)) }, _changeView: function (date, dir) { date = date || this.focused || this.date; var nextView = dir == 'up' ? this.viewIndex + 1 : this.viewIndex - 1; if (nextView > 2) nextView = 2; if (nextView < 0) nextView = 0; this.silent = true; this.date = new Date(date.getFullYear(), date.getMonth(), 1); this.silent = false; this.view = this.viewIndexes[nextView]; }, _handleHotKey: function (key) { var date = datepicker.getParsedDate(this._getFocusedDate()), focusedParsed, o = this.opts, newDate, totalDaysInNextMonth, monthChanged = false, yearChanged = false, decadeChanged = false, y = date.year, m = date.month, d = date.date; switch (key) { case 'ctrlRight': case 'ctrlUp': m += 1; monthChanged = true; break; case 'ctrlLeft': case 'ctrlDown': m -= 1; monthChanged = true; break; case 'shiftRight': case 'shiftUp': yearChanged = true; y += 1; break; case 'shiftLeft': case 'shiftDown': yearChanged = true; y -= 1; break; case 'altRight': case 'altUp': decadeChanged = true; y += 10; break; case 'altLeft': case 'altDown': decadeChanged = true; y -= 10; break; case 'ctrlShiftUp': this.up(); break; } totalDaysInNextMonth = datepicker.getDaysCount(new Date(y,m)); newDate = new Date(y,m,d); // If next month has less days than current, set date to total days in that month if (totalDaysInNextMonth < d) d = totalDaysInNextMonth; // Check if newDate is in valid range if (newDate.getTime() < this.minTime) { newDate = this.minDate; } else if (newDate.getTime() > this.maxTime) { newDate = this.maxDate; } this.focused = newDate; focusedParsed = datepicker.getParsedDate(newDate); if (monthChanged && o.onChangeMonth) { o.onChangeMonth(focusedParsed.month, focusedParsed.year) } if (yearChanged && o.onChangeYear) { o.onChangeYear(focusedParsed.year) } if (decadeChanged && o.onChangeDecade) { o.onChangeDecade(this.curDecade) } }, _registerKey: function (key) { var exists = this.keys.some(function (curKey) { return curKey == key; }); if (!exists) { this.keys.push(key) } }, _unRegisterKey: function (key) { var index = this.keys.indexOf(key); this.keys.splice(index, 1); }, _isHotKeyPressed: function () { var currentHotKey, found = false, _this = this, pressedKeys = this.keys.sort(); for (var hotKey in hotKeys) { currentHotKey = hotKeys[hotKey]; if (pressedKeys.length != currentHotKey.length) continue; if (currentHotKey.every(function (key, i) { return key == pressedKeys[i]})) { _this._trigger('hotKey', hotKey); found = true; } } return found; }, _trigger: function (event, args) { this.$el.trigger(event, args) }, _focusNextCell: function (keyCode, type) { type = type || this.cellType; var date = datepicker.getParsedDate(this._getFocusedDate()), y = date.year, m = date.month, d = date.date; if (this._isHotKeyPressed()){ return; } switch(keyCode) { case 37: // left type == 'day' ? (d -= 1) : ''; type == 'month' ? (m -= 1) : ''; type == 'year' ? (y -= 1) : ''; break; case 38: // up type == 'day' ? (d -= 7) : ''; type == 'month' ? (m -= 3) : ''; type == 'year' ? (y -= 4) : ''; break; case 39: // right type == 'day' ? (d += 1) : ''; type == 'month' ? (m += 1) : ''; type == 'year' ? (y += 1) : ''; break; case 40: // down type == 'day' ? (d += 7) : ''; type == 'month' ? (m += 3) : ''; type == 'year' ? (y += 4) : ''; break; } var nd = new Date(y,m,d); if (nd.getTime() < this.minTime) { nd = this.minDate; } else if (nd.getTime() > this.maxTime) { nd = this.maxDate; } this.focused = nd; }, _getFocusedDate: function () { var focused = this.focused || this.selectedDates[this.selectedDates.length - 1], d = this.parsedDate; if (!focused) { switch (this.view) { case 'days': focused = new Date(d.year, d.month, new Date().getDate()); break; case 'months': focused = new Date(d.year, d.month, 1); break; case 'years': focused = new Date(d.year, 0, 1); break; } } return focused; }, _getCell: function (date, type) { type = type || this.cellType; var d = datepicker.getParsedDate(date), selector = '.datepicker--cell[data-year="' + d.year + '"]', $cell; switch (type) { case 'month': selector = '[data-month="' + d.month + '"]'; break; case 'day': selector += '[data-month="' + d.month + '"][data-date="' + d.date + '"]'; break; } $cell = this.views[this.currentView].$el.find(selector); return $cell.length ? $cell : $(''); }, destroy: function () { var _this = this; _this.$el .off('.adp') .data('datepicker', ''); _this.selectedDates = []; _this.focused = ''; _this.views = {}; _this.keys = []; _this.minRange = ''; _this.maxRange = ''; if (_this.opts.inline || !_this.elIsInput) { _this.$datepicker.closest('.datepicker-inline').remove(); } else { _this.$datepicker.remove(); } }, _handleAlreadySelectedDates: function (alreadySelected, selectedDate) { if (this.opts.range) { if (!this.opts.toggleSelected) { // Add possibility to select same date when range is true if (this.selectedDates.length != 2) { this._trigger('clickCell', selectedDate); } } else { this.removeDate(selectedDate); } } else if (this.opts.toggleSelected){ this.removeDate(selectedDate); } // Change last selected date to be able to change time when clicking on this cell if (!this.opts.toggleSelected) { this.lastSelectedDate = alreadySelected; if (this.opts.timepicker) { this.timepicker._setTime(alreadySelected); this.timepicker.update(); } } }, _onShowEvent: function (e) { if (!this.visible) { this.show(); } }, _onBlur: function () { if (!this.inFocus && this.visible) { this.hide(); } }, _onMouseDownDatepicker: function (e) { this.inFocus = true; }, _onMouseUpDatepicker: function (e) { this.inFocus = false; e.originalEvent.inFocus = true; if (!e.originalEvent.timepickerFocus) this.$el.focus(); }, _onKeyUpGeneral: function (e) { var val = this.$el.val(); if (!val) { this.clear(); } }, _onResize: function () { if (this.visible) { this.setPosition(); } }, _onMouseUpBody: function (e) { if (e.originalEvent.inFocus) return; if (this.visible && !this.inFocus) { this.hide(); } }, _onMouseUpEl: function (e) { e.originalEvent.inFocus = true; setTimeout(this._onKeyUpGeneral.bind(this),4); }, _onKeyDown: function (e) { var code = e.which; this._registerKey(code); // Arrows if (code >= 37 && code <= 40) { e.preventDefault(); this._focusNextCell(code); } // Enter if (code == 13) { if (this.focused) { if (this._getCell(this.focused).hasClass('-disabled-')) return; if (this.view != this.opts.minView) { this.down() } else { var alreadySelected = this._isSelected(this.focused, this.cellType); if (!alreadySelected) { if (this.timepicker) { this.focused.setHours(this.timepicker.hours); this.focused.setMinutes(this.timepicker.minutes); } this.selectDate(this.focused); return; } this._handleAlreadySelectedDates(alreadySelected, this.focused) } } } // Esc if (code == 27) { this.hide(); } }, _onKeyUp: function (e) { var code = e.which; this._unRegisterKey(code); }, _onHotKey: function (e, hotKey) { this._handleHotKey(hotKey); }, _onMouseEnterCell: function (e) { var $cell = $(e.target).closest('.datepicker--cell'), date = this._getDateFromCell($cell); // Prevent from unnecessary rendering and setting new currentDate this.silent = true; if (this.focused) { this.focused = '' } $cell.addClass('-focus-'); this.focused = date; this.silent = false; if (this.opts.range && this.selectedDates.length == 1) { this.minRange = this.selectedDates[0]; this.maxRange = ''; if (datepicker.less(this.minRange, this.focused)) { this.maxRange = this.minRange; this.minRange = ''; } this.views[this.currentView]._update(); } }, _onMouseLeaveCell: function (e) { var $cell = $(e.target).closest('.datepicker--cell'); $cell.removeClass('-focus-'); this.silent = true; this.focused = ''; this.silent = false; }, _onTimeChange: function (e, h, m) { var date = new Date(), selectedDates = this.selectedDates, selected = false; if (selectedDates.length) { selected = true; date = this.lastSelectedDate; } date.setHours(h); date.setMinutes(m); if (!selected && !this._getCell(date).hasClass('-disabled-')) { this.selectDate(date); } else { this._setInputValue(); if (this.opts.onSelect) { this._triggerOnChange(); } } }, _onClickCell: function (e, date) { if (this.timepicker) { date.setHours(this.timepicker.hours); date.setMinutes(this.timepicker.minutes); } this.selectDate(date); }, set focused(val) { if (!val && this.focused) { var $cell = this._getCell(this.focused); if ($cell.length) { $cell.removeClass('-focus-') } } this._focused = val; if (this.opts.range && this.selectedDates.length == 1) { this.minRange = this.selectedDates[0]; this.maxRange = ''; if (datepicker.less(this.minRange, this._focused)) { this.maxRange = this.minRange; this.minRange = ''; } } if (this.silent) return; this.date = val; }, get focused() { return this._focused; }, get parsedDate() { return datepicker.getParsedDate(this.date); }, set date (val) { if (!(val instanceof Date)) return; this.currentDate = val; if (this.inited && !this.silent) { this.views[this.view]._render(); this.nav._render(); if (this.visible && this.elIsInput) { this.setPosition(); } } return val; }, get date () { return this.currentDate }, set view (val) { this.viewIndex = this.viewIndexes.indexOf(val); if (this.viewIndex < 0) { return; } this.prevView = this.currentView; this.currentView = val; if (this.inited) { if (!this.views[val]) { this.views[val] = new $.fn.air_datepicker.Body(this, val, this.opts) } else { this.views[val]._render(); } this.views[this.prevView].hide(); this.views[val].show(); this.nav._render(); if (this.opts.onChangeView) { this.opts.onChangeView(val) } if (this.elIsInput && this.visible) this.setPosition(); } return val }, get view() { return this.currentView; }, get cellType() { return this.view.substring(0, this.view.length - 1) }, get minTime() { var min = datepicker.getParsedDate(this.minDate); return new Date(min.year, min.month, min.date).getTime() }, get maxTime() { var max = datepicker.getParsedDate(this.maxDate); return new Date(max.year, max.month, max.date).getTime() }, get curDecade() { return datepicker.getDecade(this.date) } }; // Utils // ------------------------------------------------- datepicker.getDaysCount = function (date) { return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); }; datepicker.getParsedDate = function (date) { return { year: date.getFullYear(), month: date.getMonth(), fullMonth: (date.getMonth() + 1) < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1, // One based date: date.getDate(), fullDate: date.getDate() < 10 ? '0' + date.getDate() : date.getDate(), day: date.getDay(), hours: date.getHours(), fullHours: date.getHours() < 10 ? '0' + date.getHours() : date.getHours() , minutes: date.getMinutes(), fullMinutes: date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() } }; datepicker.getDecade = function (date) { var firstYear = Math.floor(date.getFullYear() / 10) * 10; return [firstYear, firstYear + 9]; }; datepicker.template = function (str, data) { return str.replace(/#\{([\w]+)\}/g, function (source, match) { if (data[match] || data[match] === 0) { return data[match] } }); }; datepicker.isSame = function (date1, date2, type) { if (!date1 || !date2) return false; var d1 = datepicker.getParsedDate(date1), d2 = datepicker.getParsedDate(date2), _type = type ? type : 'day', conditions = { day: d1.date == d2.date && d1.month == d2.month && d1.year == d2.year, month: d1.month == d2.month && d1.year == d2.year, year: d1.year == d2.year }; return conditions[_type]; }; datepicker.less = function (dateCompareTo, date, type) { if (!dateCompareTo || !date) return false; return date.getTime() < dateCompareTo.getTime(); }; datepicker.bigger = function (dateCompareTo, date, type) { if (!dateCompareTo || !date) return false; return date.getTime() > dateCompareTo.getTime(); }; datepicker.getLeadingZeroNum = function (num) { return parseInt(num) < 10 ? '0' + num : num; }; /** * Returns copy of date with hours and minutes equals to 0 * @param date {Date} */ datepicker.resetTime = function (date) { if (typeof date != 'object') return; date = datepicker.getParsedDate(date); return new Date(date.year, date.month, date.date) }; $.fn.air_datepicker = function ( options ) { return this.each(function () { if (!$.data(this, pluginName)) { $.data(this, pluginName, new Datepicker( this, options )); } else { var _this = $.data(this, pluginName); _this.opts = $.extend(true, _this.opts, options); _this.update(); } }); }; $.fn.air_datepicker.Constructor = Datepicker; $.fn.air_datepicker.language = { ru: { days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'], daysShort: ['Вос','Пон','Вто','Сре','Чет','Пят','Суб'], daysMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'], months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'], monthsShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'], today: 'Сегодня', clear: 'Очистить', dateFormat: 'dd.mm.yyyy', timeFormat: 'hh:ii', firstDay: 1 } }; $(function () { $(autoInitSelector).air_datepicker(); }) })(); ;(function () { var templates = { days:'' + '
' + '
' + '
' + '
', months: '' + '
' + '
' + '
', years: '' + '
' + '
' + '
' }, datepicker = $.fn.air_datepicker, dp = datepicker.Constructor; datepicker.Body = function (d, type, opts) { this.d = d; this.type = type; this.opts = opts; this.$el = $(''); if (this.opts.onlyTimepicker) return; this.init(); }; datepicker.Body.prototype = { init: function () { this._buildBaseHtml(); this._render(); this._bindEvents(); }, _bindEvents: function () { this.$el.on('click', '.datepicker--cell', $.proxy(this._onClickCell, this)); }, _buildBaseHtml: function () { this.$el = $(templates[this.type]).appendTo(this.d.$content); this.$names = $('.datepicker--days-names', this.$el); this.$cells = $('.datepicker--cells', this.$el); }, _getDayNamesHtml: function (firstDay, curDay, html, i) { curDay = curDay != undefined ? curDay : firstDay; html = html ? html : ''; i = i != undefined ? i : 0; if (i > 7) return html; if (curDay == 7) return this._getDayNamesHtml(firstDay, 0, html, ++i); html += '
' + this.d.loc.daysMin[curDay] + '
'; return this._getDayNamesHtml(firstDay, ++curDay, html, ++i); }, _getCellContents: function (date, type) { var classes = "datepicker--cell datepicker--cell-" + type, currentDate = new Date(), parent = this.d, minRange = dp.resetTime(parent.minRange), maxRange = dp.resetTime(parent.maxRange), opts = parent.opts, d = dp.getParsedDate(date), render = {}, html = d.date; switch (type) { case 'day': if (parent.isWeekend(d.day)) classes += " -weekend-"; if (d.month != this.d.parsedDate.month) { classes += " -other-month-"; if (!opts.selectOtherMonths) { classes += " -disabled-"; } if (!opts.showOtherMonths) html = ''; } break; case 'month': html = parent.loc[parent.opts.monthsField][d.month]; break; case 'year': var decade = parent.curDecade; html = d.year; if (d.year < decade[0] || d.year > decade[1]) { classes += ' -other-decade-'; if (!opts.selectOtherYears) { classes += " -disabled-"; } if (!opts.showOtherYears) html = ''; } break; } if (opts.onRenderCell) { render = opts.onRenderCell(date, type) || {}; html = render.html ? render.html : html; classes += render.classes ? ' ' + render.classes : ''; } if (opts.range) { if (dp.isSame(minRange, date, type)) classes += ' -range-from-'; if (dp.isSame(maxRange, date, type)) classes += ' -range-to-'; if (parent.selectedDates.length == 1 && parent.focused) { if ( (dp.bigger(minRange, date) && dp.less(parent.focused, date)) || (dp.less(maxRange, date) && dp.bigger(parent.focused, date))) { classes += ' -in-range-' } if (dp.less(maxRange, date) && dp.isSame(parent.focused, date)) { classes += ' -range-from-' } if (dp.bigger(minRange, date) && dp.isSame(parent.focused, date)) { classes += ' -range-to-' } } else if (parent.selectedDates.length == 2) { if (dp.bigger(minRange, date) && dp.less(maxRange, date)) { classes += ' -in-range-' } } } if (dp.isSame(currentDate, date, type)) classes += ' -current-'; if (parent.focused && dp.isSame(date, parent.focused, type)) classes += ' -focus-'; if (parent._isSelected(date, type)) classes += ' -selected-'; if (!parent._isInRange(date, type) || render.disabled) classes += ' -disabled-'; return { html: html, classes: classes } }, /** * Calculates days number to render. Generates days html and returns it. * @param {object} date - Date object * @returns {string} * @private */ _getDaysHtml: function (date) { var totalMonthDays = dp.getDaysCount(date), firstMonthDay = new Date(date.getFullYear(), date.getMonth(), 1).getDay(), lastMonthDay = new Date(date.getFullYear(), date.getMonth(), totalMonthDays).getDay(), daysFromPevMonth = firstMonthDay - this.d.loc.firstDay, daysFromNextMonth = 6 - lastMonthDay + this.d.loc.firstDay; daysFromPevMonth = daysFromPevMonth < 0 ? daysFromPevMonth + 7 : daysFromPevMonth; daysFromNextMonth = daysFromNextMonth > 6 ? daysFromNextMonth - 7 : daysFromNextMonth; var startDayIndex = -daysFromPevMonth + 1, m, y, html = ''; for (var i = startDayIndex, max = totalMonthDays + daysFromNextMonth; i <= max; i++) { y = date.getFullYear(); m = date.getMonth(); html += this._getDayHtml(new Date(y, m, i)) } return html; }, _getDayHtml: function (date) { var content = this._getCellContents(date, 'day'); return '
' + content.html + '
'; }, /** * Generates months html * @param {object} date - date instance * @returns {string} * @private */ _getMonthsHtml: function (date) { var html = '', d = dp.getParsedDate(date), i = 0; while(i < 12) { html += this._getMonthHtml(new Date(d.year, i)); i++ } return html; }, _getMonthHtml: function (date) { var content = this._getCellContents(date, 'month'); return '
' + content.html + '
' }, _getYearsHtml: function (date) { var d = dp.getParsedDate(date), decade = dp.getDecade(date), firstYear = decade[0] - 1, html = '', i = firstYear; for (i; i <= decade[1] + 1; i++) { html += this._getYearHtml(new Date(i , 0)); } return html; }, _getYearHtml: function (date) { var content = this._getCellContents(date, 'year'); return '
' + content.html + '
' }, _renderTypes: { days: function () { var dayNames = this._getDayNamesHtml(this.d.loc.firstDay), days = this._getDaysHtml(this.d.currentDate); this.$cells.html(days); this.$names.html(dayNames) }, months: function () { var html = this._getMonthsHtml(this.d.currentDate); this.$cells.html(html) }, years: function () { var html = this._getYearsHtml(this.d.currentDate); this.$cells.html(html) } }, _render: function () { if (this.opts.onlyTimepicker) return; this._renderTypes[this.type].bind(this)(); }, _update: function () { var $cells = $('.datepicker--cell', this.$cells), _this = this, classes, $cell, date; $cells.each(function (cell, i) { $cell = $(this); date = _this.d._getDateFromCell($(this)); classes = _this._getCellContents(date, _this.d.cellType); $cell.attr('class',classes.classes) }); }, show: function () { if (this.opts.onlyTimepicker) return; this.$el.addClass('active'); this.acitve = true; }, hide: function () { this.$el.removeClass('active'); this.active = false; }, // Events // ------------------------------------------------- _handleClick: function (el) { var date = el.data('date') || 1, month = el.data('month') || 0, year = el.data('year') || this.d.parsedDate.year, dp = this.d; // Change view if min view does not reach yet if (dp.view != this.opts.minView) { dp.down(new Date(year, month, date)); return; } // Select date if min view is reached var selectedDate = new Date(year, month, date), alreadySelected = this.d._isSelected(selectedDate, this.d.cellType); if (!alreadySelected) { dp._trigger('clickCell', selectedDate); return; } dp._handleAlreadySelectedDates.bind(dp, alreadySelected, selectedDate)(); }, _onClickCell: function (e) { var $el = $(e.target).closest('.datepicker--cell'); if ($el.hasClass('-disabled-')) return; this._handleClick.bind(this)($el); } }; })(); ;(function () { var template = '' + '
#{prevHtml}
' + '
#{title}
' + '
#{nextHtml}
', buttonsContainerTemplate = '
', button = '#{label}', datepicker = $.fn.air_datepicker, dp = datepicker.Constructor; datepicker.Navigation = function (d, opts) { this.d = d; this.opts = opts; this.$buttonsContainer = ''; this.init(); }; datepicker.Navigation.prototype = { init: function () { this._buildBaseHtml(); this._bindEvents(); }, _bindEvents: function () { this.d.$nav.on('click', '.datepicker--nav-action', $.proxy(this._onClickNavButton, this)); this.d.$nav.on('click', '.datepicker--nav-title', $.proxy(this._onClickNavTitle, this)); this.d.$datepicker.on('click', '.datepicker--button', $.proxy(this._onClickNavButton, this)); }, _buildBaseHtml: function () { if (!this.opts.onlyTimepicker) { this._render(); } this._addButtonsIfNeed(); }, _addButtonsIfNeed: function () { if (this.opts.todayButton) { this._addButton('today') } if (this.opts.clearButton) { this._addButton('clear') } }, _render: function () { var title = this._getTitle(this.d.currentDate), html = dp.template(template, $.extend({title: title}, this.opts)); this.d.$nav.html(html); if (this.d.view == 'years') { $('.datepicker--nav-title', this.d.$nav).addClass('-disabled-'); } this.setNavStatus(); }, _getTitle: function (date) { return this.d.formatDate(this.opts.navTitles[this.d.view], date) }, _addButton: function (type) { if (!this.$buttonsContainer.length) { this._addButtonsContainer(); } var data = { action: type, label: this.d.loc[type] }, html = dp.template(button, data); if ($('[data-action=' + type + ']', this.$buttonsContainer).length) return; this.$buttonsContainer.append(html); }, _addButtonsContainer: function () { this.d.$datepicker.append(buttonsContainerTemplate); this.$buttonsContainer = $('.datepicker--buttons', this.d.$datepicker); }, setNavStatus: function () { if (!(this.opts.minDate || this.opts.maxDate) || !this.opts.disableNavWhenOutOfRange) return; var date = this.d.parsedDate, m = date.month, y = date.year, d = date.date; switch (this.d.view) { case 'days': if (!this.d._isInRange(new Date(y, m-1, 1), 'month')) { this._disableNav('prev') } if (!this.d._isInRange(new Date(y, m+1, 1), 'month')) { this._disableNav('next') } break; case 'months': if (!this.d._isInRange(new Date(y-1, m, d), 'year')) { this._disableNav('prev') } if (!this.d._isInRange(new Date(y+1, m, d), 'year')) { this._disableNav('next') } break; case 'years': var decade = dp.getDecade(this.d.date); if (!this.d._isInRange(new Date(decade[0] - 1, 0, 1), 'year')) { this._disableNav('prev') } if (!this.d._isInRange(new Date(decade[1] + 1, 0, 1), 'year')) { this._disableNav('next') } break; } }, _disableNav: function (nav) { $('[data-action="' + nav + '"]', this.d.$nav).addClass('-disabled-') }, _activateNav: function (nav) { $('[data-action="' + nav + '"]', this.d.$nav).removeClass('-disabled-') }, _onClickNavButton: function (e) { var $el = $(e.target).closest('[data-action]'), action = $el.data('action'); this.d[action](); }, _onClickNavTitle: function (e) { if ($(e.target).hasClass('-disabled-')) return; if (this.d.view == 'days') { return this.d.view = 'months' } this.d.view = 'years'; } } })(); ;(function () { var template = '
' + '
' + ' #{hourVisible}' + ' :' + ' #{minValue}' + '
' + '
' + '
' + ' ' + '
' + '
' + ' ' + '
' + '
' + '
', datepicker = $.fn.air_datepicker, dp = datepicker.Constructor; datepicker.Timepicker = function (inst, opts) { this.d = inst; this.opts = opts; this.init(); }; datepicker.Timepicker.prototype = { init: function () { var input = 'input'; this._setTime(this.d.date); this._buildHTML(); if (navigator.userAgent.match(/trident/gi)) { input = 'change'; } this.d.$el.on('selectDate', this._onSelectDate.bind(this)); this.$ranges.on(input, this._onChangeRange.bind(this)); this.$ranges.on('mouseup', this._onMouseUpRange.bind(this)); this.$ranges.on('mousemove focus ', this._onMouseEnterRange.bind(this)); this.$ranges.on('mouseout blur', this._onMouseOutRange.bind(this)); }, _setTime: function (date) { var _date = dp.getParsedDate(date); this._handleDate(date); this.hours = _date.hours < this.minHours ? this.minHours : _date.hours; this.minutes = _date.minutes < this.minMinutes ? this.minMinutes : _date.minutes; }, /** * Sets minHours and minMinutes from date (usually it's a minDate) * Also changes minMinutes if current hours are bigger then @date hours * @param date {Date} * @private */ _setMinTimeFromDate: function (date) { this.minHours = date.getHours(); this.minMinutes = date.getMinutes(); // If, for example, min hours are 10, and current hours are 12, // update minMinutes to default value, to be able to choose whole range of values if (this.d.lastSelectedDate) { if (this.d.lastSelectedDate.getHours() > date.getHours()) { this.minMinutes = this.opts.minMinutes; } } }, _setMaxTimeFromDate: function (date) { this.maxHours = date.getHours(); this.maxMinutes = date.getMinutes(); if (this.d.lastSelectedDate) { if (this.d.lastSelectedDate.getHours() < date.getHours()) { this.maxMinutes = this.opts.maxMinutes; } } }, _setDefaultMinMaxTime: function () { var maxHours = 23, maxMinutes = 59, opts = this.opts; this.minHours = opts.minHours < 0 || opts.minHours > maxHours ? 0 : opts.minHours; this.minMinutes = opts.minMinutes < 0 || opts.minMinutes > maxMinutes ? 0 : opts.minMinutes; this.maxHours = opts.maxHours < 0 || opts.maxHours > maxHours ? maxHours : opts.maxHours; this.maxMinutes = opts.maxMinutes < 0 || opts.maxMinutes > maxMinutes ? maxMinutes : opts.maxMinutes; }, /** * Looks for min/max hours/minutes and if current values * are out of range sets valid values. * @private */ _validateHoursMinutes: function (date) { if (this.hours < this.minHours) { this.hours = this.minHours; } else if (this.hours > this.maxHours) { this.hours = this.maxHours; } if (this.minutes < this.minMinutes) { this.minutes = this.minMinutes; } else if (this.minutes > this.maxMinutes) { this.minutes = this.maxMinutes; } }, _buildHTML: function () { var lz = dp.getLeadingZeroNum, data = { hourMin: this.minHours, hourMax: lz(this.maxHours), hourStep: this.opts.hoursStep, hourValue: this.hours, hourVisible: lz(this.displayHours), minMin: this.minMinutes, minMax: lz(this.maxMinutes), minStep: this.opts.minutesStep, minValue: lz(this.minutes) }, _template = dp.template(template, data); this.$timepicker = $(_template).appendTo(this.d.$datepicker); this.$ranges = $('[type="range"]', this.$timepicker); this.$hours = $('[name="hours"]', this.$timepicker); this.$minutes = $('[name="minutes"]', this.$timepicker); this.$hoursText = $('.datepicker--time-current-hours', this.$timepicker); this.$minutesText = $('.datepicker--time-current-minutes', this.$timepicker); if (this.d.ampm) { this.$ampm = $('') .appendTo($('.datepicker--time-current', this.$timepicker)) .html(this.dayPeriod); this.$timepicker.addClass('-am-pm-'); } }, _updateCurrentTime: function () { var h = dp.getLeadingZeroNum(this.displayHours), m = dp.getLeadingZeroNum(this.minutes); this.$hoursText.html(h); this.$minutesText.html(m); if (this.d.ampm) { this.$ampm.html(this.dayPeriod); } }, _updateRanges: function () { this.$hours.attr({ min: this.minHours, max: this.maxHours }).val(this.hours); this.$minutes.attr({ min: this.minMinutes, max: this.maxMinutes }).val(this.minutes) }, /** * Sets minHours, minMinutes etc. from date. If date is not passed, than sets * values from options * @param [date] {object} - Date object, to get values from * @private */ _handleDate: function (date) { this._setDefaultMinMaxTime(); if (date) { if (dp.isSame(date, this.d.opts.minDate)) { this._setMinTimeFromDate(this.d.opts.minDate); } else if (dp.isSame(date, this.d.opts.maxDate)) { this._setMaxTimeFromDate(this.d.opts.maxDate); } } this._validateHoursMinutes(date); }, update: function () { this._updateRanges(); this._updateCurrentTime(); }, /** * Calculates valid hour value to display in text input and datepicker's body. * @param date {Date|Number} - date or hours * @param [ampm] {Boolean} - 12 hours mode * @returns {{hours: *, dayPeriod: string}} * @private */ _getValidHoursFromDate: function (date, ampm) { var d = date, hours = date; if (date instanceof Date) { d = dp.getParsedDate(date); hours = d.hours; } var _ampm = ampm || this.d.ampm, dayPeriod = 'am'; if (_ampm) { switch(true) { case hours == 0: hours = 12; break; case hours == 12: dayPeriod = 'pm'; break; case hours > 11: hours = hours - 12; dayPeriod = 'pm'; break; default: break; } } return { hours: hours, dayPeriod: dayPeriod } }, set hours (val) { this._hours = val; var displayHours = this._getValidHoursFromDate(val); this.displayHours = displayHours.hours; this.dayPeriod = displayHours.dayPeriod; }, get hours() { return this._hours; }, // Events // ------------------------------------------------- _onChangeRange: function (e) { var $target = $(e.target), name = $target.attr('name'); this.d.timepickerIsActive = true; this[name] = $target.val(); this._updateCurrentTime(); this.d._trigger('timeChange', [this.hours, this.minutes]); this._handleDate(this.d.lastSelectedDate); this.update() }, _onSelectDate: function (e, data) { this._handleDate(data); this.update(); }, _onMouseEnterRange: function (e) { var name = $(e.target).attr('name'); $('.datepicker--time-current-' + name, this.$timepicker).addClass('-focus-'); }, _onMouseOutRange: function (e) { var name = $(e.target).attr('name'); if (this.d.inFocus) return; // Prevent removing focus when mouse out of range slider $('.datepicker--time-current-' + name, this.$timepicker).removeClass('-focus-'); }, _onMouseUpRange: function (e) { this.d.timepickerIsActive = false; } }; })(); })(window, jQuery);