PageSourceSearch

https://www.likai.tw/js/bootstrap-datetimepicker.js

js likai.tw collected 2026-10-08 16:31:14 UTC 54,266 bytes, 1,697 lines download raw bytes

1/* =========================================================
2 * bootstrap-datetimepicker.js
3 * =========================================================
4 * Copyright 2012 Stefan Petre
5 * Improvements by Andrew Rowls
6 * Improvements by Sébastien Malot
7 * Improvements by Yun Lai
8 * Project URL : http://www.malot.fr/bootstrap-datetimepicker
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 * ========================================================= */
22
23/*
24 * Improvement by CuGBabyBeaR @ 2013-09-12
25 * 
26 * Make it work in bootstrap v3
27 */
28
29!function ($) {
30
31	function UTCDate() {
32		return new Date(Date.UTC.apply(Date, arguments));
33	}
34
35	function UTCToday() {
36		var today = new Date();
37		return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate(), today.getUTCHours(), today.getUTCMinutes(), today.getUTCSeconds(), 0);
38	}
39
40	// Picker object
41
42	var Datetimepicker = function (element, options) {
43		var that = this;
44
45		this.element = $(element);
46
47		this.language = options.language || this.element.data('date-language') || "en";
48		this.language = this.language in dates ? this.language : "en";
49		this.isRTL = dates[this.language].rtl || false;
50		this.formatType = options.formatType || this.element.data('format-type') || 'standard';
51		this.format = DPGlobal.parseFormat(options.format || this.element.data('date-format') || dates[this.language].format || DPGlobal.getDefaultFormat(this.formatType, 'input'), this.formatType);
52		this.isInline = false;
53		this.isVisible = false;
54		this.isInput = this.element.is('input');
55
56		this.bootcssVer = this.isInput ? (this.element.is('.form-control') ? 3 : 2) : ( this.bootcssVer = this.element.is('.input-group') ? 3 : 2 );
57
58		this.component = this.element.is('.date') ? ( this.bootcssVer == 3 ? this.element.find('.input-group-addon .glyphicon-th, .input-group-addon .glyphicon-time, .input-group-addon .glyphicon-calendar').parent() : this.element.find('.add-on .icon-th, .add-on .icon-time, .add-on .icon-calendar').parent()) : false;
59		this.componentReset = this.element.is('.date') ? ( this.bootcssVer == 3 ? this.element.find('.input-group-addon .glyphicon-remove').parent() : this.element.find('.add-on .icon-remove').parent()) : false;
60		this.hasInput = this.component && this.element.find('input').length;
61		if (this.component && this.component.length === 0) {
62			this.component = false;
63		}
64		this.linkField = options.linkField || this.element.data('link-field') || false;
65		this.linkFormat = DPGlobal.parseFormat(options.linkFormat || this.element.data('link-format') || DPGlobal.getDefaultFormat(this.formatType, 'link'), this.formatType);
66		this.minuteStep = options.minuteStep || this.element.data('minute-step') || 5;
67		this.pickerPosition = options.pickerPosition || this.element.data('picker-position') || 'bottom-right';
68		this.showMeridian = options.showMeridian || this.element.data('show-meridian') || false;
69		this.initialDate = options.initialDate || new Date();
70
71		this._attachEvents();
72
73		this.formatViewType = "datetime";
74		if ('formatViewType' in options) {
75			this.formatViewType = options.formatViewType;
76		} else if ('formatViewType' in this.element.data()) {
77			this.formatViewType = this.element.data('formatViewType');
78		}
79
80		this.minView = 0;
81		if ('minView' in options) {
82			this.minView = options.minView;
83		} else if ('minView' in this.element.data()) {
84			this.minView = this.element.data('min-view');
85		}
86		this.minView = DPGlobal.convertViewMode(this.minView);
87
88		this.maxView = DPGlobal.modes.length - 1;
89		if ('maxView' in options) {
90			this.maxView = options.maxView;
91		} else if ('maxView' in this.element.data()) {
92			this.maxView = this.element.data('max-view');
93		}
94		this.maxView = DPGlobal.convertViewMode(this.maxView);
95
96		this.wheelViewModeNavigation = false;
97		if ('wheelViewModeNavigation' in options) {
98			this.wheelViewModeNavigation = options.wheelViewModeNavigation;
99		} else if ('wheelViewModeNavigation' in this.element.data()) {
100			this.wheelViewModeNavigation = this.element.data('view-mode-wheel-navigation');
101		}
102
103		this.wheelViewModeNavigationInverseDirection = false;
104
105		if ('wheelViewModeNavigationInverseDirection' in options) {
106			this.wheelViewModeNavigationInverseDirection = options.wheelViewModeNavigationInverseDirection;
107		} else if ('wheelViewModeNavigationInverseDirection' in this.element.data()) {
108			this.wheelViewModeNavigationInverseDirection = this.element.data('view-mode-wheel-navigation-inverse-dir');
109		}
110
111		this.wheelViewModeNavigationDelay = 100;
112		if ('wheelViewModeNavigationDelay' in options) {
113			this.wheelViewModeNavigationDelay = options.wheelViewModeNavigationDelay;
114		} else if ('wheelViewModeNavigationDelay' in this.element.data()) {
115			this.wheelViewModeNavigationDelay = this.element.data('view-mode-wheel-navigation-delay');
116		}
117
118		this.startViewMode = 2;
119		if ('startView' in options) {
120			this.startViewMode = options.startView;
121		} else if ('startView' in this.element.data()) {
122			this.startViewMode = this.element.data('start-view');
123		}
124		this.startViewMode = DPGlobal.convertViewMode(this.startViewMode);
125		this.viewMode = this.startViewMode;
126
127		this.viewSelect = this.minView;
128		if ('viewSelect' in options) {
129			this.viewSelect = options.viewSelect;
130		} else if ('viewSelect' in this.element.data()) {
131			this.viewSelect = this.element.data('view-select');
132		}
133		this.viewSelect = DPGlobal.convertViewMode(this.viewSelect);
134
135		this.forceParse = true;
136		if ('forceParse' in options) {
137			this.forceParse = options.forceParse;
138		} else if ('dateForceParse' in this.element.data()) {
139			this.forceParse = this.element.data('date-force-parse');
140		}
141
142		this.picker = $((this.bootcssVer == 3) ? DPGlobal.templateV3 : DPGlobal.template)
143			.appendTo(this.isInline ? this.element : 'body')
144			.on({
145				click:     $.proxy(this.click, this),
146				mousedown: $.proxy(this.mousedown, this)
147			});
148
149		if (this.wheelViewModeNavigation) {
150			if ($.fn.mousewheel) {
151				this.picker.on({mousewheel: $.proxy(this.mousewheel, this)});
152			} else {
153				console.log("Mouse Wheel event is not supported. Please include the jQuery Mouse Wheel plugin before enabling this option");
154			}
155		}
156
157		if (this.isInline) {
158			this.picker.addClass('datetimepicker-inline');
159		} else {
160			this.picker.addClass('datetimepicker-dropdown-' + this.pickerPosition + ' dropdown-menu');
161		}
162		if (this.isRTL) {
163			this.picker.addClass('datetimepicker-rtl');
164			if (this.bootcssVer == 3) {
165				this.picker.find('.prev span, .next span')
166					.toggleClass('glyphicon-arrow-left glyphicon-arrow-right');
167			} else {
168				this.picker.find('.prev i, .next i')
169					.toggleClass('icon-arrow-left icon-arrow-right');
170			}
171			;
172
173		}
174		$(document).on('mousedown', function (e) {
175			// Clicked outside the datetimepicker, hide it
176			if ($(e.target).closest('.datetimepicker').length === 0) {
177				that.hide();
178			}
179		});
180
181		this.autoclose = false;
182		if ('autoclose' in options) {
183			this.autoclose = options.autoclose;
184		} else if ('dateAutoclose' in this.element.data()) {
185			this.autoclose = this.element.data('date-autoclose');
186		}
187
188		this.keyboardNavigation = true;
189		if ('keyboardNavigation' in options) {
190			this.keyboardNavigation = options.keyboardNavigation;
191		} else if ('dateKeyboardNavigation' in this.element.data()) {
192			this.keyboardNavigation = this.element.data('date-keyboard-navigation');
193		}
194
195		this.todayBtn = (options.todayBtn || this.element.data('date-today-btn') || false);
196		this.todayHighlight = (options.todayHighlight || this.element.data('date-today-highlight') || false);
197
198		this.weekStart = ((options.weekStart || this.element.data('date-weekstart') || dates[this.language].weekStart || 0) % 7);
199		this.weekEnd = ((this.weekStart + 6) % 7);
200		this.startDate = -Infinity;
201		this.endDate = Infinity;
202		this.daysOfWeekDisabled = [];
203		this.setStartDate(options.startDate || this.element.data('date-startdate'));
204		this.setEndDate(options.endDate || this.element.data('date-enddate'));
205		this.setDaysOfWeekDisabled(options.daysOfWeekDisabled || this.element.data('date-days-of-week-disabled'));
206		this.fillDow();
207		this.fillMonths();
208		this.update();
209		this.showMode();
210
211		if (this.isInline) {
212			this.show();
213		}
214	};
215
216	Datetimepicker.prototype = {
217		constructor: Datetimepicker,
218
219		_events:       [],
220		_attachEvents: function () {
221			this._detachEvents();
222			if (this.isInput) { // single input
223				this._events = [
224					[this.element, {
225						focus:   $.proxy(this.show, this),
226						keyup:   $.proxy(this.update, this),
227						keydown: $.proxy(this.keydown, this)
228					}]
229				];
230			}
231			else if (this.component && this.hasInput) { // component: input + button
232				this._events = [
233					// For components that are not readonly, allow keyboard nav
234					[this.element.find('input'), {
235						focus:   $.proxy(this.show, this),
236						keyup:   $.proxy(this.update, this),
237						keydown: $.proxy(this.keydown, this)
238					}],
239					[this.component, {
240						click: $.proxy(this.show, this)
241					}]
242				];
243				if (this.componentReset) {
244					this._events.push([
245						this.componentReset,
246						{click: $.proxy(this.reset, this)}
247					]);
248				}
249			}
250			else if (this.element.is('div')) {  // inline datetimepicker
251				this.isInline = true;
252			}
253			else {
254				this._events = [
255					[this.element, {
256						click: $.proxy(this.show, this)
257					}]
258				];
259			}
260			for (var i = 0, el, ev; i < this._events.length; i++) {
261				el = this._events[i][0];
262				ev = this._events[i][1];
263				el.on(ev);
264			}
265		},
266
267		_detachEvents: function () {
268			for (var i = 0, el, ev; i < this._events.length; i++) {
269				el = this._events[i][0];
270				ev = this._events[i][1];
271				el.off(ev);
272			}
273			this._events = [];
274		},
275
276		show: function (e) {
277			this.picker.show();
278			this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
279			if (this.forceParse) {
280				this.update();
281			}
282			this.place();
283			$(window).on('resize', $.proxy(this.place, this));
284			if (e) {
285				e.stopPropagation();
286				e.preventDefault();
287			}
288			this.isVisible = true;
289			this.element.trigger({
290				type: 'show',
291				date: this.date
292			});
293		},
294
295		hide: function (e) {
296			if (!this.isVisible) return;
297			if (this.isInline) return;
298			this.picker.hide();
299			$(window).off('resize', this.place);
300			this.viewMode = this.startViewMode;
301			this.showMode();
302			if (!this.isInput) {
303				$(document).off('mousedown', this.hide);
304			}
305
306			if (
307				this.forceParse &&
308					(
309						this.isInput && this.element.val() ||
310							this.hasInput && this.element.find('input').val()
311						)
312				)
313				this.setValue();
314			this.isVisible = false;
315			this.element.trigger({
316				type: 'hide',
317				date: this.date
318			});
319		},
320
321		remove: function () {
322			this._detachEvents();
323			this.picker.remove();
324			delete this.picker;
325			delete this.element.data().datetimepicker;
326		},
327
328		getDate: function () {
329			var d = this.getUTCDate();
330			return new Date(d.getTime() + (d.getTimezoneOffset() * 60000));
331		},
332
333		getUTCDate: function () {
334			return this.date;
335		},
336
337		setDate: function (d) {
338			this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset() * 60000)));
339		},
340
341		setUTCDate: function (d) {
342			if (d >= this.startDate && d <= this.endDate) {
343				this.date = d;
344				this.setValue();
345				this.viewDate = this.date;
346				this.fill();
347			} else {
348				this.element.trigger({
349					type:      'outOfRange',
350					date:      d,
351					startDate: this.startDate,
352					endDate:   this.endDate
353				});
354			}
355		},
356
357		setFormat: function (format) {
358			this.format = DPGlobal.parseFormat(format, this.formatType);
359			var element;
360			if (this.isInput) {
361				element = this.element;
362			} else if (this.component) {
363				element = this.element.find('input');
364			}
365			if (element && element.val()) {
366				this.setValue();
367			}
368		},
369
370		setValue: function () {
371			var formatted = this.getFormattedDate();
372			if (!this.isInput) {
373				if (this.component) {
374					this.element.find('input').val(formatted);
375				}
376				this.element.data('date', formatted);
377			} else {
378				this.element.val(formatted);
379			}
380			if (this.linkField) {
381				$('#' + this.linkField).val(this.getFormattedDate(this.linkFormat));
382			}
383		},
384
385		getFormattedDate: function (format) {
386			if (format == undefined) format = this.format;
387			return DPGlobal.formatDate(this.date, format, this.language, this.formatType);
388		},
389
390		setStartDate: function (startDate) {
391			this.startDate = startDate || -Infinity;
392			if (this.startDate !== -Infinity) {
393				this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language, this.formatType);
394			}
395			this.update();
396			this.updateNavArrows();
397		},
398
399		setEndDate: function (endDate) {
400			this.endDate = endDate || Infinity;
401			if (this.endDate !== Infinity) {
402				this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language, this.formatType);
403			}
404			this.update();
405			this.updateNavArrows();
406		},
407
408		setDaysOfWeekDisabled: function (daysOfWeekDisabled) {
409			this.daysOfWeekDisabled = daysOfWeekDisabled || [];
410			if (!$.isArray(this.daysOfWeekDisabled)) {
411				this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
412			}
413			this.daysOfWeekDisabled = $.map(this.daysOfWeekDisabled, function (d) {
414				return parseInt(d, 10);
415			});
416			this.update();
417			this.updateNavArrows();
418		},
419
420		place: function () {
421			if (this.isInline) return;
422
423			var index_highest = 0;
424			$('div').each(function () {
425				var index_current = parseInt($(this).css("zIndex"), 10);
426				if (index_current > index_highest) {
427					index_highest = index_current;
428				}
429			});
430			var zIndex = index_highest + 10;
431
432			var offset, top, left;
433			if (this.component) {
434				offset = this.component.offset();
435				left = offset.left;
436				if (this.pickerPosition == 'bottom-left' || this.pickerPosition == 'top-left') {
437					left += this.component.outerWidth() - this.picker.outerWidth();
438				}
439			} else {
440				offset = this.element.offset();
441				left = offset.left;
442			}
443			if (this.pickerPosition == 'top-left' || this.pickerPosition == 'top-right') {
444				top = offset.top - this.picker.outerHeight();
445			} else {
446				top = offset.top + this.height;
447			}
448			this.picker.css({
449				top:    top,
450				left:   left,
451				zIndex: zIndex
452			});
453		},
454
455		update: function () {
456			var date, fromArgs = false;
457			if (arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) {
458				date = arguments[0];
459				fromArgs = true;
460			} else {
461				date = this.element.data('date') || (this.isInput ? this.element.val() : this.element.find('input').val()) || this.initialDate;
462				if (typeof date == 'string' || date instanceof String) {
463				  date = date.replace(/^\s+|\s+$/g,'');
464				}
465			}
466
467			if (!date) {
468				date = new Date();
469				fromArgs = false;
470			}
471
472			this.date = DPGlobal.parseDate(date, this.format, this.language, this.formatType);
473
474			if (fromArgs) this.setValue();
475
476			if (this.date < this.startDate) {
477				this.viewDate = new Date(this.startDate);
478			} else if (this.date > this.endDate) {
479				this.viewDate = new Date(this.endDate);
480			} else {
481				this.viewDate = new Date(this.date);
482			}
483			this.fill();
484		},
485
486		fillDow: function () {
487			var dowCnt = this.weekStart,
488				html = '<tr>';
489			while (dowCnt < this.weekStart + 7) {
490				html += '<th class="dow">' + dates[this.language].daysMin[(dowCnt++) % 7] + '</th>';
491			}
492			html += '</tr>';
493			this.picker.find('.datetimepicker-days thead').append(html);
494		},
495
496		fillMonths: function () {
497			var html = '',
498				i = 0;
499			while (i < 12) {
500				html += '<span class="month">' + dates[this.language].monthsShort[i++] + '</span>';
501			}
502			this.picker.find('.datetimepicker-months td').html(html);
503		},
504
505		fill: function () {
506			if (this.date == null || this.viewDate == null) {
507				return;
508			}
509			var d = new Date(this.viewDate),
510				year = d.getUTCFullYear(),
511				month = d.getUTCMonth(),
512				dayMonth = d.getUTCDate(),
513				hours = d.getUTCHours(),
514				minutes = d.getUTCMinutes(),
515				startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
516				startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
517				endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
518				endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
519				currentDate = (new UTCDate(this.date.getUTCFullYear(), this.date.getUTCMonth(), this.date.getUTCDate())).valueOf(),
520				today = new Date();
521			this.picker.find('.datetimepicker-days thead th:eq(1)')
522				.text(dates[this.language].months[month] + ' ' + year);
523			if (this.formatViewType == "time") {
524				var hourConverted = hours % 12 ? hours % 12 : 12;
525				var hoursDisplay = (hourConverted < 10 ? '0' : '') + hourConverted;
526				var minutesDisplay = (minutes < 10 ? '0' : '') + minutes;
527				var meridianDisplay = dates[this.language].meridiem[hours < 12 ? 0 : 1];
528				this.picker.find('.datetimepicker-hours thead th:eq(1)')
529					.text(hoursDisplay + ':' + minutesDisplay + ' ' + meridianDisplay.toUpperCase());
530				this.picker.find('.datetimepicker-minutes thead th:eq(1)')
531					.text(hoursDisplay + ':' + minutesDisplay + ' ' + meridianDisplay.toUpperCase());
532			} else {
533				this.picker.find('.datetimepicker-hours thead th:eq(1)')
534					.text(dayMonth + ' ' + dates[this.language].months[month] + ' ' + year);
535				this.picker.find('.datetimepicker-minutes thead th:eq(1)')
536					.text(dayMonth + ' ' + dates[this.language].months[month] + ' ' + year);
537			}
538			this.picker.find('tfoot th.today')
539				.text(dates[this.language].today)
540				.toggle(this.todayBtn !== false);
541			this.updateNavArrows();
542			this.fillMonths();
543			/*var prevMonth = UTCDate(year, month, 0,0,0,0,0);
544			 prevMonth.setUTCDate(prevMonth.getDate() - (prevMonth.getUTCDay() - this.weekStart + 7)%7);*/
545			var prevMonth = UTCDate(year, month - 1, 28, 0, 0, 0, 0),
546				day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
547			prevMonth.setUTCDate(day);
548			prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7) % 7);
549			var nextMonth = new Date(prevMonth);
550			nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
551			nextMonth = nextMonth.valueOf();
552			var html = [];
553			var clsName;
554			while (prevMonth.valueOf() < nextMonth) {
555				if (prevMonth.getUTCDay() == this.weekStart) {
556					html.push('<tr>');
557				}
558				clsName = '';
559				if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
560					clsName += ' old';
561				} else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
562					clsName += ' new';
563				}
564				// Compare internal UTC date with local today, not UTC today
565				if (this.todayHighlight &&
566					prevMonth.getUTCFullYear() == today.getFullYear() &&
567					prevMonth.getUTCMonth() == today.getMonth() &&
568					prevMonth.getUTCDate() == today.getDate()) {
569					clsName += ' today';
570				}
571				if (prevMonth.valueOf() == currentDate) {
572					clsName += ' active';
573				}
574				if ((prevMonth.valueOf() + 86400000) <= this.startDate || prevMonth.valueOf() > this.endDate ||
575					$.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) {
576					clsName += ' disabled';
577				}
578				html.push('<td class="day' + clsName + '">' + prevMonth.getUTCDate() + '</td>');
579				if (prevMonth.getUTCDay() == this.weekEnd) {
580					html.push('</tr>');
581				}
582				prevMonth.setUTCDate(prevMonth.getUTCDate() + 1);
583			}
584			this.picker.find('.datetimepicker-days tbody').empty().append(html.join(''));
585
586			html = [];
587			var txt = '', meridian = '', meridianOld = '';
588			for (var i = 0; i < 24; i++) {
589				var actual = UTCDate(year, month, dayMonth, i);
590				clsName = '';
591				// We want the previous hour for the startDate
592				if ((actual.valueOf() + 3600000) <= this.startDate || actual.valueOf() > this.endDate) {
593					clsName += ' disabled';
594				} else if (hours == i) {
595					clsName += ' active';
596				}
597				if (this.showMeridian && dates[this.language].meridiem.length == 2) {
598					meridian = (i < 12 ? dates[this.language].meridiem[0] : dates[this.language].meridiem[1]);
599					if (meridian != meridianOld) {
600						if (meridianOld != '') {
601							html.push('</fieldset>');
602						}
603						html.push('<fieldset class="hour"><legend>' + meridian.toUpperCase() + '</legend>');
604					}
605					meridianOld = meridian;
606					txt = (i % 12 ? i % 12 : 12);
607					html.push('<span class="hour' + clsName + ' hour_' + (i < 12 ? 'am' : 'pm') + '">' + txt + '</span>');
608					if (i == 23) {
609						html.push('</fieldset>');
610					}
611				} else {
612					txt = i + ':00';
613					html.push('<span class="hour' + clsName + '">' + txt + '</span>');
614				}
615			}
616			this.picker.find('.datetimepicker-hours td').html(html.join(''));
617
618			html = [];
619			txt = '', meridian = '', meridianOld = '';
620			for (var i = 0; i < 60; i += this.minuteStep) {
621				var actual = UTCDate(year, month, dayMonth, hours, i, 0);
622				clsName = '';
623				if (actual.valueOf() < this.startDate || actual.valueOf() > this.endDate) {
624					clsName += ' disabled';
625				} else if (Math.floor(minutes / this.minuteStep) == Math.floor(i / this.minuteStep)) {
626					clsName += ' active';
627				}
628				if (this.showMeridian && dates[this.language].meridiem.length == 2) {
629					meridian = (hours < 12 ? dates[this.language].meridiem[0] : dates[this.language].meridiem[1]);
630					if (meridian != meridianOld) {
631						if (meridianOld != '') {
632							html.push('</fieldset>');
633						}
634						html.push('<fieldset class="minute"><legend>' + meridian.toUpperCase() + '</legend>');
635					}
636					meridianOld = meridian;
637					txt = (hours % 12 ? hours % 12 : 12);
638					//html.push('<span class="minute'+clsName+' minute_'+(hours<12?'am':'pm')+'">'+txt+'</span>');
639					html.push('<span class="minute' + clsName + '">' + txt + ':' + (i < 10 ? '0' + i : i) + '</span>');
640					if (i == 59) {
641						html.push('</fieldset>');
642					}
643				} else {
644					txt = i + ':00';
645					//html.push('<span class="hour'+clsName+'">'+txt+'</span>');
646					html.push('<span class="minute' + clsName + '">' + hours + ':' + (i < 10 ? '0' + i : i) + '</span>');
647				}
648			}
649			this.picker.find('.datetimepicker-minutes td').html(html.join(''));
650
651			var currentYear = this.date.getUTCFullYear();
652			var months = this.picker.find('.datetimepicker-months')
653				.find('th:eq(1)')
654				.text(year)
655				.end()
656				.find('span').removeClass('active');
657			if (currentYear == year) {
658				months.eq(this.date.getUTCMonth()).addClass('active');
659			}
660			if (year < startYear || year > endYear) {
661				months.addClass('disabled');
662			}
663			if (year == startYear) {
664				months.slice(0, startMonth).addClass('disabled');
665			}
666			if (year == endYear) {
667				months.slice(endMonth + 1).addClass('disabled');
668			}
669
670			html = '';
671			year = parseInt(year / 10, 10) * 10;
672			var yearCont = this.picker.find('.datetimepicker-years')
673				.find('th:eq(1)')
674				.text(year + '-' + (year + 9))
675				.end()
676				.find('td');
677			year -= 1;
678			for (var i = -1; i < 11; i++) {
679				html += '<span class="year' + (i == -1 || i == 10 ? ' old' : '') + (currentYear == year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : '') + '">' + year + '</span>';
680				year += 1;
681			}
682			yearCont.html(html);
683			this.place();
684		},
685
686		updateNavArrows: function () {
687			var d = new Date(this.viewDate),
688				year = d.getUTCFullYear(),
689				month = d.getUTCMonth(),
690				day = d.getUTCDate(),
691				hour = d.getUTCHours();
692			switch (this.viewMode) {
693				case 0:
694					if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()
695						&& month <= this.startDate.getUTCMonth()
696						&& day <= this.startDate.getUTCDate()
697						&& hour <= this.startDate.getUTCHours()) {
698						this.picker.find('.prev').css({visibility: 'hidden'});
699					} else {
700						this.picker.find('.prev').css({visibility: 'visible'});
701					}
702					if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()
703						&& month >= this.endDate.getUTCMonth()
704						&& day >= this.endDate.getUTCDate()
705						&& hour >= this.endDate.getUTCHours()) {
706						this.picker.find('.next').css({visibility: 'hidden'});
707					} else {
708						this.picker.find('.next').css({visibility: 'visible'});
709					}
710					break;
711				case 1:
712					if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()
713						&& month <= this.startDate.getUTCMonth()
714						&& day <= this.startDate.getUTCDate()) {
715						this.picker.find('.prev').css({visibility: 'hidden'});
716					} else {
717						this.picker.find('.prev').css({visibility: 'visible'});
718					}
719					if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()
720						&& month >= this.endDate.getUTCMonth()
721						&& day >= this.endDate.getUTCDate()) {
722						this.picker.find('.next').css({visibility: 'hidden'});
723					} else {
724						this.picker.find('.next').css({visibility: 'visible'});
725					}
726					break;
727				case 2:
728					if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()
729						&& month <= this.startDate.getUTCMonth()) {
730						this.picker.find('.prev').css({visibility: 'hidden'});
731					} else {
732						this.picker.find('.prev').css({visibility: 'visible'});
733					}
734					if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()
735						&& month >= this.endDate.getUTCMonth()) {
736						this.picker.find('.next').css({visibility: 'hidden'});
737					} else {
738						this.picker.find('.next').css({visibility: 'visible'});
739					}
740					break;
741				case 3:
742				case 4:
743					if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
744						this.picker.find('.prev').css({visibility: 'hidden'});
745					} else {
746						this.picker.find('.prev').css({visibility: 'visible'});
747					}
748					if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
749						this.picker.find('.next').css({visibility: 'hidden'});
750					} else {
751						this.picker.find('.next').css({visibility: 'visible'});
752					}
753					break;
754			}
755		},
756
757		mousewheel: function (e) {
758
759			e.preventDefault();
760			e.stopPropagation();
761
762			if (this.wheelPause) {
763				return;
764			}
765
766			this.wheelPause = true;
767
768			var originalEvent = e.originalEvent;
769
770			var delta = originalEvent.wheelDelta;
771
772			var mode = delta > 0 ? 1 : (delta === 0) ? 0 : -1;
773
774			if (this.wheelViewModeNavigationInverseDirection) {
775				mode = -mode;
776			}
777
778			this.showMode(mode);
779
780			setTimeout($.proxy(function () {
781
782				this.wheelPause = false
783
784			}, this), this.wheelViewModeNavigationDelay);
785
786		},
787
788		click: function (e) {
789			e.stopPropagation();
790			e.preventDefault();
791			var target = $(e.target).closest('span, td, th, legend');
792			if (target.length == 1) {
793				if (target.is('.disabled')) {
794					this.element.trigger({
795						type:      'outOfRange',
796						date:      this.viewDate,
797						startDate: this.startDate,
798						endDate:   this.endDate
799					});
800					return;
801				}
802				switch (target[0].nodeName.toLowerCase()) {
803					case 'th':
804						switch (target[0].className) {
805							case 'switch':
806								this.showMode(1);
807								break;
808							case 'prev':
809							case 'next':
810								var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
811								switch (this.viewMode) {
812									case 0:
813										this.viewDate = this.moveHour(this.viewDate, dir);
814										break;
815									case 1:
816										this.viewDate = this.moveDate(this.viewDate, dir);
817										break;
818									case 2:
819										this.viewDate = this.moveMonth(this.viewDate, dir);
820										break;
821									case 3:
822									case 4:
823										this.viewDate = this.moveYear(this.viewDate, dir);
824										break;
825								}
826								this.fill();
827								break;
828							case 'today':
829								var date = new Date();
830								date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), 0);
831
832								// Respect startDate and endDate.
833								if (date < this.startDate) date = this.startDate;
834								else if (date > this.endDate) date = this.endDate;
835
836								this.viewMode = this.startViewMode;
837								this.showMode(0);
838								this._setDate(date);
839								this.fill();
840								if (this.autoclose) {
841									this.hide();
842								}
843								break;
844						}
845						break;
846					case 'span':
847						if (!target.is('.disabled')) {
848							var year = this.viewDate.getUTCFullYear(),
849								month = this.viewDate.getUTCMonth(),
850								day = this.viewDate.getUTCDate(),
851								hours = this.viewDate.getUTCHours(),
852								minutes = this.viewDate.getUTCMinutes(),
853								seconds = this.viewDate.getUTCSeconds();
854
855							if (target.is('.month')) {
856								this.viewDate.setUTCDate(1);
857								month = target.parent().find('span').index(target);
858								day = this.viewDate.getUTCDate();
859								this.viewDate.setUTCMonth(month);
860								this.element.trigger({
861									type: 'changeMonth',
862									date: this.viewDate
863								});
864								if (this.viewSelect >= 3) {
865									this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0));
866								}
867							} else if (target.is('.year')) {
868								this.viewDate.setUTCDate(1);
869								year = parseInt(target.text(), 10) || 0;
870								this.viewDate.setUTCFullYear(year);
871								this.element.trigger({
872									type: 'changeYear',
873									date: this.viewDate
874								});
875								if (this.viewSelect >= 4) {
876									this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0));
877								}
878							} else if (target.is('.hour')) {
879								hours = parseInt(target.text(), 10) || 0;
880								if (target.hasClass('hour_am') || target.hasClass('hour_pm')) {
881									if (hours == 12 && target.hasClass('hour_am')) {
882										hours = 0;
883									} else if (hours != 12 && target.hasClass('hour_pm')) {
884										hours += 12;
885									}
886								}
887								this.viewDate.setUTCHours(hours);
888								this.element.trigger({
889									type: 'changeHour',
890									date: this.viewDate
891								});
892								if (this.viewSelect >= 1) {
893									this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0));
894								}
895							} else if (target.is('.minute')) {
896								minutes = parseInt(target.text().substr(target.text().indexOf(':') + 1), 10) || 0;
897								this.viewDate.setUTCMinutes(minutes);
898								this.element.trigger({
899									type: 'changeMinute',
900									date: this.viewDate
901								});
902								if (this.viewSelect >= 0) {
903									this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0));
904								}
905							}
906							if (this.viewMode != 0) {
907								var oldViewMode = this.viewMode;
908								this.showMode(-1);
909								this.fill();
910								if (oldViewMode == this.viewMode && this.autoclose) {
911									this.hide();
912								}
913							} else {
914								this.fill();
915								if (this.autoclose) {
916									this.hide();
917								}
918							}
919						}
920						break;
921					case 'td':
922						if (target.is('.day') && !target.is('.disabled')) {
923							var day = parseInt(target.text(), 10) || 1;
924							var year = this.viewDate.getUTCFullYear(),
925								month = this.viewDate.getUTCMonth(),
926								hours = this.viewDate.getUTCHours(),
927								minutes = this.viewDate.getUTCMinutes(),
928								seconds = this.viewDate.getUTCSeconds();
929							if (target.is('.old')) {
930								if (month === 0) {
931									month = 11;
932									year -= 1;
933								} else {
934									month -= 1;
935								}
936							} else if (target.is('.new')) {
937								if (month == 11) {
938									month = 0;
939									year += 1;
940								} else {
941									month += 1;
942								}
943							}
944							this.viewDate.setUTCFullYear(year);
945							this.viewDate.setUTCMonth(month, day);
946							this.element.trigger({
947								type: 'changeDay',
948								date: this.viewDate
949							});
950							if (this.viewSelect >= 2) {
951								this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0));
952							}
953						}
954						var oldViewMode = this.viewMode;
955						this.showMode(-1);
956						this.fill();
957						if (oldViewMode == this.viewMode && this.autoclose) {
958							this.hide();
959						}
960						break;
961				}
962			}
963		},
964
965		_setDate: function (date, which) {
966			if (!which || which == 'date')
967				this.date = date;
968			if (!which || which == 'view')
969				this.viewDate = date;
970			this.fill();
971			this.setValue();
972			var element;
973			if (this.isInput) {
974				element = this.element;
975			} else if (this.component) {
976				element = this.element.find('input');
977			}
978			if (element) {
979				element.change();
980				if (this.autoclose && (!which || which == 'date')) {
981					//this.hide();
982				}
983			}
984			this.element.trigger({
985				type: 'changeDate',
986				date: this.date
987			});
988		},
989
990		moveMinute: function (date, dir) {
991			if (!dir) return date;
992			var new_date = new Date(date.valueOf());
993			//dir = dir > 0 ? 1 : -1;
994			new_date.setUTCMinutes(new_date.getUTCMinutes() + (dir * this.minuteStep));
995			return new_date;
996		},
997
998		moveHour: function (date, dir) {
999			if (!dir) return date;
1000			var new_date = new Date(date.valueOf());
1001			//dir = dir > 0 ? 1 : -1;
1002			new_date.setUTCHours(new_date.getUTCHours() + dir);
1003			return new_date;
1004		},
1005
1006		moveDate: function (date, dir) {
1007			if (!dir) return date;
1008			var new_date = new Date(date.valueOf());
1009			//dir = dir > 0 ? 1 : -1;
1010			new_date.setUTCDate(new_date.getUTCDate() + dir);
1011			return new_date;
1012		},
1013
1014		moveMonth: function (date, dir) {
1015			if (!dir) return date;
1016			var new_date = new Date(date.valueOf()),
1017				day = new_date.getUTCDate(),
1018				month = new_date.getUTCMonth(),
1019				mag = Math.abs(dir),
1020				new_month, test;
1021			dir = dir > 0 ? 1 : -1;
1022			if (mag == 1) {
1023				test = dir == -1
1024					// If going back one month, make sure month is not current month
1025					// (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
1026					? function () {
1027					return new_date.getUTCMonth() == month;
1028				}
1029					// If going forward one month, make sure month is as expected
1030					// (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
1031					: function () {
1032					return new_date.getUTCMonth() != new_month;
1033				};
1034				new_month = month + dir;
1035				new_date.setUTCMonth(new_month);
1036				// Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
1037				if (new_month < 0 || new_month > 11)
1038					new_month = (new_month + 12) % 12;
1039			} else {
1040				// For magnitudes >1, move one month at a time...
1041				for (var i = 0; i < mag; i++)
1042					// ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
1043					new_date = this.moveMonth(new_date, dir);
1044				// ...then reset the day, keeping it in the new month
1045				new_month = new_date.getUTCMonth();
1046				new_date.setUTCDate(day);
1047				test = function () {
1048					return new_month != new_date.getUTCMonth();
1049				};
1050			}
1051			// Common date-resetting loop -- if date is beyond end of month, make it
1052			// end of month
1053			while (test()) {
1054				new_date.setUTCDate(--day);
1055				new_date.setUTCMonth(new_month);
1056			}
1057			return new_date;
1058		},
1059
1060		moveYear: function (date, dir) {
1061			return this.moveMonth(date, dir * 12);
1062		},
1063
1064		dateWithinRange: function (date) {
1065			return date >= this.startDate && date <= this.endDate;
1066		},
1067
1068		keydown: function (e) {
1069			if (this.picker.is(':not(:visible)')) {
1070				if (e.keyCode == 27) // allow escape to hide and re-show picker
1071					this.show();
1072				return;
1073			}
1074			var dateChanged = false,
1075				dir, day, month,
1076				newDate, newViewDate;
1077			switch (e.keyCode) {
1078				case 27: // escape
1079					this.hide();
1080					e.preventDefault();
1081					break;
1082				case 37: // left
1083				case 39: // right
1084					if (!this.keyboardNavigation) break;
1085					dir = e.keyCode == 37 ? -1 : 1;
1086					viewMode = this.viewMode;
1087					if (e.ctrlKey) {
1088						viewMode += 2;
1089					} else if (e.shiftKey) {
1090						viewMode += 1;
1091					}
1092					if (viewMode == 4) {
1093						newDate = this.moveYear(this.date, dir);
1094						newViewDate = this.moveYear(this.viewDate, dir);
1095					} else if (viewMode == 3) {
1096						newDate = this.moveMonth(this.date, dir);
1097						newViewDate = this.moveMonth(this.viewDate, dir);
1098					} else if (viewMode == 2) {
1099						newDate = this.moveDate(this.date, dir);
1100						newViewDate = this.moveDate(this.viewDate, dir);
1101					} else if (viewMode == 1) {
1102						newDate = this.moveHour(this.date, dir);
1103						newViewDate = this.moveHour(this.viewDate, dir);
1104					} else if (viewMode == 0) {
1105						newDate = this.moveMinute(this.date, dir);
1106						newViewDate = this.moveMinute(this.viewDate, dir);
1107					}
1108					if (this.dateWithinRange(newDate)) {
1109						this.date = newDate;
1110						this.viewDate = newViewDate;
1111						this.setValue();
1112						this.update();
1113						e.preventDefault();
1114						dateChanged = true;
1115					}
1116					break;
1117				case 38: // up
1118				case 40: // down
1119					if (!this.keyboardNavigation) break;
1120					dir = e.keyCode == 38 ? -1 : 1;
1121					viewMode = this.viewMode;
1122					if (e.ctrlKey) {
1123						viewMode += 2;
1124					} else if (e.shiftKey) {
1125						viewMode += 1;
1126					}
1127					if (viewMode == 4) {
1128						newDate = this.moveYear(this.date, dir);
1129						newViewDate = this.moveYear(this.viewDate, dir);
1130					} else if (viewMode == 3) {
1131						newDate = this.moveMonth(this.date, dir);
1132						newViewDate = this.moveMonth(this.viewDate, dir);
1133					} else if (viewMode == 2) {
1134						newDate = this.moveDate(this.date, dir * 7);
1135						newViewDate = this.moveDate(this.viewDate, dir * 7);
1136					} else if (viewMode == 1) {
1137						if (this.showMeridian) {
1138							newDate = this.moveHour(this.date, dir * 6);
1139							newViewDate = this.moveHour(this.viewDate, dir * 6);
1140						} else {
1141							newDate = this.moveHour(this.date, dir * 4);
1142							newViewDate = this.moveHour(this.viewDate, dir * 4);
1143						}
1144					} else if (viewMode == 0) {
1145						newDate = this.moveMinute(this.date, dir * 4);
1146						newViewDate = this.moveMinute(this.viewDate, dir * 4);
1147					}
1148					if (this.dateWithinRange(newDate)) {
1149						this.date = newDate;
1150						this.viewDate = newViewDate;
1151						this.setValue();
1152						this.update();
1153						e.preventDefault();
1154						dateChanged = true;
1155					}
1156					break;
1157				case 13: // enter
1158					if (this.viewMode != 0) {
1159						var oldViewMode = this.viewMode;
1160						this.showMode(-1);
1161						this.fill();
1162						if (oldViewMode == this.viewMode && this.autoclose) {
1163							this.hide();
1164						}
1165					} else {
1166						this.fill();
1167						if (this.autoclose) {
1168							this.hide();
1169						}
1170					}
1171					e.preventDefault();
1172					break;
1173				case 9: // tab
1174					this.hide();
1175					break;
1176			}
1177			if (dateChanged) {
1178				var element;
1179				if (this.isInput) {
1180					element = this.element;
1181				} else if (this.component) {
1182					element = this.element.find('input');
1183				}
1184				if (element) {
1185					element.change();
1186				}
1187				this.element.trigger({
1188					type: 'changeDate',
1189					date: this.date
1190				});
1191			}
1192		},
1193
1194		showMode: function (dir) {
1195			if (dir) {
1196				var newViewMode = Math.max(0, Math.min(DPGlobal.modes.length - 1, this.viewMode + dir));
1197				if (newViewMode >= this.minView && newViewMode <= this.maxView) {
1198					this.element.trigger({
1199						type:        'changeMode',
1200						date:        this.viewDate,
1201						oldViewMode: this.viewMode,
1202						newViewMode: newViewMode
1203					});
1204
1205					this.viewMode = newViewMode;
1206				}
1207			}
1208			/*
1209			 vitalets: fixing bug of very special conditions:
1210			 jquery 1.7.1 + webkit + show inline datetimepicker in bootstrap popover.
1211			 Method show() does not set display css correctly and datetimepicker is not shown.
1212			 Changed to .css('display', 'block') solve the problem.
1213			 See https://github.com/vitalets/x-editable/issues/37
1214
1215			 In jquery 1.7.2+ everything works fine.
1216			 */
1217			//this.picker.find('>div').hide().filter('.datetimepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
1218			this.picker.find('>div').hide().filter('.datetimepicker-' + DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
1219			this.updateNavArrows();
1220		},
1221
1222		reset: function (e) {
1223			this._setDate(null, 'date');
1224		}
1225	};
1226
1227	$.fn.datetimepicker = function (option) {
1228		var args = Array.apply(null, arguments);
1229		args.shift();
1230		return this.each(function () {
1231			var $this = $(this),
1232				data = $this.data('datetimepicker'),
1233				options = typeof option == 'object' && option;
1234			if (!data) {
1235				$this.data('datetimepicker', (data = new Datetimepicker(this, $.extend({}, $.fn.datetimepicker.defaults, options))));
1236			}
1237			if (typeof option == 'string' && typeof data[option] == 'function') {
1238				data[option].apply(data, args);
1239			}
1240		});
1241	};
1242
1243	$.fn.datetimepicker.defaults = {
1244	};
1245	$.fn.datetimepicker.Constructor = Datetimepicker;
1246	var dates = $.fn.datetimepicker.dates = {
1247		en: {
1248			days:        ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
1249			daysShort:   ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
1250			daysMin:     ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
1251			months:      ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
1251November", "December"],
1252			monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
1253			meridiem:    ["am", "pm"],
1254			suffix:      ["st", "nd", "rd", "th"],
1255			today:       "Today"
1256		}
1257	};
1258
1259	var DPGlobal = {
1260		modes:            [
1261			{
1262				clsName: 'minutes',
1263				navFnc:  'Hours',
1264				navStep: 1
1265			},
1266			{
1267				clsName: 'hours',
1268				navFnc:  'Date',
1269				navStep: 1
1270			},
1271			{
1272				clsName: 'days',
1273				navFnc:  'Month',
1274				navStep: 1
1275			},
1276			{
1277				clsName: 'months',
1278				navFnc:  'FullYear',
1279				navStep: 1
1280			},
1281			{
1282				clsName: 'years',
1283				navFnc:  'FullYear',
1284				navStep: 10
1285			}
1286		],
1287		isLeapYear:       function (year) {
1288			return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
1289		},
1290		getDaysInMonth:   function (year, month) {
1291			return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
1292		},
1293		getDefaultFormat: function (type, field) {
1294			if (type == "standard") {
1295				if (field == 'input')
1296					return 'yyyy-mm-dd hh:ii';
1297				else
1298					return 'yyyy-mm-dd hh:ii:ss';
1299			} else if (type == "php") {
1300				if (field == 'input')
1301					return 'Y-m-d H:i';
1302				else
1303					return 'Y-m-d H:i:s';
1304			} else {
1305				throw new Error("Invalid format type.");
1306			}
1307		},
1308		validParts:       function (type) {
1309			if (type == "standard") {
1310				return /hh?|HH?|p|P|ii?|ss?|dd?|DD?|mm?|MM?|yy(?:yy)?/g;
1311			} else if (type == "php") {
1312				return /[dDjlNwzFmMnStyYaABgGhHis]/g;
1313			} else {
1314				throw new Error("Invalid format type.");
1315			}
1316		},
1317		nonpunctuation:   /[^ -\/:-@\[-`{-~\t\n\rTZ]+/g,
1318		parseFormat:      function (format, type) {
1319			// IE treats \0 as a string end in inputs (truncating the value),
1320			// so it's a bad format delimiter, anyway
1321			var separators = format.replace(this.validParts(type), '\0').split('\0'),
1322				parts = format.match(this.validParts(type));
1323			if (!separators || !separators.length || !parts || parts.length == 0) {
1324				throw new Error("Invalid date format.");
1325			}
1326			return {separators: separators, parts: parts};
1327		},
1328		parseDate:        function (date, format, language, type) {
1329			if (date instanceof Date) {
1330				var dateUTC = new Date(date.valueOf() - date.getTimezoneOffset() * 60000);
1331				dateUTC.setMilliseconds(0);
1332				return dateUTC;
1333			}
1334			if (/^\d{4}\-\d{1,2}\-\d{1,2}$/.test(date)) {
1335				format = this.parseFormat('yyyy-mm-dd', type);
1336			}
1337			if (/^\d{4}\-\d{1,2}\-\d{1,2}[T ]\d{1,2}\:\d{1,2}$/.test(date)) {
1338				format = this.parseFormat('yyyy-mm-dd hh:ii', type);
1339			}
1340			if (/^\d{4}\-\d{1,2}\-\d{1,2}[T ]\d{1,2}\:\d{1,2}\:\d{1,2}[Z]{0,1}$/.test(date)) {
1341				format = this.parseFormat('yyyy-mm-dd hh:ii:ss', type);
1342			}
1343			if (/^[-+]\d+[dmwy]([\s,]+[-+]\d+[dmwy])*$/.test(date)) {
1344				var part_re = /([-+]\d+)([dmwy])/,
1345					parts = date.match(/([-+]\d+)([dmwy])/g),
1346					part, dir;
1347				date = new Date();
1348				for (var i = 0; i < parts.length; i++) {
1349					part = part_re.exec(parts[i]);
1350					dir = parseInt(part[1]);
1351					switch (part[2]) {
1352						case 'd':
1353							date.setUTCDate(date.getUTCDate() + dir);
1354							break;
1355						case 'm':
1356							date = Datetimepicker.prototype.moveMonth.call(Datetimepicker.prototype, date, dir);
1357							break;
1358						case 'w':
1359							date.setUTCDate(date.getUTCDate() + dir * 7);
1360							break;
1361						case 'y':
1362							date = Datetimepicker.prototype.moveYear.call(Datetimepicker.prototype, date, dir);
1363							break;
1364					}
1365				}
1366				return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), 0);
1367			}
1368			var parts = date && date.match(this.nonpunctuation) || [],
1369				date = new Date(0, 0, 0, 0, 0, 0, 0),
1370				parsed = {},
1371				setters_order = ['hh', 'h', 'ii', 'i', 'ss', 's', 'yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'D', 'DD', 'd', 'dd', 'H', 'HH', 'p', 'P'],
1372				setters_map = {
1373					hh:   function (d, v) {
1374						return d.setUTCHours(v);
1375					},
1376					h:    function (d, v) {
1377						return d.setUTCHours(v);
1378					},
1379					HH:   function (d, v) {
1380						return d.setUTCHours(v == 12 ? 0 : v);
1381					},
1382					H:    function (d, v) {
1383						return d.setUTCHours(v == 12 ? 0 : v);
1384					},
1385					ii:   function (d, v) {
1386						return d.setUTCMinutes(v);
1387					},
1388					i:    function (d, v) {
1389						return d.setUTCMinutes(v);
1390					},
1391					ss:   function (d, v) {
1392						return d.setUTCSeconds(v);
1393					},
1394					s:    function (d, v) {
1395						return d.setUTCSeconds(v);
1396					},
1397					yyyy: function (d, v) {
1398						return d.setUTCFullYear(v);
1399					},
1400					yy:   function (d, v) {
1401						return d.setUTCFullYear(2000 + v);
1402					},
1403					m:    function (d, v) {
1404						v -= 1;
1405						while (v < 0) v += 12;
1406						v %= 12;
1407						d.setUTCMonth(v);
1408						while (d.getUTCMonth() != v)
1409							d.setUTCDate(d.getUTCDate() - 1);
1410						return d;
1411					},
1412					d:    function (d, v) {
1413						return d.setUTCDate(v);
1414					},
1415					p:    function (d, v) {
1416						return d.setUTCHours(v == 1 ? d.getUTCHours() + 12 : d.getUTCHours());
1417					}
1418				},
1419				val, filtered, part;
1420			setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
1421			setters_map['dd'] = setters_map['d'];
1422			setters_map['P'] = setters_map['p'];
1423			date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds());
1424			if (parts.length == format.parts.length) {
1425				for (var i = 0, cnt = format.parts.length; i < cnt; i++) {
1426					val = parseInt(parts[i], 10);
1427					part = format.parts[i];
1428					if (isNaN(val)) {
1429						switch (part) {
1430							case 'MM':
1431								filtered = $(dates[language].months).filter(function () {
1432									var m = this.slice(0, parts[i].length),
1433										p = parts[i].slice(0, m.length);
1434									return m == p;
1435								});
1436								val = $.inArray(filtered[0], dates[language].months) + 1;
1437								break;
1438							case 'M':
1439								filtered = $(dates[language].monthsShort).filter(function () {
1440									var m = this.slice(0, parts[i].length),
1441										p = parts[i].slice(0, m.length);
1442									return m == p;
1443								});
1444								val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
1445								break;
1446							case 'p':
1447							case 'P':
1448								val = $.inArray(parts[i].toLowerCase(), dates[language].meridiem);
1449								break;
1450						}
1451					}
1452					parsed[part] = val;
1453				}
1454				for (var i = 0, s; i < setters_order.length; i++) {
1455					s = setters_order[i];
1456					if (s in parsed && !isNaN(parsed[s]))
1457						setters_map[s](date, parsed[s])
1458				}
1459			}
1460			return date;
1461		},
1462		formatDate:       function (date, format, language, type) {
1463			if (date == null) {
1464				return '';
1465			}
1466			var val;
1467			if (type == 'standard') {
1468				val = {
1469					// year
1470					yy:   date.getUTCFullYear().toString().substring(2),
1471					yyyy: date.getUTCFullYear(),
1472					// month
1473					m:    date.getUTCMonth() + 1,
1474					M:    dates[language].monthsShort[date.getUTCMonth()],
1475					MM:   dates[language].months[date.getUTCMonth()],
1476					// day
1477					d:    date.getUTCDate(),
1478					D:    dates[language].daysShort[date.getUTCDay()],
1479					DD:   dates[language].days[date.getUTCDay()],
1480					p:    (dates[language].meridiem.length == 2 ? dates[language].meridiem[date.getUTCHours() < 12 ? 0 : 1] : ''),
1481					// hour
1482					h:    date.getUTCHours(),
1483					// minute
1484					i:    date.getUTCMinutes(),
1485					// second
1486					s:    date.getUTCSeconds()
1487				};
1488
1489				if (dates[language].meridiem.length == 2) {
1490					val.H = (val.h % 12 == 0 ? 12 : val.h % 12);
1491				}
1492				else {
1493					val.H = val.h;
1494				}
1495				val.HH = (val.H < 10 ? '0' : '') + val.H;
1496				val.P = val.p.toUpperCase();
1497				val.hh = (val.h < 10 ? '0' : '') + val.h;
1498				val.ii = (val.i < 10 ? '0' : '') + val.i;
1499				val.ss = (val.s < 10 ? '0' : '') + val.s;
1500				val.dd = (val.d < 10 ? '0' : '') + val.d;
1501				val.mm = (val.m < 10 ? '0' : '') + val.m;
1502			} else if (type == 'php') {
1503				// php format
1504				val = {
1505					// year
1506					y: date.getUTCFullYear().toString().substring(2),
1507					Y: date.getUTCFullYear(),
1508					// month
1509					F: dates[language].months[date.getUTCMonth()],
1510					M: dates[language].monthsShort[date.getUTCMonth()],
1511					n: date.getUTCMonth() + 1,
1512					t: DPGlobal.getDaysInMonth(date.getUTCFullYear(), date.getUTCMonth()),
1513					// day
1514					j: date.getUTCDate(),
1515					l: dates[language].days[date.getUTCDay()],
1516					D: dates[language].daysShort[date.getUTCDay()],
1517					w: date.getUTCDay(), // 0 -> 6
1518					N: (date.getUTCDay() == 0 ? 7 : date.getUTCDay()),       // 1 -> 7
1519					S: (date.getUTCDate() % 10 <= dates[language].suffix.length ? dates[language].suffix[date.getUTCDate() % 10 - 1] : ''),
1520					// hour
1521					a: (dates[language].meridiem.length == 2 ? dates[language].meridiem[date.getUTCHours() < 12 ? 0 : 1] : ''),
1522					g: (date.getUTCHours() % 12 == 0 ? 12 : date.getUTCHours() % 12),
1523					G: date.getUTCHours(),
1524					// minute
1525					i: date.getUTCMinutes(),
1526					// second
1527					s: date.getUTCSeconds()
1528				};
1529				val.m = (val.n < 10 ? '0' : '') + val.n;
1530				val.d = (val.j < 10 ? '0' : '') + val.j;
1531				val.A = val.a.toString().toUpperCase();
1532				val.h = (val.g < 10 ? '0' : '') + val.g;
1533				val.H = (val.G < 10 ? '0' : '') + val.G;
1534				val.i = (val.i < 10 ? '0' : '') + val.i;
1535				val.s = (val.s < 10 ? '0' : '') + val.s;
1536			} else {
1537				throw new Error("Invalid format type.");
1538			}
1539			var date = [],
1540				seps = $.extend([], format.separators);
1541			for (var i = 0, cnt = format.parts.length; i < cnt; i++) {
1542				if (seps.length) {
1543					date.push(seps.shift());
1544				}
1545				date.push(val[format.parts[i]]);
1546			}
1547			if (seps.length) {
1548				date.push(seps.shift());
1549			}
1550			return date.join('');
1551		},
1552		convertViewMode:  function (viewMode) {
1553			switch (viewMode) {
1554				case 4:
1555				case 'decade':
1556					viewMode = 4;
1557					break;
1558				case 3:
1559				case 'year':
1560					viewMode = 3;
1561					break;
1562				case 2:
1563				case 'month':
1564					viewMode = 2;
1565					break;
1566				case 1:
1567				case 'day':
1568					viewMode = 1;
1569					break;
1570				case 0:
1571				case 'hour':
1572					viewMode = 0;
1573					break;
1574			}
1575
1576			return viewMode;
1577		},
1578		headTemplate:     '<thead>' +
1579							  '<tr>' +
1580							  '<th class="prev"><i class="icon-arrow-left"/></th>' +
1581							  '<th colspan="5" class="switch"></th>' +
1582							  '<th class="next"><i class="icon-arrow-right"/></th>' +
1583							  '</tr>' +
1584			'</thead>',
1585		headTemplateV3:   '<thead>' +
1586							  '<tr>' +
1587							  '<th class="prev"><i class="glyphicon glyphicon-arrow-left"></i> </th>' +
1588							  '<th colspan="5" class="switch"></th>' +
1589							  '<th class="next"><i class="glyphicon glyphicon-arrow-right"></i> </th>' +
1590							  '</tr>' +
1591			'</thead>',
1592		contTemplate:     '<tbody><tr><td colspan="7"></td></tr></tbody>',
1593		footTemplate:     '<tfoot><tr><th colspan="7" class="today"></th></tr></tfoot>'
1594	};
1595	DPGlobal.template = '<div class="datetimepicker">' +
1596		'<div class="datetimepicker-minutes">' +
1597		'<table class=" table-condensed">' +
1598		DPGlobal.headTemplate +
1599		DPGlobal.contTemplate +
1600		DPGlobal.footTemplate +
1601		'</table>' +
1602		'</div>' +
1603		'<div class="datetimepicker-hours">' +
1604		'<table class=" table-condensed">' +
1605		DPGlobal.headTemplate +
1606		DPGlobal.contTemplate +
1607		DPGlobal.footTemplate +
1608		'</table>' +
1609		'</div>' +
1610		'<div class="datetimepicker-days">' +
1611		'<table class=" table-condensed">' +
1612		DPGlobal.headTemplate +
1613		'<tbody></tbody>' +
1614		DPGlobal.footTemplate +
1615		'</table>' +
1616		'</div>' +
1617		'<div class="datetimepicker-months">' +
1618		'<table class="table-condensed">' +
1619		DPGlobal.headTemplate +
1620		DPGlobal.contTemplate +
1621		DPGlobal.footTemplate +
1622		'</table>' +
1623		'</div>' +
1624		'<div class="datetimepicker-years">' +
1625		'<table class="table-condensed">' +
1626		DPGlobal.headTemplate +
1627		DPGlobal.contTemplate +
1628		DPGlobal.footTemplate +
1629		'</table>' +
1630		'</div>' +
1631		'</div>';
1632	DPGlobal.templateV3 = '<div class="datetimepicker">' +
1633		'<div class="datetimepicker-minutes">' +
1634		'<table class=" table-condensed">' +
1635		DPGlobal.headTemplateV3 +
1636		DPGlobal.contTemplate +
1637		DPGlobal.footTemplate +
1638		'</table>' +
1639		'</div>' +
1640		'<div class="datetimepicker-hours">' +
1641		'<table class=" table-condensed">' +
1642		DPGlobal.headTemplateV3 +
1643		DPGlobal.contTemplate +
1644		DPGlobal.footTemplate +
1645		'</table>' +
1646		'</div>' +
1647		'<div class="datetimepicker-days">' +
1648		'<table class=" table-condensed">' +
1649		DPGlobal.headTemplateV3 +
1650		'<tbody></tbody>' +
1651		DPGlobal.footTemplate +
1652		'</table>' +
1653		'</div>' +
1654		'<div class="datetimepicker-months">' +
1655		'<table class="table-condensed">' +
1656		DPGlobal.headTemplateV3 +
1657		DPGlobal.contTemplate +
1658		DPGlobal.footTemplate +
1659		'</table>' +
1660		'</div>' +
1661		'<div class="datetimepicker-years">' +
1662		'<table class="table-condensed">' +
1663		DPGlobal.headTemplateV3 +
1664		DPGlobal.contTemplate +
1665		DPGlobal.footTemplate +
1666		'</table>' +
1667		'</div>' +
1668		'</div>';
1669	$.fn.datetimepicker.DPGlobal = DPGlobal;
1670
1671	/* DATETIMEPICKER NO CONFLICT
1672	 * =================== */
1673
1674	$.fn.datetimepicker.noConflict = function () {
1675		$.fn.datetimepicker = old;
1676		return this;
1677	};
1678
1679	/* DATETIMEPICKER DATA-API
1680	 * ================== */
1681
1682	$(document).on(
1683		'focus.datetimepicker.data-api click.datetimepicker.data-api',
1684		'[data-provide="datetimepicker"]',
1685		function (e) {
1686			var $this = $(this);
1687			if ($this.data('datetimepicker')) return;
1688			e.preventDefault();
1689			// component click requires us to explicitly show it
1690			$this.datetimepicker('show');
1691		}
1692	);
1693	$(function () {
1694		$('[data-provide="datetimepicker-inline"]').datetimepicker();
1695	});
1696
1697}(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.