PageSourceSearch

https://autodesignmagazine.com/wp-content/plugins/halfdata-green-popups/js/airdatepicker.js?x17940&ver=7.6.1

js autodesignmagazine.com collected 2026-10-02 06:57:39 UTC 68,377 bytes, 2,443 lines download raw bytes

1;(function (window, $, undefined) { ;(function () {
2	"use strict";
3	var VERSION = '2.2.3',
4		pluginName = 'airdatepicker',
5		autoInitSelector = '.airdatepicker-here',
6		$body, $airdatepickersContainer,
7		containerBuilt = false,
8		baseTemplate = '' +
9			'<div class="airdatepicker">' +
10			'<i class="airdatepicker--pointer"></i>' +
11			'<nav class="airdatepicker--nav"></nav>' +
12			'<div class="airdatepicker--content"></div>' +
13			'</div>',
14		defaults = {
15			classes: '',
16			inline: false,
17// Modified by halfdata - 2018-10-11 - begin
18			inline_popup: false,
19// Modified by halfdata - 2018-10-11 - begin
20			language: 'en',
21			startDate: new Date(),
22			firstDay: '',
23			weekends: [6, 0],
24			dateFormat: '',
25			altField: '',
26			altFieldDateFormat: '@',
27			toggleSelected: true,
28			keyboardNav: true,
29
30			position: 'bottom left',
31			offset: 12,
32
33			view: 'days',
34			minView: 'days',
35
36			showOtherMonths: true,
37			selectOtherMonths: true,
38			moveToOtherMonthsOnSelect: true,
39
40			showOtherYears: true,
41			selectOtherYears: true,
42			moveToOtherYearsOnSelect: true,
43
44			minDate: '',
45			maxDate: '',
46			disableNavWhenOutOfRange: true,
47
48			multipleDates: false, // Boolean or Number
49			multipleDatesSeparator: ',',
50			range: false,
51
52			todayButton: false,
53			clearButton: false,
54
55			showEvent: 'focus',
56			autoClose: false,
57
58			// navigation
59			monthsField: 'monthsShort',
60			prevHtml: '<svg><path d="M 17,12 l -5,5 l 5,5"></path></svg>',
61			nextHtml: '<svg><path d="M 14,12 l 5,5 l -5,5"></path></svg>',
62			navTitles: {
63				days: 'MM, <i>yyyy</i>',
64				months: 'yyyy',
65				years: 'yyyy1 - yyyy2'
66			},
67
68			// timepicker
69			timepicker: false,
70			onlyTimepicker: false,
71			dateTimeSeparator: ' ',
72			timeFormat: '',
73			minHours: 0,
74			maxHours: 24,
75			minMinutes: 0,
76			maxMinutes: 59,
77			hoursStep: 1,
78			minutesStep: 1,
79
80			// events
81			onSelect: '',
82			onShow: '',
83			onHide: '',
84			onChangeMonth: '',
85			onChangeYear: '',
86			onChangeDecade: '',
87			onChangeView: '',
88			onRenderCell: ''
89		},
90		hotKeys = {
91			'ctrlRight': [17, 39],
92			'ctrlUp': [17, 38],
93			'ctrlLeft': [17, 37],
94			'ctrlDown': [17, 40],
95			'shiftRight': [16, 39],
96			'shiftUp': [16, 38],
97			'shiftLeft': [16, 37],
98			'shiftDown': [16, 40],
99			'altUp': [18, 38],
100			'altRight': [18, 39],
101			'altLeft': [18, 37],
102			'altDown': [18, 40],
103			'ctrlShiftUp': [16, 17, 38]
104		},
105		airdatepicker;
106
107	var Datepicker  = function (el, options) {
108		this.el = el;
109		this.$el = $(el);
110
111		this.opts = $.extend(true, {}, defaults, options, this.$el.data());
112
113		if ($body == undefined) {
114			$body = $('body');
115		}
116
117		if (!this.opts.startDate) {
118			this.opts.startDate = new Date();
119		}
120
121		if (this.el.nodeName == 'INPUT') {
122			this.elIsInput = true;
123		}
124
125		if (this.opts.altField) {
126			this.$altField = typeof this.opts.altField == 'string' ? $(this.opts.altField) : this.opts.altField;
127		}
128
129		this.inited = false;
130		this.visible = false;
131		this.silent = false; // Need to prevent unnecessary rendering
132
133		this.currentDate = this.opts.startDate;
134		this.currentView = this.opts.view;
135		this._createShortCuts();
136		this.selectedDates = [];
137		this.views = {};
138		this.keys = [];
139		this.minRange = '';
140		this.maxRange = '';
141		this._prevOnSelectValue = '';
142
143		this.init()
144	};
145
146	airdatepicker = Datepicker;
147
148	airdatepicker.prototype = {
149		VERSION: VERSION,
150		viewIndexes: ['days', 'months', 'years'],
151
152		init: function () {
153			if (!containerBuilt && !this.opts.inline && this.elIsInput) {
154				this._buildDatepickersContainer();
155			}
156			this._buildBaseHtml();
157			this._defineLocale(this.opts.language);
158			this._syncWithMinMaxDates();
159
160			if (this.elIsInput) {
161				if (!this.opts.inline) {
162					// Set extra classes for proper transitions
163					this._setPositionClasses(this.opts.position);
164					this._bindEvents()
165				}
166				if (this.opts.keyboardNav && !this.opts.onlyTimepicker) {
167					this._bindKeyboardEvents();
168				}
169				this.$airdatepicker.on('mousedown', this._onMouseDownDatepicker.bind(this));
170				this.$airdatepicker.on('mouseup', this._onMouseUpDatepicker.bind(this));
171			}
172
173			if (this.opts.classes) {
174				this.$airdatepicker.addClass(this.opts.classes)
175			}
176
177			if (this.opts.timepicker) {
178				this.timepicker = new $.fn.airdatepicker.Timepicker(this, this.opts);
179				this._bindTimepickerEvents();
180			}
181
182			if (this.opts.onlyTimepicker) {
183				this.$airdatepicker.addClass('-only-timepicker-');
184			}
185
186			this.views[this.currentView] = new $.fn.airdatepicker.Body(this, this.currentView, this.opts);
187			this.views[this.currentView].show();
188			this.nav = new $.fn.airdatepicker.Navigation(this, this.opts);
189			this.view = this.currentView;
190
191			this.$el.on('clickCell.adp', this._onClickCell.bind(this));
192			this.$airdatepicker.on('mouseenter', '.airdatepicker--cell', this._onMouseEnterCell.bind(this));
193			this.$airdatepicker.on('mouseleave', '.airdatepicker--cell', this._onMouseLeaveCell.bind(this));
194
195			this.inited = true;
196		},
197
198		_createShortCuts: function () {
199			this.minDate = this.opts.minDate ? this.opts.minDate : new Date(-8639999913600000);
200			this.maxDate = this.opts.maxDate ? this.opts.maxDate : new Date(8639999913600000);
201		},
202
203		_bindEvents : function () {
204			this.$el.on(this.opts.showEvent + '.adp', this._onShowEvent.bind(this));
205			this.$el.on('mouseup.adp', this._onMouseUpEl.bind(this));
206			this.$el.on('blur.adp', this._onBlur.bind(this));
207			this.$el.on('keyup.adp', this._onKeyUpGeneral.bind(this));
208			$(window).on('resize.adp', this._onResize.bind(this));
209			$('body').on('mouseup.adp', this._onMouseUpBody.bind(this));
210		},
211
212		_bindKeyboardEvents: function () {
213			this.$el.on('keydown.adp', this._onKeyDown.bind(this));
214			this.$el.on('keyup.adp', this._onKeyUp.bind(this));
215			this.$el.on('hotKey.adp', this._onHotKey.bind(this));
216		},
217
218		_bindTimepickerEvents: function () {
219			this.$el.on('timeChange.adp', this._onTimeChange.bind(this));
220		},
221
222		isWeekend: function (day) {
223			return this.opts.weekends.indexOf(day) !== -1;
224		},
225
226		_defineLocale: function (lang) {
227			if (typeof lang == 'string') {
228				this.loc = $.fn.airdatepicker.language[lang];
229				if (!this.loc) {
230					console.warn('Can\'t find language "' + lang + '" in Datepicker.language, will use "en" instead');
231					this.loc = $.extend(true, {}, $.fn.airdatepicker.language.ru)
232				}
233
234				this.loc = $.extend(true, {}, $.fn.airdatepicker.language.ru, $.fn.airdatepicker.language[lang])
235			} else {
236				this.loc = $.extend(true, {}, $.fn.airdatepicker.language.ru, lang)
237			}
238
239			if (this.opts.dateFormat) {
240				this.loc.dateFormat = this.opts.dateFormat
241			}
242
243			if (this.opts.timeFormat) {
244				this.loc.timeFormat = this.opts.timeFormat
245			}
246
247			if (this.opts.firstDay !== '') {
248				this.loc.firstDay = this.opts.firstDay
249			}
250
251			if (this.opts.timepicker) {
252				this.loc.dateFormat = [this.loc.dateFormat, this.loc.timeFormat].join(this.opts.dateTimeSeparator);
253			}
254
255			if (this.opts.onlyTimepicker) {
256				this.loc.dateFormat = this.loc.timeFormat;
257			}
258
259			var boundary = this._getWordBoundaryRegExp;
260			if (this.loc.timeFormat.match(boundary('aa')) ||
261				this.loc.timeFormat.match(boundary('AA'))
262			) {
263			   this.ampm = true;
264			}
265		},
266
267		_buildDatepickersContainer: function () {
268			containerBuilt = true;
269			$body.append('<div class="airdatepickers-container" id="airdatepickers-container"></div>');
270			$airdatepickersContainer = $('#airdatepickers-container');
271		},
272
273		_buildBaseHtml: function () {
274			var $appendTarget,
275				$inline = $('<div class="airdatepicker-inline">');
276
277			if(this.el.nodeName == 'INPUT') {
278				if (!this.opts.inline) {
279// Modified by halfdata - 2018-10-11 - begin
280					//$appendTarget = $airdatepickersContainer;
281					if (!this.opts.inline_popup) $appendTarget = $airdatepickersContainer;
282					else $appendTarget = jQuery(this.$el).parent();
283// Modified by halfdata - 2018-10-11 - end
284				} else {
285					$appendTarget = $inline.insertAfter(this.$el)
286				}
287			} else {
288				$appendTarget = $inline.appendTo(this.$el)
289			}
290
291			this.$airdatepicker = $(baseTemplate).appendTo($appendTarget);
292			this.$content = $('.airdatepicker--content', this.$airdatepicker);
293			this.$nav = $('.airdatepicker--nav', this.$airdatepicker);
294		},
295
296		_triggerOnChange: function () {
297			if (!this.selectedDates.length) {
298				// Prevent from triggering multiple onSelect callback with same argument (empty string) in IE10-11
299				if (this._prevOnSelectValue === '') return;
300				this._prevOnSelectValue = '';
301				return this.opts.onSelect('', '', this);
302			}
303
304			var selectedDates = this.selectedDates,
305				parsedSelected = airdatepicker.getParsedDate(selectedDates[0]),
306				formattedDates,
307				_this = this,
308				dates = new Date(
309					parsedSelected.year,
310					parsedSelected.month,
311					parsedSelected.date,
312					parsedSelected.hours,
313					parsedSelected.minutes
314				);
315
316				formattedDates = selectedDates.map(function (date) {
317					return _this.formatDate(_this.loc.dateFormat, date)
318				}).join(this.opts.multipleDatesSeparator);
319
320			// Create new dates array, to separate it from original selectedDates
321			if (this.opts.multipleDates || this.opts.range) {
322				dates = selectedDates.map(function(date) {
323					var parsedDate = airdatepicker.getParsedDate(date);
324					return new Date(
325						parsedDate.year,
326						parsedDate.month,
327						parsedDate.date,
328						parsedDate.hours,
329						parsedDate.minutes
330					);
331				})
332			}
333
334			this._prevOnSelectValue = formattedDates;
335			this.opts.onSelect(formattedDates, dates, this);
336		},
337
338		next: function () {
339			var d = this.parsedDate,
340				o = this.opts;
341			switch (this.view) {
342				case 'days':
343					this.date = new Date(d.year, d.month + 1, 1);
344					if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year);
345					break;
346				case 'months':
347					this.date = new Date(d.year + 1, d.month, 1);
348					if (o.onChangeYear) o.onChangeYear(this.parsedDate.year);
349					break;
350				case 'years':
351					this.date = new Date(d.year + 10, 0, 1);
352					if (o.onChangeDecade) o.onChangeDecade(this.curDecade);
353					break;
354			}
355		},
356
357		prev: function () {
358			var d = this.parsedDate,
359				o = this.opts;
360			switch (this.view) {
361				case 'days':
362					this.date = new Date(d.year, d.month - 1, 1);
363					if (o.onChangeMonth) o.onChangeMonth(this.parsedDate.month, this.parsedDate.year);
364					break;
365				case 'months':
366					this.date = new Date(d.year - 1, d.month, 1);
367					if (o.onChangeYear) o.onChangeYear(this.parsedDate.year);
368					break;
369				case 'years':
370					this.date = new Date(d.year - 10, 0, 1);
371					if (o.onChangeDecade) o.onChangeDecade(this.curDecade);
372					break;
373			}
374		},
375
376		formatDate: function (string, date) {
377			date = date || this.date;
378			var result = string,
379				boundary = this._getWordBoundaryRegExp,
380				locale = this.loc,
381				leadingZero = airdatepicker.getLeadingZeroNum,
382				decade = airdatepicker.getDecade(date),
383				d = airdatepicker.getParsedDate(date),
384				fullHours = d.fullHours,
385				hours = d.hours,
386				ampm = string.match(boundary('aa')) || string.match(boundary('AA')),
387				dayPeriod = 'am',
388				replacer = this._replacer,
389				validHours;
390
391			if (this.opts.timepicker && this.timepicker && ampm) {
392				validHours = this.timepicker._getValidHoursFromDate(date, ampm);
393				fullHours = leadingZero(validHours.hours);
394				hours = validHours.hours;
395				dayPeriod = validHours.dayPeriod;
396			}
397
398			switch (true) {
399				case /@/.test(result):
400					result = result.replace(/@/, date.getTime());
401				case /aa/.test(result):
402					result = replacer(result, boundary('aa'), dayPeriod);
403				case /AA/.test(result):
404					result = replacer(result, boundary('AA'), dayPeriod.toUpperCase());
405				case /dd/.test(result):
406					result = replacer(result, boundary('dd'), d.fullDate);
407				case /d/.test(result):
408					result = replacer(result, boundary('d'), d.date);
409				case /DD/.test(result):
410					result = replacer(result, boundary('DD'), locale.days[d.day]);
411				case /D/.test(result):
412					result = replacer(result, boundary('D'), locale.daysShort[d.day]);
413				case /mm/.test(result):
414					result = replacer(result, boundary('mm'), d.fullMonth);
415				case /m/.test(result):
416					result = replacer(result, boundary('m'), d.month + 1);
417				case /MM/.test(result):
418					result = replacer(result, boundary('MM'), this.loc.months[d.month]);
419				case /M/.test(result):
420					result = replacer(result, boundary('M'), locale.monthsShort[d.month]);
421				case /ii/.test(result):
422					result = replacer(result, boundary('ii'), d.fullMinutes);
423				case /i/.test(result):
424					result = replacer(result, boundary('i'), d.minutes);
425				case /hh/.test(result):
426					result = replacer(result, boundary('hh'), fullHours);
427				case /h/.test(result):
428					result = replacer(result, boundary('h'), hours);
429				case /yyyy/.test(result):
430					result = replacer(result, boundary('yyyy'), d.year);
431				case /yyyy1/.test(result):
432					result = replacer(result, boundary('yyyy1'), decade[0]);
433				case /yyyy2/.test(result):
434					result = replacer(result, boundary('yyyy2'), decade[1]);
435				case /yy/.test(result):
436					result = replacer(result, boundary('yy'), d.year.toString().slice(-2));
437			}
438
439			return result;
440		},
441
442		_replacer: function (str, reg, data) {
443			return str.replace(reg, function (match, p1,p2,p3) {
444				return p1 + data + p3;
445			})
446		},
447
448		_getWordBoundaryRegExp: function (sign) {
449			var symbols = '\\s|\\.|-|/|\\\\|,|\\$|\\!|\\?|:|;';
450
451			return new RegExp('(^|>|' + symbols + ')(' + sign + ')($|<|' + symbols + ')', 'g');
452		},
453
454
455		selectDate: function (date) {
456			var _this = this,
457				opts = _this.opts,
458				d = _this.parsedDate,
459				selectedDates = _this.selectedDates,
460				len = selectedDates.length,
461				newDate = '';
462
463			if (Array.isArray(date)) {
464				date.forEach(function (d) {
465					_this.selectDate(d)
466				});
467				return;
468			}
469
470			if (!(date instanceof Date)) return;
471
472			this.lastSelectedDate = date;
473
474			// Set new time values from Date
475			if (this.timepicker) {
476				this.timepicker._setTime(date);
477			}
478
479			// On this step timepicker will set valid values in it's instance
480			_this._trigger('selectDate', date);
481
482			// Set correct time values after timepicker's validation
483			// Prevent from setting hours or minutes which values are lesser then `min` value or
484			// greater then `max` value
485			if (this.timepicker) {
486				date.setHours(this.timepicker.hours);
487				date.setMinutes(this.timepicker.minutes)
488			}
489
490			if (_this.view == 'days') {
491				if (date.getMonth() != d.month && opts.moveToOtherMonthsOnSelect) {
492					newDate = new Date(date.getFullYear(), date.getMonth(), 1);
493				}
494			}
495
496			if (_this.view == 'years') {
497				if (date.getFullYear() != d.year && opts.moveToOtherYearsOnSelect) {
498					newDate = new Date(date.getFullYear(), 0, 1);
499				}
500			}
501
502			if (newDate) {
503				_this.silent = true;
504				_this.date = newDate;
505				_this.silent = false;
506				_this.nav._render()
507			}
508
509			if (opts.multipleDates && !opts.range) { // Set priority to range functionality
510				if (len === opts.multipleDates) return;
511				if (!_this._isSelected(date)) {
512					_this.selectedDates.push(date);
513				}
514			} else if (opts.range) {
515				if (len == 2) {
516					_this.selectedDates = [date];
517					_this.minRange = date;
518					_this.maxRange = '';
519				} else if (len == 1) {
520					_this.selectedDates.push(date);
521					if (!_this.maxRange){
522						_this.maxRange = date;
523					} else {
524						_this.minRange = date;
525					}
526					// Swap dates if they were selected via dp.selectDate() and second date was smaller then first
527					if (airdatepicker.bigger(_this.maxRange, _this.minRange)) {
528						_this.maxRange = _this.minRange;
529						_this.minRange = date;
530					}
531					_this.selectedDates = [_this.minRange, _this.maxRange]
532
533				} else {
534					_this.selectedDates = [date];
535					_this.minRange = date;
536				}
537			} else {
538				_this.selectedDates = [date];
539			}
540
541			_this._setInputValue();
542
543			if (opts.onSelect) {
544				_this._triggerOnChange();
545			}
546
547			if (opts.autoClose && !this.timepickerIsActive) {
548				if (!opts.multipleDates && !opts.range) {
549					_this.hide();
550				} else if (opts.range && _this.selectedDates.length == 2) {
551					_this.hide();
552				}
553			}
554
555			_this.views[this.currentView]._render()
556		},
557
558		removeDate: function (date) {
559			var selected = this.selectedDates,
560				_this = this;
561
562			if (!(date instanceof Date)) return;
563
564			return selected.some(function (curDate, i) {
565				if (airdatepicker.isSame(curDate, date)) {
566					selected.splice(i, 1);
567
568					if (!_this.selectedDates.length) {
569						_this.minRange = '';
570						_this.maxRange = '';
571						_this.lastSelectedDate = '';
572					} else {
573						_this.lastSelectedDate = _this.selectedDates[_this.selectedDates.length - 1];
574					}
575
576					_this.views[_this.currentView]._render();
577					_this._setInputValue();
578
579					if (_this.opts.onSelect) {
580						_this._triggerOnChange();
581					}
582
583					return true
584				}
585			})
586		},
587
588		today: function () {
589			this.silent = true;
590			this.view = this.opts.minView;
591			this.silent = false;
592			this.date = new Date();
593
594			if (this.opts.todayButton instanceof Date) {
595				this.selectDate(this.opts.todayButton)
596			}
597		},
598
599		clear: function () {
600			this.selectedDates = [];
601			this.minRange = '';
602			this.maxRange = '';
603			this.views[this.currentView]._render();
604			this._setInputValue();
605			if (this.opts.onSelect) {
606				this._triggerOnChange()
607			}
608		},
609
610		/**
611		 * Updates airdatepicker options
612		 * @param {String|Object} param - parameter's name to update. If object then it will extend current options
613		 * @param {String|Number|Object} [value] - new param value
614		 */
615		update: function (param, value) {
616			var len = arguments.length,
617				lastSelectedDate = this.lastSelectedDate;
618
619			if (len == 2) {
620				this.opts[param] = value;
621			} else if (len == 1 && typeof param == 'object') {
622				this.opts = $.extend(true, this.opts, param)
623			}
624
625			this._createShortCuts();
626			this._syncWithMinMaxDates();
627			this._defineLocale(this.opts.language);
628			this.nav._addButtonsIfNeed();
629			if (!this.opts.onlyTimepicker) this.nav._render();
630			this.views[this.currentView]._render();
631
632			if (this.elIsInput && !this.opts.inline) {
633				this._setPositionClasses(this.opts.position);
634				if (this.visible) {
635					this.setPosition(this.opts.position)
636				}
637			}
638
639			if (this.opts.classes) {
640				this.$airdatepicker.addClass(this.opts.classes)
641			}
642
643			if (this.opts.onlyTimepicker) {
644				this.$airdatepicker.addClass('-only-timepicker-');
645			}
646
647			if (this.opts.timepicker) {
648				if (lastSelectedDate) this.timepicker._handleDate(lastSelectedDate);
649				this.timepicker._updateRanges();
650				this.timepicker._updateCurrentTime();
651				// Change hours and minutes if it's values have been changed through min/max hours/minutes
652				if (lastSelectedDate) {
653					lastSelectedDate.setHours(this.timepicker.hours);
654					lastSelectedDate.setMinutes(this.timepicker.minutes);
655				}
656			}
657
658			this._setInputValue();
659
660			return this;
661		},
662
663		_syncWithMinMaxDates: function () {
664			var curTime = this.date.getTime();
665			this.silent = true;
666			if (this.minTime > curTime) {
667				this.date = this.minDate;
668			}
669
670			if (this.maxTime < curTime) {
671				this.date = this.maxDate;
672			}
673			this.silent = false;
674		},
675
676		_isSelected: function (checkDate, cellType) {
677			var res = false;
678			this.selectedDates.some(function (date) {
679				if (airdatepicker.isSame(date, checkDate, cellType)) {
680					res = date;
681					return true;
682				}
683			});
684			return res;
685		},
686
687		_setInputValue: function () {
688			var _this = this,
689				opts = _this.opts,
690				format = _this.loc.dateFormat,
691				altFormat = opts.altFieldDateFormat,
692				value = _this.selectedDates.map(function (date) {
693					return _this.formatDate(format, date)
694				}),
695				altValues;
696
697			if (opts.altField && _this.$altField.length) {
698				altValues = this.selectedDates.map(function (date) {
699					return _this.formatDate(altFormat, date)
700				});
701				altValues = altValues.join(this.opts.multipleDatesSeparator);
702				this.$altField.val(altValues);
703			}
704
705			value = value.join(this.opts.multipleDatesSeparator);
706
707			this.$el.val(value)
708		},
709
710		/**
711		 * Check if date is between minDate and maxDate
712		 * @param date {object} - date object
713		 * @param type {string} - cell type
714		 * @returns {boolean}
715		 * @private
716		 */
717		_isInRange: function (date, type) {
718			var time = date.getTime(),
719				d = airdatepicker.getParsedDate(date),
720				min = airdatepicker.getParsedDate(this.minDate),
721				max = airdatepicker.getParsedDate(this.maxDate),
722				dMinTime = new Date(d.year, d.month, min.date).getTime(),
723				dMaxTime = new Date(d.year, d.month, max.date).getTime(),
724				types = {
725					day: time >= this.minTime && time <= this.maxTime,
726					month: dMinTime >= this.minTime && dMaxTime <= this.maxTime,
727					year: d.year >= min.year && d.year <= max.year
728				};
729			return type ? types[type] : types.day
730		},
731
732		_getDimensions: function ($el) {
733			var offset = $el.offset();
734
735			return {
736				width: $el.outerWidth(),
737				height: $el.outerHeight(),
738				left: offset.left,
739				top: offset.top
740			}
741		},
742
743		_getDateFromCell: function (cell) {
744			var curDate = this.parsedDate,
745				year = cell.data('year') || curDate.year,
746				month = cell.data('month') == undefined ? curDate.month : cell.data('month'),
747				date = cell.data('date') || 1;
748
749			return new Date(year, month, date);
750		},
751
752		_setPositionClasses: function (pos) {
753			pos = pos.split(' ');
754			var main = pos[0],
755				sec = pos[1],
756				classes = 'airdatepicker -' + main + '-' + sec + '- -from-' + main + '-';
757
758			if (this.visible) classes += ' active';
759
760			this.$airdatepicker
761				.removeAttr('class')
762				.addClass(classes);
763		},
764
765		setPosition: function (position) {
766			position = position || this.opts.position;
767
768			var dims = this._getDimensions(this.$el),
769				selfDims = this._getDimensions(this.$airdatepicker),
770				pos = position.split(' '),
771				top, left,
772				offset = this.opts.offset,
773				main = pos[0],
774				secondary = pos[1];
775
776// Modified by halfdata - 2018-10-11 - begin
777			if (this.opts.inline_popup) {
778				dims.left = 0;
779				dims.top = 0;
780			}
781// Modified by halfdata - 2018-10-11 - end
782
783			switch (main) {
784				case 'top':
785					top = dims.top - selfDims.height - offset;
786					break;
787				case 'right':
788					left = dims.left + dims.width + offset;
789					break;
790				case 'bottom':
791					top = dims.top + dims.height + offset;
792					break;
793				case 'left':
794					left = dims.left - selfDims.width - offset;
795					break;
796			}
797
798			switch(secondary) {
799				case 'top':
800					top = dims.top;
801					break;
802				case 'right':
803					left = dims.left + dims.width - selfDims.width;
804					break;
805				case 'bottom':
806					top = dims.top + dims.height - selfDims.height;
807					break;
808				case 'left':
809					left = dims.left;
810					break;
811				case 'center':
812					if (/left|right/.test(main)) {
813						top = dims.top + dims.height/2 - selfDims.height/2;
814					} else {
815						left = dims.left + dims.width/2 - selfDims.width/2;
816					}
817			}
818			
819			this.$airdatepicker
820				.css({
821					left: left,
822					top: top
823				})
824		},
825
826		show: function () {
827			var onShow = this.opts.onShow;
828
829			this.setPosition(this.opts.position);
830			this.$airdatepicker.addClass('active');
831			this.visible = true;
832
833			if (onShow) {
834				this._bindVisionEvents(onShow)
835			}
836		},
837
838		hide: function () {
839			var onHide = this.opts.onHide;
840
841			this.$airdatepicker
842				.removeClass('active')
843				.css({
844					left: '-100000px'
845				});
846
847			this.focused = '';
848			this.keys = [];
849
850			this.inFocus = false;
851			this.visible = false;
852			this.$el.blur();
853
854			if (onHide) {
855				this._bindVisionEvents(onHide)
856			}
857		},
858
859		down: function (date) {
860			this._changeView(date, 'down');
861		},
862
863		up: function (date) {
864			this._changeView(date, 'up');
865		},
866
867		_bindVisionEvents: function (event) {
868			this.$airdatepicker.off('transitionend.dp');
869			event(this, false);
870			this.$airdatepicker.one('transitionend.dp', event.bind(this, this, true))
871		},
872
873		_changeView: function (date, dir) {
874			date = date || this.focused || this.date;
875
876			var nextView = dir == 'up' ? this.viewIndex + 1 : this.viewIndex - 1;
877			if (nextView > 2) nextView = 2;
878			if (nextView < 0) nextView = 0;
879
880			this.silent = true;
881			this.date = new Date(date.getFullYear(), date.getMonth(), 1);
882			this.silent = false;
883			this.view = this.viewIndexes[nextView];
884
885		},
886
887		_handleHotKey: function (key) {
888			var date = airdatepicker.getParsedDate(this._getFocusedDate()),
889				focusedParsed,
890				o = this.opts,
891				newDate,
892				totalDaysInNextMonth,
893				monthChanged = false,
894				yearChanged = false,
895				decadeChanged = false,
896				y = date.year,
897				m = date.month,
898				d = date.date;
899
900			switch (key) {
901				case 'ctrlRight':
902				case 'ctrlUp':
903					m += 1;
904					monthChanged = true;
905					break;
906				case 'ctrlLeft':
907				case 'ctrlDown':
908					m -= 1;
909					monthChanged = true;
910					break;
911				case 'shiftRight':
912				case 'shiftUp':
913					yearChanged = true;
914					y += 1;
915					break;
916				case 'shiftLeft':
917				case 'shiftDown':
918					yearChanged = true;
919					y -= 1;
920					break;
921				case 'altRight':
922				case 'altUp':
923					decadeChanged = true;
924					y += 10;
925					break;
926				case 'altLeft':
927				case 'altDown':
928					decadeChanged = true;
929					y -= 10;
930					break;
931				case 'ctrlShiftUp':
932					this.up();
933					break;
934			}
935
936			totalDaysInNextMonth = airdatepicker.getDaysCount(new Date(y,m));
937			newDate = new Date(y,m,d);
938
939			// If next month has less days than current, set date to total days in that month
940			if (totalDaysInNextMonth < d) d = totalDaysInNextMonth;
941
942			// Check if newDate is in valid range
943			if (newDate.getTime() < this.minTime) {
944				newDate = this.minDate;
945			} else if (newDate.getTime() > this.maxTime) {
946				newDate = this.maxDate;
947			}
948
949			this.focused = newDate;
950
951			focusedParsed = airdatepicker.getParsedDate(newDate);
952			if (monthChanged && o.onChangeMonth) {
953				o.onChangeMonth(focusedParsed.month, focusedParsed.year)
954			}
955			if (yearChanged && o.onChangeYear) {
956				o.onChangeYear(focusedParsed.year)
957			}
958			if (decadeChanged && o.onChangeDecade) {
959				o.onChangeDecade(this.curDecade)
960			}
961		},
962
963		_registerKey: function (key) {
964			var exists = this.keys.some(function (curKey) {
965				return curKey == key;
966			});
967
968			if (!exists) {
969				this.keys.push(key)
970			}
971		},
972
973		_unRegisterKey: function (key) {
974			var index = this.keys.indexOf(key);
975
976			this.keys.splice(index, 1);
977		},
978
979		_isHotKeyPressed: function () {
980			var currentHotKey,
981				found = false,
982				_this = this,
983				pressedKeys = this.keys.sort();
984
985			for (var hotKey in hotKeys) {
986				currentHotKey = hotKeys[hotKey];
987				if (pressedKeys.length != currentHotKey.length) continue;
988
989				if (currentHotKey.every(function (key, i) { return key == pressedKeys[i]})) {
990					_this._trigger('hotKey', hotKey);
991					found = true;
992				}
993			}
994
995			return found;
996		},
997
998		_trigger: function (event, args) {
999			this.$el.trigger(event, args)
1000		},
1001
1002		_focusNextCell: function (keyCode, type) {
1003			type = type || this.cellType;
1004
1005			var date = airdatepicker.getParsedDate(this._getFocusedDate()),
1006				y = date.year,
1007				m = date.month,
1008				d = date.date;
1009
1010			if (this._isHotKeyPressed()){
1011				return;
1012			}
1013
1014			switch(keyCode) {
1015				case 37: // left
1016					type == 'day' ? (d -= 1) : '';
1017					type == 'month' ? (m -= 1) : '';
1018					type == 'year' ? (y -= 1) : '';
1019					break;
1020				case 38: // up
1021					type == 'day' ? (d -= 7) : '';
1022					type == 'month' ? (m -= 3) : '';
1023					type == 'year' ? (y -= 4) : '';
1024					break;
1025				case 39: // right
1026					type == 'day' ? (d += 1) : '';
1027					type == 'month' ? (m += 1) : '';
1028					type == 'year' ? (y += 1) : '';
1029					break;
1030				case 40: // down
1031					type == 'day' ? (d += 7) : '';
1032					type == 'month' ? (m += 3) : '';
1033					type == 'year' ? (y += 4) : '';
1034					break;
1035			}
1036
1037			var nd = new Date(y,m,d);
1038			if (nd.getTime() < this.minTime) {
1039				nd = this.minDate;
1040			} else if (nd.getTime() > this.maxTime) {
1041				nd = this.maxDate;
1042			}
1043
1044			this.focused = nd;
1045
1046		},
1047
1048		_getFocusedDate: function () {
1049			var focused  = this.focused || this.selectedDates[this.selectedDates.length - 1],
1050				d = this.parsedDate;
1051
1052			if (!focused) {
1053				switch (this.view) {
1054					case 'days':
1055						focused = new Date(d.year, d.month, new Date().getDate());
1056						break;
1057					case 'months':
1058						focused = new Date(d.year, d.month, 1);
1059						break;
1060					case 'years':
1061						focused = new Date(d.year, 0, 1);
1062						break;
1063				}
1064			}
1065
1066			return focused;
1067		},
1068
1069		_getCell: function (date, type) {
1070			type = type || this.cellType;
1071
1072			var d = airdatepicker.getParsedDate(date),
1073				selector = '.airdatepicker--cell[data-year="' + d.year + '"]',
1074				$cell;
1075
1076			switch (type) {
1077				case 'month':
1078					selector = '[data-month="' + d.month + '"]';
1079					break;
1080				case 'day':
1081					selector += '[data-month="' + d.month + '"][data-date="' + d.date + '"]';
1082					break;
1083			}
1084			$cell = this.views[this.currentView].$el.find(selector);
1085
1086			return $cell.length ? $cell : $('');
1087		},
1088
1089		destroy: function () {
1090			var _this = this;
1091			_this.$el
1092				.off('.adp')
1093				.data('airdatepicker', '');
1094
1095			_this.selectedDates = [];
1096			_this.focused = '';
1097			_this.views = {};
1098			_this.keys = [];
1099			_this.minRange = '';
1100			_this.maxRange = '';
1101
1102			if (_this.opts.inline || !_this.elIsInput) {
1103				_this.$airdatepicker.closest('.airdatepicker-inline').remove();
1104			} else {
1105				_this.$airdatepicker.remove();
1106			}
1107		},
1108
1109		_handleAlreadySelectedDates: function (alreadySelected, selectedDate) {
1110			if (this.opts.range) {
1111				if (!this.opts.toggleSelected) {
1112					// Add possibility to select same date when range is true
1113					if (this.selectedDates.length != 2) {
1114						this._trigger('clickCell', selectedDate);
1115					}
1116				} else {
1117					this.removeDate(selectedDate);
1118				}
1119			} else if (this.opts.toggleSelected){
1120				this.removeDate(selectedDate);
1121			}
1122
1123			// Change last selected date to be able to change time when clicking on this cell
1124			if (!this.opts.toggleSelected) {
1125				this.lastSelectedDate = alreadySelected;
1126				if (this.opts.timepicker) {
1127					this.timepicker._setTime(alreadySelected);
1128					this.timepicker.update();
1129				}
1130			}
1131		},
1132
1133		_onShowEvent: function (e) {
1134			if (!this.visible) {
1135				this.show();
1136			}
1137		},
1138
1139		_onBlur: function () {
1140			if (!this.inFocus && this.visible) {
1141				this.hide();
1142			}
1143		},
1144
1145		_onMouseDownDatepicker: function (e) {
1146			this.inFocus = true;
1147		},
1148
1149		_onMouseUpDatepicker: function (e) {
1150			this.inFocus = false;
1151			e.originalEvent.inFocus = true;
1152			if (!e.originalEvent.timepickerFocus) this.$el.focus();
1153		},
1154
1155		_onKeyUpGeneral: function (e) {
1156			var val = this.$el.val();
1157
1158			if (!val) {
1159				this.clear();
1160			}
1161		},
1162
1163		_onResize: function () {
1164			if (this.visible) {
1165				this.setPosition();
1166			}
1167		},
1168
1169		_onMouseUpBody: function (e) {
1170			if (e.originalEvent.inFocus) return;
1171
1172			if (this.visible && !this.inFocus) {
1173				this.hide();
1174			}
1175		},
1176
1177		_onMouseUpEl: function (e) {
1178			e.originalEvent.inFocus = true;
1179			setTimeout(this._onKeyUpGeneral.bind(this),4);
1180		},
1181
1182		_onKeyDown: function (e) {
1183			var code = e.which;
1184			this._registerKey(code);
1185
1186			// Arrows
1187			if (code >= 37 && code <= 40) {
1188				e.preventDefault();
1189				this._focusNextCell(code);
1190			}
1191
1192			// Enter
1193			if (code == 13) {
1194				if (this.focused) {
1195					if (this._getCell(this.focused).hasClass('-disabled-')) return;
1196					if (this.view != this.opts.minView) {
1197						this.down()
1198					} else {
1199						var alreadySelected = this._isSelected(this.focused, this.cellType);
1200
1201						if (!alreadySelected) {
1202							if (this.timepicker) {
1203								this.focused.setHours(this.timepicker.hours);
1204								this.focused.setMinutes(this.timepicker.minutes);
1205							}
1206							this.selectDate(this.focused);
1207							return;
1208						}
1209						this._handleAlreadySelectedDates(alreadySelected, this.focused)
1210					}
1211				}
1212			}
1213
1214			// Esc
1215			if (code == 27) {
1216				this.hide();
1217			}
1218		},
1219
1220		_onKeyUp: function (e) {
1221			var code = e.which;
1222			this._unRegisterKey(code);
1223		},
1224
1225		_onHotKey: function (e, hotKey) {
1226			this._handleHotKey(hotKey);
1227		},
1228
1229		_onMouseEnterCell: function (e) {
1230			var $cell = $(e.target).closest('.airdatepicker--cell'),
1231				date = this._getDateFromCell($cell);
1232
1233			// Prevent from unnecessary rendering and setting new currentDate
1234			this.silent = true;
1235
1236			if (this.focused) {
1237				this.focused = ''
1238			}
1239
1240			$cell.addClass('-focus-');
1241
1242			this.focused = date;
1243			this.silent = false;
1244
1245			if (this.opts.range && this.selectedDates.length == 1) {
1246				this.minRange = this.selectedDates[0];
1247				this.maxRange = '';
1248				if (airdatepicker.less(this.minRange, this.focused)) {
1249					this.maxRange = this.minRange;
1250					this.minRange = '';
1251				}
1252				this.views[this.currentView]._update();
1253			}
1254		},
1255
1256		_onMouseLeaveCell: function (e) {
1257			var $cell = $(e.target).closest('.airdatepicker--cell');
1258
1259			$cell.removeClass('-focus-');
1260
1261			this.silent = true;
1262			this.focused = '';
1263			this.silent = false;
1264		},
1265
1266		_onTimeChange: function (e, h, m) {
1267			var date = new Date(),
1268				selectedDates = this.selectedDates,
1269				selected = false;
1270
1271			if (selectedDates.length) {
1272				selected = true;
1273				date = this.lastSelectedDate;
1274			}
1275
1276			date.setHours(h);
1277			date.setMinutes(m);
1278
1279			if (!selected && !this._getCell(date).hasClass('-disabled-')) {
1280				this.selectDate(date);
1281			} else {
1282				this._setInputValue();
1283				if (this.opts.onSelect) {
1284					this._triggerOnChange();
1285				}
1286			}
1287		},
1288
1289		_onClickCell: function (e, date) {
1290			if (this.timepicker) {
1291				date.setHours(this.timepicker.hours);
1292				date.setMinutes(this.timepicker.minutes);
1293			}
1294			this.selectDate(date);
1295		},
1296
1297		set focused(val) {
1298			if (!val && this.focused) {
1299				var $cell = this._getCell(this.focused);
1300
1301				if ($cell.length) {
1302					$cell.removeClass('-focus-')
1303				}
1304			}
1305			this._focused = val;
1306			if (this.opts.range && this.selectedDates.length == 1) {
1307				this.minRange = this.selectedDates[0];
1308				this.maxRange = '';
1309				if (airdatepicker.less(this.minRange, this._focused)) {
1310					this.maxRange = this.minRange;
1311					this.minRange = '';
1312				}
1313			}
1314			if (this.silent) return;
1315			this.date = val;
1316		},
1317
1318		get focused() {
1319			return this._focused;
1320		},
1321
1322		get parsedDate() {
1323			return airdatepicker.getParsedDate(this.date);
1324		},
1325
1326		set date (val) {
1327			if (!(val instanceof Date)) return;
1328
1329			this.currentDate = val;
1330
1331			if (this.inited && !this.silent) {
1332				this.views[this.view]._render();
1333				this.nav._render();
1334				if (this.visible && this.elIsInput) {
1335					this.setPosition();
1336				}
1337			}
1338			return val;
1339		},
1340
1341		get date () {
1342			return this.currentDate
1343		},
1344
1345		set view (val) {
1346			this.viewIndex = this.viewIndexes.indexOf(val);
1347
1348			if (this.viewIndex < 0) {
1349				return;
1350			}
1351
1352			this.prevView = this.currentView;
1353			this.currentView = val;
1354
1355			if (this.inited) {
1356				if (!this.views[val]) {
1357					this.views[val] = new  $.fn.airdatepicker.Body(this, val, this.opts)
1358				} else {
1359					this.views[val]._render();
1360				}
1361
1362				this.views[this.prevView].hide();
1363				this.views[val].show();
1364				this.nav._render();
1365
1366				if (this.opts.onChangeView) {
1367					this.opts.onChangeView(val)
1368				}
1369				if (this.elIsInput && this.visible) this.setPosition();
1370			}
1371
1372			return val
1373		},
1374
1375		get view() {
1376			return this.currentView;
1377		},
1378
1379		get cellType() {
1380			return this.view.substring(0, this.view.length - 1)
1381		},
1382
1383		get minTime() {
1384			var min = airdatepicker.getParsedDate(this.minDate);
1385			return new Date(min.year, min.month, min.date).getTime()
1386		},
1387
1388		get maxTime() {
1389			var max = airdatepicker.getParsedDate(this.maxDate);
1390			return new Date(max.year, max.month, max.date).getTime()
1391		},
1392
1393		get curDecade() {
1394			return airdatepicker.getDecade(this.date)
1395		}
1396	};
1397
1398	//  Utils
1399	// -------------------------------------------------
1400
1401	airdatepicker.getDaysCount = function (date) {
1402		return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
1403	};
1404
1405	airdatepicker.getParsedDate = function (date) {
1406		return {
1407			year: date.getFullYear(),
1408			month: date.getMonth(),
1409			fullMonth: (date.getMonth() + 1) < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1, // One based
1410			date: date.getDate(),
1411			fullDate: date.getDate() < 10 ? '0' + date.getDate() : date.getDate(),
1412			day: date.getDay(),
1413			hours: date.getHours(),
1414			fullHours:  date.getHours() < 10 ? '0' + date.getHours() :  date.getHours() ,
1415			minutes: date.getMinutes(),
1416			fullMinutes:  date.getMinutes() < 10 ? '0' + date.getMinutes() :  date.getMinutes()
1417		}
1418	};
1419
1420	airdatepicker.getDecade = function (date) {
1421		var firstYear = Math.floor(date.getFullYear() / 10) * 10;
1422
1423		return [firstYear, firstYear + 9];
1424	};
1425
1426	airdatepicker.template = function (str, data) {
1427		return str.replace(/#\{([\w]+)\}/g, function (source, match) {
1428			if (data[match] || data[match] === 0) {
1429				return data[match]
1430			}
1431		});
1432	};
1433
1434	airdatepicker.isSame = function (date1, date2, type) {
1435		if (!date1 || !date2) return false;
1436		var d1 = airdatepicker.getParsedDate(date1),
1437			d2 = airdatepicker.getParsedDate(date2),
1438			_type = type ? type : 'day',
1439
1440			conditions = {
1441				day: d1.date == d2.date && d1.month == d2.month && d1.year == d2.year,
1442				month: d1.month == d2.month && d1.year == d2.year,
1443				year: d1.year == d2.year
1444			};
1445
1446		return conditions[_type];
1447	};
1448
1449	airdatepicker.less = function (dateCompareTo, date, type) {
1450		if (!dateCompareTo || !date) return false;
1451		return date.getTime() < dateCompareTo.getTime();
1452	};
1453
1454	airdatepicker.bigger = function (dateCompareTo, date, type) {
1455		if (!dateCompareTo || !date) return false;
1456		return date.getTime() > dateCompareTo.getTime();
1457	};
1458
1459	airdatepicker.getLeadingZeroNum = function (num) {
1460		return parseInt(num) < 10 ? '0' + num : num;
1461	};
1462
1463	/**
1464	 * Returns copy of date with hours and minutes equals to 0
1465	 * @param date {Date}
1466	 */
1467	airdatepicker.resetTime = function (date) {
1468		if (typeof date != 'object') return;
1469		date = airdatepicker.getParsedDate(date);
1470		return new Date(date.year, date.month, date.date)
1471	};
1472
1473	$.fn.airdatepicker = function ( options ) {
1474		return this.each(function () {
1475			if (!$.data(this, pluginName)) {
1476				$.data(this,  pluginName,
1477					new Datepicker( this, options ));
1478			} else {
1479				var _this = $.data(this, pluginName);
1480
1481				_this.opts = $.extend(true, _this.opts, options);
1482				_this.update();
1483			}
1484		});
1485	};
1486
1487	$.fn.airdatepicker.Constructor = Datepicker;
1488
1489	$.fn.airdatepicker.language = {
1490		en: {
1491			days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
1492			daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
1493			daysMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
1494			months: ['January','February','March','April','May','June', 'July','August','September','October','November','December'],
1495			monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
1496			today: 'Today',
1497			clear: 'Clear',
1498			dateFormat: 'mm/dd/yyyy',
1499			timeFormat: 'hh:ii aa',
1500			firstDay: 0
1501		},
1502		cs: {
1503			days: ['Neděle', 'Pondělí', 'Úterý', 'Středa', 'Čtvrtek', 'Pátek', 'Sobota'],
1504			daysShort: ['Ne', 'Po', 'Út', 'St', 'Čt', 'Pá', 'So'],
1505			daysMin: ['Ne', 'Po', 'Út', 'St', 'Čt', 'Pá', 'So'],
1506			months: ['Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen', 'Červenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec'],
1507			monthsShort: ['Led', 'Úno', 'Bře', 'Dub', 'Kvě', 'Čvn', 'Čvc', 'Srp', 'Zář', 'Říj', 'Lis', 'Pro'],
1508			today: 'Dnes',
1509			clear: 'Vymazat',
1510			dateFormat: 'dd.mm.yyyy',
1511			timeFormat: 'hh:ii',
1512			firstDay: 1
1513		},
1514		da: {
1515			days: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'],
1516			daysShort: ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'],
1517			daysMin: ['Sø', 'Ma', 'Ti', 'On', 'To', 'Fr', 'Lø'],
1518			months: ['Januar','Februar','Marts','April','Maj','Juni', 'Juli','August','September','Oktober','November','December'],
1519			monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
1520			today: 'I dag',
1521			clear: 'Nulstil',
1522			dateFormat: 'dd/mm/yyyy',
1523			timeFormat: 'hh:ii',
1524			firstDay: 1
1525		},
1526		de: {
1527			days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
1528			daysShort: ['Son', 'Mon', 'Die', 'Mit', 'Don', 'Fre', 'Sam'],
1529			daysMin: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
1530			months: ['Januar','Februar','März','April','Mai','Juni', 'Juli','August','September','Oktober','November','Dezember'],
1531			monthsShort: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
1532			today: 'Heute',
1533			clear: 'Aufräumen',
1534			dateFormat: 'dd.mm.yyyy',
1535			timeFormat: 'hh:ii',
1536			firstDay: 1
1537		},
1538		es: {
1539			days: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
1540			daysShort: ['Dom', 'Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab'],
1541			daysMin: ['Do', 'Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa'],
1542			months: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Augosto','Septiembre','Octubre','Noviembre','Diciembre'],
1543			monthsShort: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
1544			today: 'Hoy',
1545			clear: 'Limpiar',
1546			dateFormat: 'dd/mm/yyyy',
1547			timeFormat: 'hh:ii aa',
1548			firstDay: 1
1549		},
1550		fi: {
1551			days: ['Sunnuntai', 'Maanantai', 'Tiistai', 'Keskiviikko', 'Torstai', 'Perjantai', 'Lauantai'],
1552			daysShort: ['Su', 'Ma', 'Ti', 'Ke', 'To', 'Pe', 'La'],
1553			daysMin: ['Su', 'Ma', 'Ti', 'Ke', 'To', 'Pe', 'La'],
1554			months: ['Tammikuu','Helmikuu','Maaliskuu','Huhtikuu','Toukokuu','Kesäkuu', 'Heinäkuu','Elokuu','Syyskuu','Lokakuu','Marraskuu','Joulukuu'],
1555			monthsShort: ['Tammi', 'Helmi', 'Maalis', 'Huhti', 'Touko', 'Kesä', 'Heinä', 'Elo', 'Syys', 'L
1555oka', 'Marras', 'Joulu'],
1556			today: 'Tänään',
1557			clear: 'Tyhjennä',
1558			dateFormat: 'dd.mm.yyyy',
1559			timeFormat: 'hh:ii',
1560			firstDay: 1
1561		},
1562		fr: {
1563			days: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
1564			daysShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
1565			daysMin: ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'],
1566			months: ['Janvier','Février','Mars','Avril','Mai','Juin', 'Juillet','Août','Septembre','Octobre','Novembre','Decembre'],
1567			monthsShort: ['Jan', 'Fév', 'Mars', 'Avr', 'Mai', 'Juin', 'Juil', 'Août', 'Sep', 'Oct', 'Nov', 'Dec'],
1568			today: "Aujourd'hui",
1569			clear: 'Effacer',
1570			dateFormat: 'dd/mm/yyyy',
1571			timeFormat: 'hh:ii',
1572			firstDay: 1
1573		},
1574		hu: {
1575			days: ['Vasárnap', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat'],
1576			daysShort: ['Va', 'Hé', 'Ke', 'Sze', 'Cs', 'Pé', 'Szo'],
1577			daysMin: ['V', 'H', 'K', 'Sz', 'Cs', 'P', 'Sz'],
1578			months: ['Január', 'Február', 'Március', 'Április', 'Május', 'Június', 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December'],
1579			monthsShort: ['Jan', 'Feb', 'Már', 'Ápr', 'Máj', 'Jún', 'Júl', 'Aug', 'Szep', 'Okt', 'Nov', 'Dec'],
1580			today: 'Ma',
1581			clear: 'Törlés',
1582			dateFormat: 'yyyy-mm-dd',
1583			timeFormat: 'hh:ii aa',
1584			firstDay: 1
1585		},
1586		it: {
1587			days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'],
1588			daysShort: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'],
1589			daysMin: ['Do', 'Lu', 'Ma', 'Me', 'Gi', 'Ve', 'Sa'],
1590			months: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
1591			monthsShort: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'],
1592			today: 'Oggi',
1593			clear: 'Pulisci',
1594			dateFormat: 'dd/mm/yyyy',
1595			timeFormat: 'hh:ii aa',
1596			firstDay: 1
1597		},
1598		nl: {
1599			days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
1600			daysShort: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
1601			daysMin: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
1602			months: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
1603			monthsShort: ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
1604			today: 'Vandaag',
1605			clear: 'Legen',
1606			dateFormat: 'dd-mm-yyyy',
1607			timeFormat: 'hh:ii',
1608			firstDay: 1
1609		},
1610		pl: {
1611			days: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'],
1612			daysShort: ['Nie', 'Pon', 'Wto', 'Śro', 'Czw', 'Pią', 'Sob'],
1613			daysMin: ['Nd', 'Pn', 'Wt', 'Śr', 'Czw', 'Pt', 'So'],
1614			months: ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec', 'Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień'],
1615			monthsShort: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
1616			today: 'Dzisiaj',
1617			clear: 'Wyczyść',
1618			dateFormat: 'yyyy-mm-dd',
1619			timeFormat: 'hh:ii aa',
1620			firstDay: 1
1621		},
1622		pt: {
1623			days: ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado'],
1624			daysShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'],
1625			daysMin: ['Do', 'Se', 'Te', 'Qa', 'Qi', 'Sx', 'Sa'],
1626			months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
1627			monthsShort: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
1628			today: 'Hoje',
1629			clear: 'Limpar',
1630			dateFormat: 'dd/mm/yyyy',
1631			timeFormat: 'hh:ii',
1632			firstDay: 1
1633		},
1634		ro: {
1635			days: ['Duminică', 'Luni', 'Marţi', 'Miercuri', 'Joi', 'Vineri', 'Sâmbătă'],
1636			daysShort: ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
1637			daysMin: ['D', 'L', 'Ma', 'Mi', 'J', 'V', 'S'],
1638			months: ['Ianuarie','Februarie','Martie','Aprilie','Mai','Iunie','Iulie','August','Septembrie','Octombrie','Noiembrie','Decembrie'],
1639			monthsShort: ['Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', 'Iul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'],
1640			today: 'Azi',
1641			clear: 'Şterge',
1642			dateFormat: 'dd.mm.yyyy',
1643			timeFormat: 'hh:ii',
1644			firstDay: 1
1645		},
1646		ru: {
1647			days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
1648			daysShort: ['Вос','Пон','Вто','Сре','Чет','Пят','Суб'],
1649			daysMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'],
1650			months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
1651			monthsShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
1652			today: 'Сегодня',
1653			clear: 'Очистить',
1654			dateFormat: 'dd.mm.yyyy',
1655			timeFormat: 'hh:ii',
1656			firstDay: 1
1657		},
1658		sk: {
1659			days: ['Nedeľa', 'Pondelok', 'Utorok', 'Streda', 'Štvrtok', 'Piatok', 'Sobota'],
1660			daysShort: ['Ned', 'Pon', 'Uto', 'Str', 'Å tv', 'Pia', 'Sob'],
1661			daysMin: ['Ne', 'Po', 'Ut', 'St', 'Å t', 'Pi', 'So'],
1662			months: ['Január','Február','Marec','Apríl','Máj','Jún', 'Júl','August','September','Október','November','December'],
1663			monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'Máj', 'Jún', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
1664			today: 'Dnes',
1665			clear: 'Vymazať',
1666			dateFormat: 'dd.mm.yyyy',
1667			timeFormat: 'hh:ii',
1668			firstDay: 1
1669		},
1670		tr: {
1671			days: ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'],
1672			daysShort: ['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt'],
1673			daysMin: ['Pz', 'Pt', 'Sa', 'Ça', 'Pe', 'Cu', 'Ct'],
1674			months: ['Ocak','Şubat','Mart','Nisan','Mayıs','Haziran', 'Temmuz','Ağustos','Eylül','Ekim','Kasım','Aralık'],
1675			monthsShort: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
1676			today: 'Bugün',
1677			clear: 'Temizle',
1678			dateFormat: 'dd.mm.yyyy',
1679			timeFormat: 'hh:ii',
1680			firstDay: 1
1681		},
1682		zh: {
1683			days: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
1684			daysShort: ['日', '一', '二', '三', '四', '五', '六'],
1685			daysMin: ['日', '一', '二', '三', '四', '五', '六'],
1686			months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
1687			monthsShort: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
1688			today: '今天',
1689			clear: '清除',
1690			dateFormat: 'yyyy-mm-dd',
1691			timeFormat: 'hh:ii',
1692			firstDay: 1
1693		}
1694	};
1695
1696	$(function () {
1697		$(autoInitSelector).airdatepicker();
1698	})
1699
1700})();
1701
1702;(function () {
1703	var templates = {
1704		days:'' +
1705		'<div class="airdatepicker--days airdatepicker--body">' +
1706		'<div class="airdatepicker--days-names"></div>' +
1707		'<div class="airdatepicker--cells airdatepicker--cells-days"></div>' +
1708		'</div>',
1709		months: '' +
1710		'<div class="airdatepicker--months airdatepicker--body">' +
1711		'<div class="airdatepicker--cells airdatepicker--cells-months"></div>' +
1712		'</div>',
1713		years: '' +
1714		'<div class="airdatepicker--years airdatepicker--body">' +
1715		'<div class="airdatepicker--cells airdatepicker--cells-years"></div>' +
1716		'</div>'
1717		},
1718		airdatepicker = $.fn.airdatepicker,
1719		dp = airdatepicker.Constructor;
1720
1721	airdatepicker.Body = function (d, type, opts) {
1722		this.d = d;
1723		this.type = type;
1724		this.opts = opts;
1725		this.$el = $('');
1726
1727		if (this.opts.onlyTimepicker) return;
1728		this.init();
1729	};
1730
1731	airdatepicker.Body.prototype = {
1732		init: function () {
1733			this._buildBaseHtml();
1734			this._render();
1735
1736			this._bindEvents();
1737		},
1738
1739		_bindEvents: function () {
1740			this.$el.on('click', '.airdatepicker--cell', $.proxy(this._onClickCell, this));
1741		},
1742
1743		_buildBaseHtml: function () {
1744			this.$el = $(templates[this.type]).appendTo(this.d.$content);
1745			this.$names = $('.airdatepicker--days-names', this.$el);
1746			this.$cells = $('.airdatepicker--cells', this.$el);
1747		},
1748
1749		_getDayNamesHtml: function (firstDay, curDay, html, i) {
1750			curDay = curDay != undefined ? curDay : firstDay;
1751			html = html ? html : '';
1752			i = i != undefined ? i : 0;
1753
1754			if (i > 7) return html;
1755			if (curDay == 7) return this._getDayNamesHtml(firstDay, 0, html, ++i);
1756
1757			html += '<div class="airdatepicker--day-name' + (this.d.isWeekend(curDay) ? " -weekend-" : "") + '">' + this.d.loc.daysMin[curDay] + '</div>';
1758
1759			return this._getDayNamesHtml(firstDay, ++curDay, html, ++i);
1760		},
1761
1762		_getCellContents: function (date, type) {
1763			var classes = "airdatepicker--cell airdatepicker--cell-" + type,
1764				currentDate = new Date(),
1765				parent = this.d,
1766				minRange = dp.resetTime(parent.minRange),
1767				maxRange = dp.resetTime(parent.maxRange),
1768				opts = parent.opts,
1769				d = dp.getParsedDate(date),
1770				render = {},
1771				html = d.date;
1772
1773			switch (type) {
1774				case 'day':
1775					if (parent.isWeekend(d.day)) classes += " -weekend-";
1776					if (d.month != this.d.parsedDate.month) {
1777						classes += " -other-month-";
1778						if (!opts.selectOtherMonths) {
1779							classes += " -disabled-";
1780						}
1781						if (!opts.showOtherMonths) html = '';
1782					}
1783					break;
1784				case 'month':
1785					html = parent.loc[parent.opts.monthsField][d.month];
1786					break;
1787				case 'year':
1788					var decade = parent.curDecade;
1789					html = d.year;
1790					if (d.year < decade[0] || d.year > decade[1]) {
1791						classes += ' -other-decade-';
1792						if (!opts.selectOtherYears) {
1793							classes += " -disabled-";
1794						}
1795						if (!opts.showOtherYears) html = '';
1796					}
1797					break;
1798			}
1799
1800			if (opts.onRenderCell) {
1801				render = opts.onRenderCell(date, type) || {};
1802				html = render.html ? render.html : html;
1803				classes += render.classes ? ' ' + render.classes : '';
1804			}
1805
1806			if (opts.range) {
1807				if (dp.isSame(minRange, date, type)) classes += ' -range-from-';
1808				if (dp.isSame(maxRange, date, type)) classes += ' -range-to-';
1809
1810				if (parent.selectedDates.length == 1 && parent.focused) {
1811					if (
1812						(dp.bigger(minRange, date) && dp.less(parent.focused, date)) ||
1813						(dp.less(maxRange, date) && dp.bigger(parent.focused, date)))
1814					{
1815						classes += ' -in-range-'
1816					}
1817
1818					if (dp.less(maxRange, date) && dp.isSame(parent.focused, date)) {
1819						classes += ' -range-from-'
1820					}
1821					if (dp.bigger(minRange, date) && dp.isSame(parent.focused, date)) {
1822						classes += ' -range-to-'
1823					}
1824
1825				} else if (parent.selectedDates.length == 2) {
1826					if (dp.bigger(minRange, date) && dp.less(maxRange, date)) {
1827						classes += ' -in-range-'
1828					}
1829				}
1830			}
1831
1832
1833			if (dp.isSame(currentDate, date, type)) classes += ' -current-';
1834			if (parent.focused && dp.isSame(date, parent.focused, type)) classes += ' -focus-';
1835			if (parent._isSelected(date, type)) classes += ' -selected-';
1836			if (!parent._isInRange(date, type) || render.disabled) classes += ' -disabled-';
1837
1838			return {
1839				html: html,
1840				classes: classes
1841			}
1842		},
1843
1844		/**
1845		 * Calculates days number to render. Generates days html and returns it.
1846		 * @param {object} date - Date object
1847		 * @returns {string}
1848		 * @private
1849		 */
1850		_getDaysHtml: function (date) {
1851			var totalMonthDays = dp.getDaysCount(date),
1852				firstMonthDay = new Date(date.getFullYear(), date.getMonth(), 1).getDay(),
1853				lastMonthDay = new Date(date.getFullYear(), date.getMonth(), totalMonthDays).getDay(),
1854				daysFromPevMonth = firstMonthDay - this.d.loc.firstDay,
1855				daysFromNextMonth = 6 - lastMonthDay + this.d.loc.firstDay;
1856
1857			daysFromPevMonth = daysFromPevMonth < 0 ? daysFromPevMonth + 7 : daysFromPevMonth;
1858			daysFromNextMonth = daysFromNextMonth > 6 ? daysFromNextMonth - 7 : daysFromNextMonth;
1859
1860			var startDayIndex = -daysFromPevMonth + 1,
1861				m, y,
1862				html = '';
1863
1864			for (var i = startDayIndex, max = totalMonthDays + daysFromNextMonth; i <= max; i++) {
1865				y = date.getFullYear();
1866				m = date.getMonth();
1867
1868				html += this._getDayHtml(new Date(y, m, i))
1869			}
1870
1871			return html;
1872		},
1873
1874		_getDayHtml: function (date) {
1875		   var content = this._getCellContents(date, 'day');
1876
1877			return '<div class="' + content.classes + '" ' +
1878				'data-date="' + date.getDate() + '" ' +
1879				'data-month="' + date.getMonth() + '" ' +
1880				'data-year="' + date.getFullYear() + '">' + content.html + '</div>';
1881		},
1882
1883		/**
1884		 * Generates months html
1885		 * @param {object} date - date instance
1886		 * @returns {string}
1887		 * @private
1888		 */
1889		_getMonthsHtml: function (date) {
1890			var html = '',
1891				d = dp.getParsedDate(date),
1892				i = 0;
1893
1894			while(i < 12) {
1895				html += this._getMonthHtml(new Date(d.year, i));
1896				i++
1897			}
1898
1899			return html;
1900		},
1901
1902		_getMonthHtml: function (date) {
1903			var content = this._getCellContents(date, 'month');
1904
1905			return '<div class="' + content.classes + '" data-month="' + date.getMonth() + '">' + content.html + '</div>'
1906		},
1907
1908		_getYearsHtml: function (date) {
1909			var d = dp.getParsedDate(date),
1910				decade = dp.getDecade(date),
1911				firstYear = decade[0] - 1,
1912				html = '',
1913				i = firstYear;
1914
1915			for (i; i <= decade[1] + 1; i++) {
1916				html += this._getYearHtml(new Date(i , 0));
1917			}
1918
1919			return html;
1920		},
1921
1922		_getYearHtml: function (date) {
1923			var content = this._getCellContents(date, 'year');
1924
1925			return '<div class="' + content.classes + '" data-year="' + date.getFullYear() + '">' + content.html + '</div>'
1926		},
1927
1928		_renderTypes: {
1929			days: function () {
1930				var dayNames = this._getDayNamesHtml(this.d.loc.firstDay),
1931					days = this._getDaysHtml(this.d.currentDate);
1932
1933				this.$cells.html(days);
1934				this.$names.html(dayNames)
1935			},
1936			months: function () {
1937				var html = this._getMonthsHtml(this.d.currentDate);
1938
1939				this.$cells.html(html)
1940			},
1941			years: function () {
1942				var html = this._getYearsHtml(this.d.currentDate);
1943
1944				this.$cells.html(html)
1945			}
1946		},
1947
1948		_render: function () {
1949			if (this.opts.onlyTimepicker) return;
1950			this._renderTypes[this.type].bind(this)();
1951		},
1952
1953		_update: function () {
1954			var $cells = $('.airdatepicker--cell', this.$cells),
1955				_this = this,
1956				classes,
1957				$cell,
1958				date;
1959			$cells.each(function (cell, i) {
1960				$cell = $(this);
1961				date = _this.d._getDateFromCell($(this));
1962				classes = _this._getCellContents(date, _this.d.cellType);
1963				$cell.attr('class',classes.classes)
1964			});
1965		},
1966
1967		show: function () {
1968			if (this.opts.onlyTimepicker) return;
1969			this.$el.addClass('active');
1970			this.acitve = true;
1971		},
1972
1973		hide: function () {
1974			this.$el.removeClass('active');
1975			this.active = false;
1976		},
1977
1978		//  Events
1979		// -------------------------------------------------
1980
1981		_handleClick: function (el) {
1982			var date = el.data('date') || 1,
1983				month = el.data('month') || 0,
1984				year = el.data('year') || this.d.parsedDate.year,
1985				dp = this.d;
1986			// Change view if min view does not reach yet
1987			if (dp.view != this.opts.minView) {
1988				dp.down(new Date(year, month, date));
1989				return;
1990			}
1991			// Select date if min view is reached
1992			var selectedDate = new Date(year, month, date),
1993				alreadySelected = this.d._isSelected(selectedDate, this.d.cellType);
1994
1995			if (!alreadySelected) {
1996				dp._trigger('clickCell', selectedDate);
1997				return;
1998			}
1999
2000			dp._handleAlreadySelectedDates.bind(dp, alreadySelected, selectedDate)();
2001
2002		},
2003
2004		_onClickCell: function (e) {
2005			var $el = $(e.target).closest('.airdatepicker--cell');
2006
2007			if ($el.hasClass('-disabled-')) return;
2008
2009			this._handleClick.bind(this)($el);
2010		}
2011	};
2012})();
2013
2014;(function () {
2015	var template = '' +
2016		'<div class="airdatepicker--nav-action" data-action="prev">#{prevHtml}</div>' +
2017		'<div class="airdatepicker--nav-title">#{title}</div>' +
2018		'<div class="airdatepicker--nav-action" data-action="next">#{nextHtml}</div>',
2019		buttonsContainerTemplate = '<div class="airdatepicker--buttons"></div>',
2020		button = '<span class="airdatepicker--button" data-action="#{action}">#{label}</span>',
2021		airdatepicker = $.fn.airdatepicker,
2022		dp = airdatepicker.Constructor;
2023
2024	airdatepicker.Navigation = function (d, opts) {
2025		this.d = d;
2026		this.opts = opts;
2027
2028		this.$buttonsContainer = '';
2029
2030		this.init();
2031	};
2032
2033	airdatepicker.Navigation.prototype = {
2034		init: function () {
2035			this._buildBaseHtml();
2036			this._bindEvents();
2037		},
2038
2039		_bindEvents: function () {
2040			this.d.$nav.on('click', '.airdatepicker--nav-action', $.proxy(this._onClickNavButton, this));
2041			this.d.$nav.on('click', '.airdatepicker--nav-title', $.proxy(this._onClickNavTitle, this));
2042			this.d.$airdatepicker.on('click', '.airdatepicker--button', $.proxy(this._onClickNavButton, this));
2043		},
2044
2045		_buildBaseHtml: function () {
2046			if (!this.opts.onlyTimepicker) {
2047				this._render();
2048			}
2049			this._addButtonsIfNeed();
2050		},
2051
2052		_addButtonsIfNeed: function () {
2053			if (this.opts.todayButton) {
2054				this._addButton('today')
2055			}
2056			if (this.opts.clearButton) {
2057				this._addButton('clear')
2058			}
2059		},
2060
2061		_render: function () {
2062			var title = this._getTitle(this.d.currentDate),
2063				html = dp.template(template, $.extend({title: title}, this.opts));
2064			this.d.$nav.html(html);
2065			if (this.d.view == 'years') {
2066				$('.airdatepicker--nav-title', this.d.$nav).addClass('-disabled-');
2067			}
2068			this.setNavStatus();
2069		},
2070
2071		_getTitle: function (date) {
2072			return this.d.formatDate(this.opts.navTitles[this.d.view], date)
2073		},
2074
2075		_addButton: function (type) {
2076			if (!this.$buttonsContainer.length) {
2077				this._addButtonsContainer();
2078			}
2079
2080			var data = {
2081					action: type,
2082					label: this.d.loc[type]
2083				},
2084				html = dp.template(button, data);
2085
2086			if ($('[data-action=' + type + ']', this.$buttonsContainer).length) return;
2087			this.$buttonsContainer.append(html);
2088		},
2089
2090		_addButtonsContainer: function () {
2091			this.d.$airdatepicker.append(buttonsContainerTemplate);
2092			this.$buttonsContainer = $('.airdatepicker--buttons', this.d.$airdatepicker);
2093		},
2094
2095		setNavStatus: function () {
2096			if (!(this.opts.minDate || this.opts.maxDate) || !this.opts.disableNavWhenOutOfRange) return;
2097
2098			var date = this.d.parsedDate,
2099				m = date.month,
2100				y = date.year,
2101				d = date.date;
2102
2103			switch (this.d.view) {
2104				case 'days':
2105					if (!this.d._isInRange(new Date(y, m-1, 1), 'month')) {
2106						this._disableNav('prev')
2107					}
2108					if (!this.d._isInRange(new Date(y, m+1, 1), 'month')) {
2109						this._disableNav('next')
2110					}
2111					break;
2112				case 'months':
2113					if (!this.d._isInRange(new Date(y-1, m, d), 'year')) {
2114						this._disableNav('prev')
2115					}
2116					if (!this.d._isInRange(new Date(y+1, m, d), 'year')) {
2117						this._disableNav('next')
2118					}
2119					break;
2120				case 'years':
2121					var decade = dp.getDecade(this.d.date);
2122					if (!this.d._isInRange(new Date(decade[0] - 1, 0, 1), 'year')) {
2123						this._disableNav('prev')
2124					}
2125					if (!this.d._isInRange(new Date(decade[1] + 1, 0, 1), 'year')) {
2126						this._disableNav('next')
2127					}
2128					break;
2129			}
2130		},
2131
2132		_disableNav: function (nav) {
2133			$('[data-action="' + nav + '"]', this.d.$nav).addClass('-disabled-')
2134		},
2135
2136		_activateNav: function (nav) {
2137			$('[data-action="' + nav + '"]', this.d.$nav).removeClass('-disabled-')
2138		},
2139
2140		_onClickNavButton: function (e) {
2141			var $el = $(e.target).closest('[data-action]'),
2142				action = $el.data('action');
2143
2144			this.d[action]();
2145		},
2146
2147		_onClickNavTitle: function (e) {
2148			if ($(e.target).hasClass('-disabled-')) return;
2149
2150			if (this.d.view == 'days') {
2151				return this.d.view = 'months'
2152			}
2153
2154			this.d.view = 'years';
2155		}
2156	}
2157
2158})();
2159
2160;(function () {
2161	var template = '<div class="airdatepicker--time">' +
2162		'<div class="airdatepicker--time-current">' +
2163		'   <span class="airdatepicker--time-current-hours">#{hourVisible}</span>' +
2164		'   <span class="airdatepicker--time-current-colon">:</span>' +
2165		'   <span class="airdatepicker--time-current-minutes">#{minValue}</span>' +
2166		'</div>' +
2167		'<div class="airdatepicker--time-sliders">' +
2168		'   <div class="airdatepicker--time-row">' +
2169		'	  <input type="range" name="hours" value="#{hourValue}" min="#{hourMin}" max="#{hourMax}" step="#{hourStep}"/>' +
2170		'   </div>' +
2171		'   <div class="airdatepicker--time-row">' +
2172		'	  <input type="range" name="minutes" value="#{minValue}" min="#{minMin}" max="#{minMax}" step="#{minStep}"/>' +
2173		'   </div>' +
2174		'</div>' +
2175		'</div>',
2176		airdatepicker = $.fn.airdatepicker,
2177		dp = airdatepicker.Constructor;
2178
2179	airdatepicker.Timepicker = function (inst, opts) {
2180		this.d = inst;
2181		this.opts = opts;
2182
2183		this.init();
2184	};
2185
2186	airdatepicker.Timepicker.prototype = {
2187		init: function () {
2188			var input = 'input';
2189			this._setTime(this.d.date);
2190			this._buildHTML();
2191
2192			if (navigator.userAgent.match(/trident/gi)) {
2193				input = 'change';
2194			}
2195
2196			this.d.$el.on('selectDate', this._onSelectDate.bind(this));
2197			this.$ranges.on(input, this._onChangeRange.bind(this));
2198			this.$ranges.on('mouseup', this._onMouseUpRange.bind(this));
2199			this.$ranges.on('mousemove focus ', this._onMouseEnterRange.bind(this));
2200			this.$ranges.on('mouseout blur', this._onMouseOutRange.bind(this));
2201		},
2202
2203		_setTime: function (date) {
2204			var _date = dp.getParsedDate(date);
2205
2206			this._handleDate(date);
2207			this.hours = _date.hours < this.minHours ? this.minHours : _date.hours;
2208			this.minutes = _date.minutes < this.minMinutes ? this.minMinutes : _date.minutes;
2209		},
2210
2211		/**
2212		 * Sets minHours and minMinutes from date (usually it's a minDate)
2213		 * Also changes minMinutes if current hours are bigger then @date hours
2214		 * @param date {Date}
2215		 * @private
2216		 */
2217		_setMinTimeFromDate: function (date) {
2218			this.minHours = date.getHours();
2219			this.minMinutes = date.getMinutes();
2220
2221			// If, for example, min hours are 10, and current hours are 12,
2222			// update minMinutes to default value, to be able to choose whole range of values
2223			if (this.d.lastSelectedDate) {
2224				if (this.d.lastSelectedDate.getHours() > date.getHours()) {
2225					this.minMinutes = this.opts.minMinutes;
2226				}
2227			}
2228		},
2229
2230		_setMaxTimeFromDate: function (date) {
2231			this.maxHours = date.getHours();
2232			this.maxMinutes = date.getMinutes();
2233
2234			if (this.d.lastSelectedDate) {
2235				if (this.d.lastSelectedDate.getHours() < date.getHours()) {
2236					this.maxMinutes = this.opts.maxMinutes;
2237				}
2238			}
2239		},
2240
2241		_setDefaultMinMaxTime: function () {
2242			var maxHours = 23,
2243				maxMinutes = 59,
2244				opts = this.opts;
2245
2246			this.minHours = opts.minHours < 0 || opts.minHours > maxHours ? 0 : opts.minHours;
2247			this.minMinutes = opts.minMinutes < 0 || opts.minMinutes > maxMinutes ? 0 : opts.minMinutes;
2248			this.maxHours = opts.maxHours < 0 || opts.maxHours > maxHours ? maxHours : opts.maxHours;
2249			this.maxMinutes = opts.maxMinutes < 0 || opts.maxMinutes > maxMinutes ? maxMinutes : opts.maxMinutes;
2250		},
2251
2252		/**
2253		 * Looks for min/max hours/minutes and if current values
2254		 * are out of range sets valid values.
2255		 * @private
2256		 */
2257		_validateHoursMinutes: function (date) {
2258			if (this.hours < this.minHours) {
2259				this.hours = this.minHours;
2260			} else if (this.hours > this.maxHours) {
2261				this.hours = this.maxHours;
2262			}
2263
2264			if (this.minutes < this.minMinutes) {
2265				this.minutes = this.minMinutes;
2266			} else if (this.minutes > this.maxMinutes) {
2267				this.minutes = this.maxMinutes;
2268			}
2269		},
2270
2271		_buildHTML: function () {
2272			var lz = dp.getLeadingZeroNum,
2273				data = {
2274					hourMin: this.minHours,
2275					hourMax: lz(this.maxHours),
2276					hourStep: this.opts.hoursStep,
2277					hourValue: this.hours,
2278					hourVisible: lz(this.displayHours),
2279					minMin: this.minMinutes,
2280					minMax: lz(this.maxMinutes),
2281					minStep: this.opts.minutesStep,
2282					minValue: lz(this.minutes)
2283				},
2284				_template = dp.template(template, data);
2285
2286			this.$timepicker = $(_template).appendTo(this.d.$airdatepicker);
2287			this.$ranges = $('[type="range"]', this.$timepicker);
2288			this.$hours = $('[name="hours"]', this.$timepicker);
2289			this.$minutes = $('[name="minutes"]', this.$timepicker);
2290			this.$hoursText = $('.airdatepicker--time-current-hours', this.$timepicker);
2291			this.$minutesText = $('.airdatepicker--time-current-minutes', this.$timepicker);
2292
2293			if (this.d.ampm) {
2294				this.$ampm = $('<span class="airdatepicker--time-current-ampm">')
2295					.appendTo($('.airdatepicker--time-current', this.$timepicker))
2296					.html(this.dayPeriod);
2297
2298				this.$timepicker.addClass('-am-pm-');
2299			}
2300		},
2301
2302		_updateCurrentTime: function () {
2303			var h =  dp.getLeadingZeroNum(this.displayHours),
2304				m = dp.getLeadingZeroNum(this.minutes);
2305
2306			this.$hoursText.html(h);
2307			this.$minutesText.html(m);
2308
2309			if (this.d.ampm) {
2310				this.$ampm.html(this.dayPeriod);
2311			}
2312		},
2313
2314		_updateRanges: function () {
2315			this.$hours.attr({
2316				min: this.minHours,
2317				max: this.maxHours
2318			}).val(this.hours);
2319
2320			this.$minutes.attr({
2321				min: this.minMinutes,
2322				max: this.maxMinutes
2323			}).val(this.minutes)
2324		},
2325
2326		/**
2327		 * Sets minHours, minMinutes etc. from date. If date is not passed, than sets
2328		 * values from options
2329		 * @param [date] {object} - Date object, to get values from
2330		 * @private
2331		 */
2332		_handleDate: function (date) {
2333			this._setDefaultMinMaxTime();
2334			if (date) {
2335				if (dp.isSame(date, this.d.opts.minDate)) {
2336					this._setMinTimeFromDate(this.d.opts.minDate);
2337				} else if (dp.isSame(date, this.d.opts.maxDate)) {
2338					this._setMaxTimeFromDate(this.d.opts.maxDate);
2339				}
2340			}
2341
2342			this._validateHoursMinutes(date);
2343		},
2344
2345		update: function () {
2346			this._updateRanges();
2347			this._updateCurrentTime();
2348		},
2349
2350		/**
2351		 * Calculates valid hour value to display in text input and airdatepicker's body.
2352		 * @param date {Date|Number} - date or hours
2353		 * @param [ampm] {Boolean} - 12 hours mode
2354		 * @returns {{hours: *, dayPeriod: string}}
2355		 * @private
2356		 */
2357		_getValidHoursFromDate: function (date, ampm) {
2358			var d = date,
2359				hours = date;
2360
2361			if (date instanceof Date) {
2362				d = dp.getParsedDate(date);
2363				hours = d.hours;
2364			}
2365
2366			var _ampm = ampm || this.d.ampm,
2367				dayPeriod = 'am';
2368
2369			if (_ampm) {
2370				switch(true) {
2371					case hours == 0:
2372						hours = 12;
2373						break;
2374					case hours == 12:
2375						dayPeriod = 'pm';
2376						break;
2377					case hours > 11:
2378						hours = hours - 12;
2379						dayPeriod = 'pm';
2380						break;
2381					default:
2382						break;
2383				}
2384			}
2385
2386			return {
2387				hours: hours,
2388				dayPeriod: dayPeriod
2389			}
2390		},
2391
2392		set hours (val) {
2393			this._hours = val;
2394
2395			var displayHours = this._getValidHoursFromDate(val);
2396
2397			this.displayHours = displayHours.hours;
2398			this.dayPeriod = displayHours.dayPeriod;
2399		},
2400
2401		get hours() {
2402			return this._hours;
2403		},
2404
2405		//  Events
2406		// -------------------------------------------------
2407
2408		_onChangeRange: function (e) {
2409			var $target = $(e.target),
2410				name = $target.attr('name');
2411			
2412			this.d.timepickerIsActive = true;
2413
2414			this[name] = $target.val();
2415			this._updateCurrentTime();
2416			this.d._trigger('timeChange', [this.hours, this.minutes]);
2417
2418			this._handleDate(this.d.lastSelectedDate);
2419			this.update()
2420		},
2421
2422		_onSelectDate: function (e, data) {
2423			this._handleDate(data);
2424			this.update();
2425		},
2426
2427		_onMouseEnterRange: function (e) {
2428			var name = $(e.target).attr('name');
2429			$('.airdatepicker--time-current-' + name, this.$timepicker).addClass('-focus-');
2430		},
2431
2432		_onMouseOutRange: function (e) {
2433			var name = $(e.target).attr('name');
2434			if (this.d.inFocus) return; // Prevent removing focus when mouse out of range slider
2435			$('.airdatepicker--time-current-' + name, this.$timepicker).removeClass('-focus-');
2436		},
2437
2438		_onMouseUpRange: function (e) {
2439			this.d.timepickerIsActive = false;
2440		}
2441	};
2442})();
2443 })(window, jQuery);

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