Esta web utiliza cookies para proporcionarte la mejor experiencia de navegación posible. Estas cookies son guardadas en tu navegador y cumplen funciones como reconocerte cuando regresas a esta web y ayudarnos a saber qué secciones de la web encuentras más interesantes y útiles.
Solución para la incompatibilidad entre AirDatepicker y Jquery-ui datepicker.
- octubre 17, 2018
- Categoría: desarrollo framework jquery web
Importante: Esta información es para uso lúdico personal. En ningún caso se pretende dar conocimientos, consejos, formaciones, herramientas, ni soluciones técnicas de ámbito comercial ni profesional. Simplemente es información que se ha redactado en finalidad de recordatorio de ciertos proyectos realizados. Si usted no está seguro de la fiabilidad de la información, no cumple con sus expectativas o no es de su agrado, le ruego que abandone este sitio web. Para ver todas las exclusiones garantía y de responsabilidad acceda a la sección Aviso Legal.
Airdatepicker es una calendario moderno que podemos usar en cualquier proyecto web. Jquery-ui datepicker es otra librería muy usada para mostrar calendarios en páginas realizadas con WordPress.
El problema, y por esto escribo este post, es que los dos calendarios utilizan el mismo nombre para instanciar su objecto. El nombre es Datepicker. Entonces, si en una página web usamos (tenemos cargados) ambos datepicker o librerias se genera un conflicto y no funcionan.
En mi caso, la solución ha sido por cambiar el código del Air Datepicker para que use otro nombre en instanciar el objecto. Aquí dejo el código fuente del proyecto Air Datepicker y la clase modificada.
Proyecto Air Datepicker: http://t1m0n.name/air-datepicker/docs/
Solución al conflicto:
// Datepicker library ;(function (window, jQuery, undefined) { ;(function () { var VERSION = '2.2.3', pluginName = 'datepicker', autoInitSelector = '.datepicker-here', $body, $datepickersContainer, containerBuilt = false, baseTemplate = '' + '<div class="datepicker">' + '<i class="datepicker--pointer"></i>' + '<nav class="datepicker--nav"></nav>' + '<div class="datepicker--content"></div>' + '</div>', 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: '<svg><path d="M 17,12 l -5,5 l 5,5"></path></svg>', nextHtml: '<svg><path d="M 14,12 l 5,5 l -5,5"></path></svg>', navTitles: { days: 'MM, <i>yyyy</i>', 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] }, airdatepicker; var Airdatepicker = function (el, options) { this.el = el; this.$el = jQuery(el); this.opts = jQuery.extend(true, {}, defaults, options, this.$el.data()); if ($body == undefined) { $body = jQuery('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' ? jQuery(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() }; airdatepicker = Airdatepicker; airdatepicker.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.$airdatepicker.on('mousedown', this._onMouseDownDatepicker.bind(this)); this.$airdatepicker.on('mouseup', this._onMouseUpDatepicker.bind(this)); } if (this.opts.classes) { this.$airdatepicker.addClass(this.opts.classes) } if (this.opts.timepicker) { this.timepicker = new jQuery.fn.airdatepicker.Timepicker(this, this.opts); this._bindTimepickerEvents(); } if (this.opts.onlyTimepicker) { this.$airdatepicker.addClass('-only-timepicker-'); } this.views[this.currentView] = new jQuery.fn.airdatepicker.Body(this, this.currentView, this.opts); this.views[this.currentView].show(); this.nav = new jQuery.fn.airdatepicker.Navigation(this, this.opts); this.view = this.currentView; this.$el.on('clickCell.adp', this._onClickCell.bind(this)); this.$airdatepicker.on('mouseenter', '.datepicker--cell', this._onMouseEnterCell.bind(this)); this.$airdatepicker.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)); jQuery(window).on('resize.adp', this._onResize.bind(this)); jQuery('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 = jQuery.fn.airdatepicker.language[lang]; if (!this.loc) { console.warn('Can\'t find language "' + lang + '" in Datepicker.language, will use "ru" instead'); this.loc = jQuery.extend(true, {}, jQuery.fn.airdatepicker.language.ru) } this.loc = jQuery.extend(true, {}, jQuery.fn.airdatepicker.language.ru, jQuery.fn.airdatepicker.language[lang]) } else { this.loc = jQuery.extend(true, {}, jQuery.fn.airdatepicker.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('<div class="datepickers-container" id="datepickers-container"></div>'); $datepickersContainer = jQuery('#datepickers-container'); }, _buildBaseHtml: function () { var $appendTarget, $inline = jQuery('<div class="datepicker-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.$airdatepicker = jQuery(baseTemplate).appendTo($appendTarget); this.$content = jQuery('.datepicker--content', this.$airdatepicker); this.$nav = jQuery('.datepicker--nav', this.$airdatepicker); }, _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 = airdatepicker.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 = airdatepicker.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 = airdatepicker.getLeadingZeroNum, decade = airdatepicker.getDecade(date), d = airdatepicker.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 (airdatepicker.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 (airdatepicker.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 = jQuery.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.$airdatepicker.addClass(this.opts.classes) } if (this.opts.onlyTimepicker) { this.$airdatepicker.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 (airdatepicker.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 = airdatepicker.getParsedDate(date), min = airdatepicker.getParsedDate(this.minDate), max = airdatepicker.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.$airdatepicker .removeAttr('class') .addClass(classes); }, setPosition: function (position) { position = position || this.opts.position; var dims = this._getDimensions(this.$el), selfDims = this._getDimensions(this.$airdatepicker), 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.$airdatepicker .css({ left: left, top: top }) }, show: function () { var onShow = this.opts.onShow; this.setPosition(this.opts.position); this.$airdatepicker.addClass('active'); this.visible = true; if (onShow) { this._bindVisionEvents(onShow) } }, hide: function () { var onHide = this.opts.onHide; this.$airdatepicker .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.$airdatepicker.off('transitionend.dp'); event(this, false); this.$airdatepicker.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 = airdatepicker.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 = airdatepicker.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 = airdatepicker.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 = airdatepicker.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 = airdatepicker.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 : jQuery(''); }, 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.$airdatepicker.closest('.datepicker-inline').remove(); } else { _this.$airdatepicker.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 = jQuery(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 (airdatepicker.less(this.minRange, this.focused)) { this.maxRange = this.minRange; this.minRange = ''; } this.views[this.currentView]._update(); } }, _onMouseLeaveCell: function (e) { var $cell = jQuery(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 (airdatepicker.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 airdatepicker.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 jQuery.fn.airdatepicker.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 = airdatepicker.getParsedDate(this.minDate); return new Date(min.year, min.month, min.date).getTime() }, get maxTime() { var max = airdatepicker.getParsedDate(this.maxDate); return new Date(max.year, max.month, max.date).getTime() }, get curDecade() { return airdatepicker.getDecade(this.date) } }; // Utils // ------------------------------------------------- airdatepicker.getDaysCount = function (date) { return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); }; airdatepicker.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() } }; airdatepicker.getDecade = function (date) { var firstYear = Math.floor(date.getFullYear() / 10) * 10; return [firstYear, firstYear + 9]; }; airdatepicker.template = function (str, data) { return str.replace(/#\{([\w]+)\}/g, function (source, match) { if (data[match] || data[match] === 0) { return data[match] } }); }; airdatepicker.isSame = function (date1, date2, type) { if (!date1 || !date2) return false; var d1 = airdatepicker.getParsedDate(date1), d2 = airdatepicker.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]; }; airdatepicker.less = function (dateCompareTo, date, type) { if (!dateCompareTo || !date) return false; return date.getTime() < dateCompareTo.getTime(); }; airdatepicker.bigger = function (dateCompareTo, date, type) { if (!dateCompareTo || !date) return false; return date.getTime() > dateCompareTo.getTime(); }; airdatepicker.getLeadingZeroNum = function (num) { return parseInt(num) < 10 ? '0' + num : num; }; /** * Returns copy of date with hours and minutes equals to 0 * @param date {Date} */ airdatepicker.resetTime = function (date) { if (typeof date != 'object') return; date = airdatepicker.getParsedDate(date); return new Date(date.year, date.month, date.date) }; jQuery.fn.airdatepicker = function ( options ) { return this.each(function () { if (!jQuery.data(this, pluginName)) { jQuery.data(this, pluginName, new Airdatepicker( this, options )); } else { var _this = jQuery.data(this, pluginName); _this.opts = jQuery.extend(true, _this.opts, options); _this.update(); } }); }; jQuery.fn.airdatepicker.Constructor = Airdatepicker; jQuery.fn.airdatepicker.language = { ru: { days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'], daysShort: ['Вос','Пон','Вто','Сре','Чет','Пят','Суб'], daysMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'], months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'], monthsShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'], today: 'Сегодня', clear: 'Очистить', dateFormat: 'dd.mm.yyyy', timeFormat: 'hh:ii', firstDay: 1 } }; jQuery(function () { jQuery(autoInitSelector).airdatepicker(); }) })(); ;(function () { var templates = { days:'' + '<div class="datepicker--days datepicker--body">' + '<div class="datepicker--days-names"></div>' + '<div class="datepicker--cells datepicker--cells-days"></div>' + '</div>', months: '' + '<div class="datepicker--months datepicker--body">' + '<div class="datepicker--cells datepicker--cells-months"></div>' + '</div>', years: '' + '<div class="datepicker--years datepicker--body">' + '<div class="datepicker--cells datepicker--cells-years"></div>' + '</div>' }, airdatepicker = jQuery.fn.airdatepicker, dp = airdatepicker.Constructor; airdatepicker.Body = function (d, type, opts) { this.d = d; this.type = type; this.opts = opts; this.$el = jQuery(''); if (this.opts.onlyTimepicker) return; this.init(); }; airdatepicker.Body.prototype = { init: function () { this._buildBaseHtml(); this._render(); this._bindEvents(); }, _bindEvents: function () { this.$el.on('click', '.datepicker--cell', jQuery.proxy(this._onClickCell, this)); }, _buildBaseHtml: function () { this.$el = jQuery(templates[this.type]).appendTo(this.d.$content); this.$names = jQuery('.datepicker--days-names', this.$el); this.$cells = jQuery('.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 += '<div class="datepicker--day-name' + (this.d.isWeekend(curDay) ? " -weekend-" : "") + '">' + this.d.loc.daysMin[curDay] + '</div>'; 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 '<div class="' + content.classes + '" ' + 'data-date="' + date.getDate() + '" ' + 'data-month="' + date.getMonth() + '" ' + 'data-year="' + date.getFullYear() + '">' + content.html + '</div>'; }, /** * 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 '<div class="' + content.classes + '" data-month="' + date.getMonth() + '">' + content.html + '</div>' }, _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 '<div class="' + content.classes + '" data-year="' + date.getFullYear() + '">' + content.html + '</div>' }, _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 = jQuery('.datepicker--cell', this.$cells), _this = this, classes, $cell, date; $cells.each(function (cell, i) { $cell = jQuery(this); date = _this.d._getDateFromCell(jQuery(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 = jQuery(e.target).closest('.datepicker--cell'); if ($el.hasClass('-disabled-')) return; this._handleClick.bind(this)($el); } }; })(); ;(function () { var template = '' + '<div class="datepicker--nav-action" data-action="prev">#{prevHtml}</div>' + '<div class="datepicker--nav-title">#{title}</div>' + '<div class="datepicker--nav-action" data-action="next">#{nextHtml}</div>', buttonsContainerTemplate = '<div class="datepicker--buttons"></div>', button = '<span class="datepicker--button" data-action="#{action}">#{label}</span>', airdatepicker = jQuery.fn.airdatepicker, dp = airdatepicker.Constructor; airdatepicker.Navigation = function (d, opts) { this.d = d; this.opts = opts; this.$buttonsContainer = ''; this.init(); }; airdatepicker.Navigation.prototype = { init: function () { this._buildBaseHtml(); this._bindEvents(); }, _bindEvents: function () { this.d.$nav.on('click', '.datepicker--nav-action', jQuery.proxy(this._onClickNavButton, this)); this.d.$nav.on('click', '.datepicker--nav-title', jQuery.proxy(this._onClickNavTitle, this)); this.d.$airdatepicker.on('click', '.datepicker--button', jQuery.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, jQuery.extend({title: title}, this.opts)); this.d.$nav.html(html); if (this.d.view == 'years') { jQuery('.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 (jQuery('[data-action=' + type + ']', this.$buttonsContainer).length) return; this.$buttonsContainer.append(html); }, _addButtonsContainer: function () { this.d.$airdatepicker.append(buttonsContainerTemplate); this.$buttonsContainer = jQuery('.datepicker--buttons', this.d.$airdatepicker); }, 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) { jQuery('[data-action="' + nav + '"]', this.d.$nav).addClass('-disabled-') }, _activateNav: function (nav) { jQuery('[data-action="' + nav + '"]', this.d.$nav).removeClass('-disabled-') }, _onClickNavButton: function (e) { var $el = jQuery(e.target).closest('[data-action]'), action = $el.data('action'); this.d[action](); }, _onClickNavTitle: function (e) { if (jQuery(e.target).hasClass('-disabled-')) return; if (this.d.view == 'days') { return this.d.view = 'months' } this.d.view = 'years'; } } })(); ;(function () { var template = '<div class="datepicker--time">' + '<div class="datepicker--time-current">' + ' <span class="datepicker--time-current-hours">#{hourVisible}</span>' + ' <span class="datepicker--time-current-colon">:</span>' + ' <span class="datepicker--time-current-minutes">#{minValue}</span>' + '</div>' + '<div class="datepicker--time-sliders">' + ' <div class="datepicker--time-row">' + ' <input type="range" name="hours" value="#{hourValue}" min="#{hourMin}" max="#{hourMax}" step="#{hourStep}"/>' + ' </div>' + ' <div class="datepicker--time-row">' + ' <input type="range" name="minutes" value="#{minValue}" min="#{minMin}" max="#{minMax}" step="#{minStep}"/>' + ' </div>' + '</div>' + '</div>', airdatepicker = jQuery.fn.airdatepicker, dp = airdatepicker.Constructor; airdatepicker.Timepicker = function (inst, opts) { this.d = inst; this.opts = opts; this.init(); }; airdatepicker.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 = jQuery(_template).appendTo(this.d.$airdatepicker); this.$ranges = jQuery('[type="range"]', this.$timepicker); this.$hours = jQuery('[name="hours"]', this.$timepicker); this.$minutes = jQuery('[name="minutes"]', this.$timepicker); this.$hoursText = jQuery('.datepicker--time-current-hours', this.$timepicker); this.$minutesText = jQuery('.datepicker--time-current-minutes', this.$timepicker); if (this.d.ampm) { this.$ampm = jQuery('<span class="datepicker--time-current-ampm">') .appendTo(jQuery('.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 = jQuery(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 = jQuery(e.target).attr('name'); jQuery('.datepicker--time-current-' + name, this.$timepicker).addClass('-focus-'); }, _onMouseOutRange: function (e) { var name = jQuery(e.target).attr('name'); if (this.d.inFocus) return; // Prevent removing focus when mouse out of range slider jQuery('.datepicker--time-current-' + name, this.$timepicker).removeClass('-focus-'); }, _onMouseUpRange: function (e) { this.d.timepickerIsActive = false; } }; })(); })(window, jQuery); // Languages airdate picker ;(function (jQuery) { jQuery.fn.airdatepicker.language['en'] = { days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], months: ['January','February','March','April','May','June', 'July','August','September','October','November','December'], monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], today: 'Today', clear: 'Clear', dateFormat: 'dd/mm/yyyy', timeFormat: 'hh:ii aa', firstDay: 0 }; })(jQuery); ;(function (jQuery) { jQuery.fn.airdatepicker.language['es'] = { days: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'], daysShort: ['Dom', 'Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab'], daysMin: ['Do', 'Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa'], months: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Augosto','Septiembre','Octubre','Noviembre','Diciembre'], monthsShort: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'], today: 'Hoy', clear: 'Limpiar', dateFormat: 'dd/mm/yyyy', timeFormat: 'hh:ii aa', firstDay: 1 }; })(jQuery); ;(function (jQuery) { jQuery.fn.airdatepicker.language['ca'] = { days: ['Diumenge', 'Dilluns', 'Dimarts', 'Dimecres', 'Jueves', 'Divendres', 'Dissabte'], daysShort: ['Diu', 'Dll', 'Dmt', 'Dmc', 'Dij', 'Div', 'Dis'], daysMin: ['Dg', 'Dl', 'Dt', 'Dc', 'Dj', 'Dv', 'Ds'], months: ['Gener','Febrer','Març','Abril','Maig','Juny', 'Juliol','Agost','Setembre','Octubre','Novembre','Desembre'], monthsShort: ['Gen', 'Feb', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Oct', 'Nov', 'Des'], today: 'Avui', clear: 'Netejar', dateFormat: 'dd/mm/yyyy', timeFormat: 'hh:ii aa', firstDay: 1 }; })(jQuery); ;(function (jQuery) { jQuery.fn.airdatepicker.language['fr'] = { days: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'], daysShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'], daysMin: ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'], months: ['Janvier','Février','Mars','Avril','Mai','Juin', 'Juillet','Août','Septembre','Octobre','Novembre','Decembre'], monthsShort: ['Jan', 'Fév', 'Mars', 'Avr', 'Mai', 'Juin', 'Juil', 'Août', 'Sep', 'Oct', 'Nov', 'Dec'], today: "Aujourd'hui", clear: 'Effacer', dateFormat: 'dd/mm/yyyy', timeFormat: 'hh:ii', firstDay: 1 }; })(jQuery); ;(function (jQuery) { jQuery.fn.airdatepicker.language['nl'] = { days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'], daysShort: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], daysMin: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], months: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'], monthsShort: ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'], today: 'Vandaag', clear: 'Legen', dateFormat: 'dd/mm/yyyy', timeFormat: 'hh:ii', firstDay: 1 }; })(jQuery); ;(function (jQuery) { jQuery.fn.airdatepicker.language['de'] = { days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], daysShort: ['Son', 'Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam'], daysMin: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'], months: ['Januar','Februar','März','April','Mai','Juni', 'Juli','August','September','Oktober','November','Dezember'], monthsShort: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'], today: 'Heute', clear: 'Aufräumen', dateFormat: 'dd/mm/yyyy', timeFormat: 'hh:ii', firstDay: 1 }; })(jQuery); ;(function (jQuery) { jQuery.fn.airdatepicker.language['pl'] = { days: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'], daysShort: ['Nie', 'Pon', 'Wto', 'Śro', 'Czw', 'Pią', 'Sob'], daysMin: ['Nd', 'Pn', 'Wt', 'Śr', 'Czw', 'Pt', 'So'], months: ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec', 'Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień'], monthsShort: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'], today: 'Dzisiaj', clear: 'Wyczyść', dateFormat: 'dd/mm/yyyy', timeFormat: 'hh:ii', firstDay: 1 }; })(jQuery); ;(function (jQuery) { jQuery.fn.airdatepicker.language['it'] = { days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'], daysShort: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'], daysMin: ['Do', 'Lu', 'Ma', 'Me', 'Gi', 'Ve', 'Sa'], months: ['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno', 'Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre'], monthsShort: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'], today: 'Oggi', clear: 'Pulisci', dateFormat: 'dd/mm/yyyy', timeFormat: 'hh:ii', firstDay: 1 }; })(jQuery); ;(function (jQuery) { jQuery.fn.airdatepicker.language['ru'] = { days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], months: ['January','February','March','April','May','June', 'July','August','September','October','November','December'], monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], today: 'Today', clear: 'Clear', dateFormat: 'dd/mm/yyyy', timeFormat: 'hh:ii aa', firstDay: 0 }; })(jQuery); ;(function (jQuery) { jQuery.fn.airdatepicker.language['cn'] = { days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], months: ['January','February','March','April','May','June', 'July','August','September','October','November','December'], monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], today: 'Today', clear: 'Clear', dateFormat: 'dd/mm/yyyy', timeFormat: 'hh:ii aa', firstDay: 0 }; })(jQuery);