Projet

Général

Profil

Télécharger (54,4 ko) Statistiques
| Branche: | Tag: | Révision:

root / corbo / static / js / bootstrap-datetimepicker.js @ 83bedd6f

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
		// add container for single page application
48
		// when page switch the datetimepicker div will be removed also.
49
		this.container = options.container || 'body';
50

    
51
		this.language = options.language || this.element.data('date-language') || "en";
52
		this.language = this.language in dates ? this.language : "en";
53
		this.isRTL = dates[this.language].rtl || false;
54
		this.formatType = options.formatType || this.element.data('format-type') || 'standard';
55
		this.format = DPGlobal.parseFormat(options.format || this.element.data('date-format') || dates[this.language].format || DPGlobal.getDefaultFormat(this.formatType, 'input'), this.formatType);
56
		this.isInline = false;
57
		this.isVisible = false;
58
		this.isInput = this.element.is('input');
59

    
60

    
61
		this.bootcssVer = this.isInput ? (this.element.is('.form-control') ? 3 : 2) : ( this.bootcssVer = this.element.is('.input-group') ? 3 : 2 );
62

    
63
		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;
64
		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;
65
		this.hasInput = this.component && this.element.find('input').length;
66
		if (this.component && this.component.length === 0) {
67
			this.component = false;
68
		}
69
		this.linkField = options.linkField || this.element.data('link-field') || false;
70
		this.linkFormat = DPGlobal.parseFormat(options.linkFormat || this.element.data('link-format') || DPGlobal.getDefaultFormat(this.formatType, 'link'), this.formatType);
71
		this.minuteStep = options.minuteStep || this.element.data('minute-step') || 5;
72
		this.pickerPosition = options.pickerPosition || this.element.data('picker-position') || 'bottom-right';
73
		this.showMeridian = options.showMeridian || this.element.data('show-meridian') || false;
74
		this.initialDate = options.initialDate || new Date();
75

    
76
		this._attachEvents();
77

    
78
		this.formatViewType = "datetime";
79
		if ('formatViewType' in options) {
80
			this.formatViewType = options.formatViewType;
81
		} else if ('formatViewType' in this.element.data()) {
82
			this.formatViewType = this.element.data('formatViewType');
83
		}
84

    
85
		this.minView = 0;
86
		if ('minView' in options) {
87
			this.minView = options.minView;
88
		} else if ('minView' in this.element.data()) {
89
			this.minView = this.element.data('min-view');
90
		}
91
		this.minView = DPGlobal.convertViewMode(this.minView);
92

    
93
		this.maxView = DPGlobal.modes.length - 1;
94
		if ('maxView' in options) {
95
			this.maxView = options.maxView;
96
		} else if ('maxView' in this.element.data()) {
97
			this.maxView = this.element.data('max-view');
98
		}
99
		this.maxView = DPGlobal.convertViewMode(this.maxView);
100

    
101
		this.wheelViewModeNavigation = false;
102
		if ('wheelViewModeNavigation' in options) {
103
			this.wheelViewModeNavigation = options.wheelViewModeNavigation;
104
		} else if ('wheelViewModeNavigation' in this.element.data()) {
105
			this.wheelViewModeNavigation = this.element.data('view-mode-wheel-navigation');
106
		}
107

    
108
		this.wheelViewModeNavigationInverseDirection = false;
109

    
110
		if ('wheelViewModeNavigationInverseDirection' in options) {
111
			this.wheelViewModeNavigationInverseDirection = options.wheelViewModeNavigationInverseDirection;
112
		} else if ('wheelViewModeNavigationInverseDirection' in this.element.data()) {
113
			this.wheelViewModeNavigationInverseDirection = this.element.data('view-mode-wheel-navigation-inverse-dir');
114
		}
115

    
116
		this.wheelViewModeNavigationDelay = 100;
117
		if ('wheelViewModeNavigationDelay' in options) {
118
			this.wheelViewModeNavigationDelay = options.wheelViewModeNavigationDelay;
119
		} else if ('wheelViewModeNavigationDelay' in this.element.data()) {
120
			this.wheelViewModeNavigationDelay = this.element.data('view-mode-wheel-navigation-delay');
121
		}
122

    
123
		this.startViewMode = 2;
124
		if ('startView' in options) {
125
			this.startViewMode = options.startView;
126
		} else if ('startView' in this.element.data()) {
127
			this.startViewMode = this.element.data('start-view');
128
		}
129
		this.startViewMode = DPGlobal.convertViewMode(this.startViewMode);
130
		this.viewMode = this.startViewMode;
131

    
132
		this.viewSelect = this.minView;
133
		if ('viewSelect' in options) {
134
			this.viewSelect = options.viewSelect;
135
		} else if ('viewSelect' in this.element.data()) {
136
			this.viewSelect = this.element.data('view-select');
137
		}
138
		this.viewSelect = DPGlobal.convertViewMode(this.viewSelect);
139

    
140
		this.forceParse = true;
141
		if ('forceParse' in options) {
142
			this.forceParse = options.forceParse;
143
		} else if ('dateForceParse' in this.element.data()) {
144
			this.forceParse = this.element.data('date-force-parse');
145
		}
146

    
147
		this.picker = $((this.bootcssVer == 3) ? DPGlobal.templateV3 : DPGlobal.template)
148
			.appendTo(this.isInline ? this.element : this.container) // 'body')
149
			.on({
150
				click:     $.proxy(this.click, this),
151
				mousedown: $.proxy(this.mousedown, this)
152
			});
153

    
154
		if (this.wheelViewModeNavigation) {
155
			if ($.fn.mousewheel) {
156
				this.picker.on({mousewheel: $.proxy(this.mousewheel, this)});
157
			} else {
158
				console.log("Mouse Wheel event is not supported. Please include the jQuery Mouse Wheel plugin before enabling this option");
159
			}
160
		}
161

    
162
		if (this.isInline) {
163
			this.picker.addClass('datetimepicker-inline');
164
		} else {
165
			this.picker.addClass('datetimepicker-dropdown-' + this.pickerPosition + ' dropdown-menu');
166
		}
167
		if (this.isRTL) {
168
			this.picker.addClass('datetimepicker-rtl');
169
			if (this.bootcssVer == 3) {
170
				this.picker.find('.prev span, .next span')
171
					.toggleClass('glyphicon-arrow-left glyphicon-arrow-right');
172
			} else {
173
				this.picker.find('.prev i, .next i')
174
					.toggleClass('icon-arrow-left icon-arrow-right');
175
			}
176
			;
177

    
178
		}
179
		$(document).on('mousedown', function (e) {
180
			// Clicked outside the datetimepicker, hide it
181
			if ($(e.target).closest('.datetimepicker').length === 0) {
182
				that.hide();
183
			}
184
		});
185

    
186
		this.autoclose = false;
187
		if ('autoclose' in options) {
188
			this.autoclose = options.autoclose;
189
		} else if ('dateAutoclose' in this.element.data()) {
190
			this.autoclose = this.element.data('date-autoclose');
191
		}
192

    
193
		this.keyboardNavigation = true;
194
		if ('keyboardNavigation' in options) {
195
			this.keyboardNavigation = options.keyboardNavigation;
196
		} else if ('dateKeyboardNavigation' in this.element.data()) {
197
			this.keyboardNavigation = this.element.data('date-keyboard-navigation');
198
		}
199

    
200
		this.todayBtn = (options.todayBtn || this.element.data('date-today-btn') || false);
201
		this.todayHighlight = (options.todayHighlight || this.element.data('date-today-highlight') || false);
202

    
203
		this.weekStart = ((options.weekStart || this.element.data('date-weekstart') || dates[this.language].weekStart || 0) % 7);
204
		this.weekEnd = ((this.weekStart + 6) % 7);
205
		this.startDate = -Infinity;
206
		this.endDate = Infinity;
207
		this.daysOfWeekDisabled = [];
208
		this.setStartDate(options.startDate || this.element.data('date-startdate'));
209
		this.setEndDate(options.endDate || this.element.data('date-enddate'));
210
		this.setDaysOfWeekDisabled(options.daysOfWeekDisabled || this.element.data('date-days-of-week-disabled'));
211
		this.fillDow();
212
		this.fillMonths();
213
		this.update();
214
		this.showMode();
215

    
216
		if (this.isInline) {
217
			this.show();
218
		}
219
	};
220

    
221
	Datetimepicker.prototype = {
222
		constructor: Datetimepicker,
223

    
224
		_events:       [],
225
		_attachEvents: function () {
226
			this._detachEvents();
227
			if (this.isInput) { // single input
228
				this._events = [
229
					[this.element, {
230
						focus:   $.proxy(this.show, this),
231
						keyup:   $.proxy(this.update, this),
232
						keydown: $.proxy(this.keydown, this)
233
					}]
234
				];
235
			}
236
			else if (this.component && this.hasInput) { // component: input + button
237
				this._events = [
238
					// For components that are not readonly, allow keyboard nav
239
					[this.element.find('input'), {
240
						focus:   $.proxy(this.show, this),
241
						keyup:   $.proxy(this.update, this),
242
						keydown: $.proxy(this.keydown, this)
243
					}],
244
					[this.component, {
245
						click: $.proxy(this.show, this)
246
					}]
247
				];
248
				if (this.componentReset) {
249
					this._events.push([
250
						this.componentReset,
251
						{click: $.proxy(this.reset, this)}
252
					]);
253
				}
254
			}
255
			else if (this.element.is('div')) {  // inline datetimepicker
256
				this.isInline = true;
257
			}
258
			else {
259
				this._events = [
260
					[this.element, {
261
						click: $.proxy(this.show, this)
262
					}]
263
				];
264
			}
265
			for (var i = 0, el, ev; i < this._events.length; i++) {
266
				el = this._events[i][0];
267
				ev = this._events[i][1];
268
				el.on(ev);
269
			}
270
		},
271

    
272
		_detachEvents: function () {
273
			for (var i = 0, el, ev; i < this._events.length; i++) {
274
				el = this._events[i][0];
275
				ev = this._events[i][1];
276
				el.off(ev);
277
			}
278
			this._events = [];
279
		},
280

    
281
		show: function (e) {
282
			this.picker.show();
283
			this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
284
			if (this.forceParse) {
285
				this.update();
286
			}
287
			this.place();
288
			$(window).on('resize', $.proxy(this.place, this));
289
			if (e) {
290
				e.stopPropagation();
291
				e.preventDefault();
292
			}
293
			this.isVisible = true;
294
			this.element.trigger({
295
				type: 'show',
296
				date: this.date
297
			});
298
		},
299

    
300
		hide: function (e) {
301
			if (!this.isVisible) return;
302
			if (this.isInline) return;
303
			this.picker.hide();
304
			$(window).off('resize', this.place);
305
			this.viewMode = this.startViewMode;
306
			this.showMode();
307
			if (!this.isInput) {
308
				$(document).off('mousedown', this.hide);
309
			}
310

    
311
			if (
312
				this.forceParse &&
313
					(
314
						this.isInput && this.element.val() ||
315
							this.hasInput && this.element.find('input').val()
316
						)
317
				)
318
				this.setValue();
319
			this.isVisible = false;
320
			this.element.trigger({
321
				type: 'hide',
322
				date: this.date
323
			});
324
		},
325

    
326
		remove: function () {
327
			this._detachEvents();
328
			this.picker.remove();
329
			delete this.picker;
330
			delete this.element.data().datetimepicker;
331
		},
332

    
333
		getDate: function () {
334
			var d = this.getUTCDate();
335
			return new Date(d.getTime() + (d.getTimezoneOffset() * 60000));
336
		},
337

    
338
		getUTCDate: function () {
339
			return this.date;
340
		},
341

    
342
		setDate: function (d) {
343
			this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset() * 60000)));
344
		},
345

    
346
		setUTCDate: function (d) {
347
			if (d >= this.startDate && d <= this.endDate) {
348
				this.date = d;
349
				this.setValue();
350
				this.viewDate = this.date;
351
				this.fill();
352
			} else {
353
				this.element.trigger({
354
					type:      'outOfRange',
355
					date:      d,
356
					startDate: this.startDate,
357
					endDate:   this.endDate
358
				});
359
			}
360
		},
361

    
362
		setFormat: function (format) {
363
			this.format = DPGlobal.parseFormat(format, this.formatType);
364
			var element;
365
			if (this.isInput) {
366
				element = this.element;
367
			} else if (this.component) {
368
				element = this.element.find('input');
369
			}
370
			if (element && element.val()) {
371
				this.setValue();
372
			}
373
		},
374

    
375
		setValue: function () {
376
			var formatted = this.getFormattedDate();
377
			if (!this.isInput) {
378
				if (this.component) {
379
					this.element.find('input').val(formatted);
380
				}
381
				this.element.data('date', formatted);
382
			} else {
383
				this.element.val(formatted);
384
			}
385
			if (this.linkField) {
386
				$('#' + this.linkField).val(this.getFormattedDate(this.linkFormat));
387
			}
388
		},
389

    
390
		getFormattedDate: function (format) {
391
			if (format == undefined) format = this.format;
392
			return DPGlobal.formatDate(this.date, format, this.language, this.formatType);
393
		},
394

    
395
		setStartDate: function (startDate) {
396
			this.startDate = startDate || -Infinity;
397
			if (this.startDate !== -Infinity) {
398
				this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language, this.formatType);
399
			}
400
			this.update();
401
			this.updateNavArrows();
402
		},
403

    
404
		setEndDate: function (endDate) {
405
			this.endDate = endDate || Infinity;
406
			if (this.endDate !== Infinity) {
407
				this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language, this.formatType);
408
			}
409
			this.update();
410
			this.updateNavArrows();
411
		},
412

    
413
		setDaysOfWeekDisabled: function (daysOfWeekDisabled) {
414
			this.daysOfWeekDisabled = daysOfWeekDisabled || [];
415
			if (!$.isArray(this.daysOfWeekDisabled)) {
416
				this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
417
			}
418
			this.daysOfWeekDisabled = $.map(this.daysOfWeekDisabled, function (d) {
419
				return parseInt(d, 10);
420
			});
421
			this.update();
422
			this.updateNavArrows();
423
		},
424

    
425
		place: function () {
426
			if (this.isInline) return;
427

    
428
			var index_highest = 0;
429
			$('div').each(function () {
430
				var index_current = parseInt($(this).css("zIndex"), 10);
431
				if (index_current > index_highest) {
432
					index_highest = index_current;
433
				}
434
			});
435
			var zIndex = index_highest + 10;
436

    
437
			var offset, top, left, containerOffset;
438
			if (this.container instanceof $) {
439
				containerOffset = this.container.offset();
440
			} else {
441
				containerOffset = $(this.container).offset();
442
			}
443

    
444
			if (this.component) {
445
				offset = this.component.offset();
446
				left = offset.left;
447
				if (this.pickerPosition == 'bottom-left' || this.pickerPosition == 'top-left') {
448
					left += this.component.outerWidth() - this.picker.outerWidth();
449
				}
450
			} else {
451
				offset = this.element.offset();
452
				left = offset.left;
453
			}
454
			
455
			if(left+220 > document.body.clientWidth){
456
            			left = document.body.clientWidth-220;
457
          		}
458
			
459
			if (this.pickerPosition == 'top-left' || this.pickerPosition == 'top-right') {
460
				top = offset.top - this.picker.outerHeight();
461
			} else {
462
				top = offset.top + this.height;
463
			}
464

    
465
			top = top - containerOffset.top;
466
			left = left - containerOffset.left;
467

    
468
			this.picker.css({
469
				top:    top,
470
				left:   left,
471
				zIndex: zIndex
472
			});
473
		},
474

    
475
		update: function () {
476
			var date, fromArgs = false;
477
			if (arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) {
478
				date = arguments[0];
479
				fromArgs = true;
480
			} else {
481
				date = (this.isInput ? this.element.val() : this.element.find('input').val()) || this.element.data('date') || this.initialDate;
482
				if (typeof date == 'string' || date instanceof String) {
483
				  date = date.replace(/^\s+|\s+$/g,'');
484
				}
485
			}
486

    
487
			if (!date) {
488
				date = new Date();
489
				fromArgs = false;
490
			}
491

    
492
			this.date = DPGlobal.parseDate(date, this.format, this.language, this.formatType);
493

    
494
			if (fromArgs) this.setValue();
495

    
496
			if (this.date < this.startDate) {
497
				this.viewDate = new Date(this.startDate);
498
			} else if (this.date > this.endDate) {
499
				this.viewDate = new Date(this.endDate);
500
			} else {
501
				this.viewDate = new Date(this.date);
502
			}
503
			this.fill();
504
		},
505

    
506
		fillDow: function () {
507
			var dowCnt = this.weekStart,
508
				html = '<tr>';
509
			while (dowCnt < this.weekStart + 7) {
510
				html += '<th class="dow">' + dates[this.language].daysMin[(dowCnt++) % 7] + '</th>';
511
			}
512
			html += '</tr>';
513
			this.picker.find('.datetimepicker-days thead').append(html);
514
		},
515

    
516
		fillMonths: function () {
517
			var html = '',
518
				i = 0;
519
			while (i < 12) {
520
				html += '<span class="month">' + dates[this.language].monthsShort[i++] + '</span>';
521
			}
522
			this.picker.find('.datetimepicker-months td').html(html);
523
		},
524

    
525
		fill: function () {
526
			if (this.date == null || this.viewDate == null) {
527
				return;
528
			}
529
			var d = new Date(this.viewDate),
530
				year = d.getUTCFullYear(),
531
				month = d.getUTCMonth(),
532
				dayMonth = d.getUTCDate(),
533
				hours = d.getUTCHours(),
534
				minutes = d.getUTCMinutes(),
535
				startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
536
				startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
537
				endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
538
				endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
539
				currentDate = (new UTCDate(this.date.getUTCFullYear(), this.date.getUTCMonth(), this.date.getUTCDate())).valueOf(),
540
				today = new Date();
541
			this.picker.find('.datetimepicker-days thead th:eq(1)')
542
				.text(dates[this.language].months[month] + ' ' + year);
543
			if (this.formatViewType == "time") {
544
				var hourConverted = hours % 12 ? hours % 12 : 12;
545
				var hoursDisplay = (hourConverted < 10 ? '0' : '') + hourConverted;
546
				var minutesDisplay = (minutes < 10 ? '0' : '') + minutes;
547
				var meridianDisplay = dates[this.language].meridiem[hours < 12 ? 0 : 1];
548
				this.picker.find('.datetimepicker-hours thead th:eq(1)')
549
					.text(hoursDisplay + ':' + minutesDisplay + ' ' + (meridianDisplay ? meridianDisplay.toUpperCase() : ''));
550
				this.picker.find('.datetimepicker-minutes thead th:eq(1)')
551
					.text(hoursDisplay + ':' + minutesDisplay + ' ' + (meridianDisplay ? meridianDisplay.toUpperCase() : ''));
552
			} else {
553
				this.picker.find('.datetimepicker-hours thead th:eq(1)')
554
					.text(dayMonth + ' ' + dates[this.language].months[month] + ' ' + year);
555
				this.picker.find('.datetimepicker-minutes thead th:eq(1)')
556
					.text(dayMonth + ' ' + dates[this.language].months[month] + ' ' + year);
557
			}
558
			this.picker.find('tfoot th.today')
559
				.text(dates[this.language].today)
560
				.toggle(this.todayBtn !== false);
561
			this.updateNavArrows();
562
			this.fillMonths();
563
			/*var prevMonth = UTCDate(year, month, 0,0,0,0,0);
564
			 prevMonth.setUTCDate(prevMonth.getDate() - (prevMonth.getUTCDay() - this.weekStart + 7)%7);*/
565
			var prevMonth = UTCDate(year, month - 1, 28, 0, 0, 0, 0),
566
				day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
567
			prevMonth.setUTCDate(day);
568
			prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7) % 7);
569
			var nextMonth = new Date(prevMonth);
570
			nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
571
			nextMonth = nextMonth.valueOf();
572
			var html = [];
573
			var clsName;
574
			while (prevMonth.valueOf() < nextMonth) {
575
				if (prevMonth.getUTCDay() == this.weekStart) {
576
					html.push('<tr>');
577
				}
578
				clsName = '';
579
				if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
580
					clsName += ' old';
581
				} else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
582
					clsName += ' new';
583
				}
584
				// Compare internal UTC date with local today, not UTC today
585
				if (this.todayHighlight &&
586
					prevMonth.getUTCFullYear() == today.getFullYear() &&
587
					prevMonth.getUTCMonth() == today.getMonth() &&
588
					prevMonth.getUTCDate() == today.getDate()) {
589
					clsName += ' today';
590
				}
591
				if (prevMonth.valueOf() == currentDate) {
592
					clsName += ' active';
593
				}
594
				if ((prevMonth.valueOf() + 86400000) <= this.startDate || prevMonth.valueOf() > this.endDate ||
595
					$.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) {
596
					clsName += ' disabled';
597
				}
598
				html.push('<td class="day' + clsName + '">' + prevMonth.getUTCDate() + '</td>');
599
				if (prevMonth.getUTCDay() == this.weekEnd) {
600
					html.push('</tr>');
601
				}
602
				prevMonth.setUTCDate(prevMonth.getUTCDate() + 1);
603
			}
604
			this.picker.find('.datetimepicker-days tbody').empty().append(html.join(''));
605

    
606
			html = [];
607
			var txt = '', meridian = '', meridianOld = '';
608
			for (var i = 0; i < 24; i++) {
609
				var actual = UTCDate(year, month, dayMonth, i);
610
				clsName = '';
611
				// We want the previous hour for the startDate
612
				if ((actual.valueOf() + 3600000) <= this.startDate || actual.valueOf() > this.endDate) {
613
					clsName += ' disabled';
614
				} else if (hours == i) {
615
					clsName += ' active';
616
				}
617
				if (this.showMeridian && dates[this.language].meridiem.length == 2) {
618
					meridian = (i < 12 ? dates[this.language].meridiem[0] : dates[this.language].meridiem[1]);
619
					if (meridian != meridianOld) {
620
						if (meridianOld != '') {
621
							html.push('</fieldset>');
622
						}
623
						html.push('<fieldset class="hour"><legend>' + meridian.toUpperCase() + '</legend>');
624
					}
625
					meridianOld = meridian;
626
					txt = (i % 12 ? i % 12 : 12);
627
					html.push('<span class="hour' + clsName + ' hour_' + (i < 12 ? 'am' : 'pm') + '">' + txt + '</span>');
628
					if (i == 23) {
629
						html.push('</fieldset>');
630
					}
631
				} else {
632
					txt = i + ':00';
633
					html.push('<span class="hour' + clsName + '">' + txt + '</span>');
634
				}
635
			}
636
			this.picker.find('.datetimepicker-hours td').html(html.join(''));
637

    
638
			html = [];
639
			txt = '', meridian = '', meridianOld = '';
640
			for (var i = 0; i < 60; i += this.minuteStep) {
641
				var actual = UTCDate(year, month, dayMonth, hours, i, 0);
642
				clsName = '';
643
				if (actual.valueOf() < this.startDate || actual.valueOf() > this.endDate) {
644
					clsName += ' disabled';
645
				} else if (Math.floor(minutes / this.minuteStep) == Math.floor(i / this.minuteStep)) {
646
					clsName += ' active';
647
				}
648
				if (this.showMeridian && dates[this.language].meridiem.length == 2) {
649
					meridian = (hours < 12 ? dates[this.language].meridiem[0] : dates[this.language].meridiem[1]);
650
					if (meridian != meridianOld) {
651
						if (meridianOld != '') {
652
							html.push('</fieldset>');
653
						}
654
						html.push('<fieldset class="minute"><legend>' + meridian.toUpperCase() + '</legend>');
655
					}
656
					meridianOld = meridian;
657
					txt = (hours % 12 ? hours % 12 : 12);
658
					//html.push('<span class="minute'+clsName+' minute_'+(hours<12?'am':'pm')+'">'+txt+'</span>');
659
					html.push('<span class="minute' + clsName + '">' + txt + ':' + (i < 10 ? '0' + i : i) + '</span>');
660
					if (i == 59) {
661
						html.push('</fieldset>');
662
					}
663
				} else {
664
					txt = i + ':00';
665
					//html.push('<span class="hour'+clsName+'">'+txt+'</span>');
666
					html.push('<span class="minute' + clsName + '">' + hours + ':' + (i < 10 ? '0' + i : i) + '</span>');
667
				}
668
			}
669
			this.picker.find('.datetimepicker-minutes td').html(html.join(''));
670

    
671
			var currentYear = this.date.getUTCFullYear();
672
			var months = this.picker.find('.datetimepicker-months')
673
				.find('th:eq(1)')
674
				.text(year)
675
				.end()
676
				.find('span').removeClass('active');
677
			if (currentYear == year) {
678
				months.eq(this.date.getUTCMonth()).addClass('active');
679
			}
680
			if (year < startYear || year > endYear) {
681
				months.addClass('disabled');
682
			}
683
			if (year == startYear) {
684
				months.slice(0, startMonth).addClass('disabled');
685
			}
686
			if (year == endYear) {
687
				months.slice(endMonth + 1).addClass('disabled');
688
			}
689

    
690
			html = '';
691
			year = parseInt(year / 10, 10) * 10;
692
			var yearCont = this.picker.find('.datetimepicker-years')
693
				.find('th:eq(1)')
694
				.text(year + '-' + (year + 9))
695
				.end()
696
				.find('td');
697
			year -= 1;
698
			for (var i = -1; i < 11; i++) {
699
				html += '<span class="year' + (i == -1 || i == 10 ? ' old' : '') + (currentYear == year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : '') + '">' + year + '</span>';
700
				year += 1;
701
			}
702
			yearCont.html(html);
703
			this.place();
704
		},
705

    
706
		updateNavArrows: function () {
707
			var d = new Date(this.viewDate),
708
				year = d.getUTCFullYear(),
709
				month = d.getUTCMonth(),
710
				day = d.getUTCDate(),
711
				hour = d.getUTCHours();
712
			switch (this.viewMode) {
713
				case 0:
714
					if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()
715
						&& month <= this.startDate.getUTCMonth()
716
						&& day <= this.startDate.getUTCDate()
717
						&& hour <= this.startDate.getUTCHours()) {
718
						this.picker.find('.prev').css({visibility: 'hidden'});
719
					} else {
720
						this.picker.find('.prev').css({visibility: 'visible'});
721
					}
722
					if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()
723
						&& month >= this.endDate.getUTCMonth()
724
						&& day >= this.endDate.getUTCDate()
725
						&& hour >= this.endDate.getUTCHours()) {
726
						this.picker.find('.next').css({visibility: 'hidden'});
727
					} else {
728
						this.picker.find('.next').css({visibility: 'visible'});
729
					}
730
					break;
731
				case 1:
732
					if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()
733
						&& month <= this.startDate.getUTCMonth()
734
						&& day <= this.startDate.getUTCDate()) {
735
						this.picker.find('.prev').css({visibility: 'hidden'});
736
					} else {
737
						this.picker.find('.prev').css({visibility: 'visible'});
738
					}
739
					if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()
740
						&& month >= this.endDate.getUTCMonth()
741
						&& day >= this.endDate.getUTCDate()) {
742
						this.picker.find('.next').css({visibility: 'hidden'});
743
					} else {
744
						this.picker.find('.next').css({visibility: 'visible'});
745
					}
746
					break;
747
				case 2:
748
					if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()
749
						&& month <= this.startDate.getUTCMonth()) {
750
						this.picker.find('.prev').css({visibility: 'hidden'});
751
					} else {
752
						this.picker.find('.prev').css({visibility: 'visible'});
753
					}
754
					if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()
755
						&& month >= this.endDate.getUTCMonth()) {
756
						this.picker.find('.next').css({visibility: 'hidden'});
757
					} else {
758
						this.picker.find('.next').css({visibility: 'visible'});
759
					}
760
					break;
761
				case 3:
762
				case 4:
763
					if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
764
						this.picker.find('.prev').css({visibility: 'hidden'});
765
					} else {
766
						this.picker.find('.prev').css({visibility: 'visible'});
767
					}
768
					if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
769
						this.picker.find('.next').css({visibility: 'hidden'});
770
					} else {
771
						this.picker.find('.next').css({visibility: 'visible'});
772
					}
773
					break;
774
			}
775
		},
776

    
777
		mousewheel: function (e) {
778

    
779
			e.preventDefault();
780
			e.stopPropagation();
781

    
782
			if (this.wheelPause) {
783
				return;
784
			}
785

    
786
			this.wheelPause = true;
787

    
788
			var originalEvent = e.originalEvent;
789

    
790
			var delta = originalEvent.wheelDelta;
791

    
792
			var mode = delta > 0 ? 1 : (delta === 0) ? 0 : -1;
793

    
794
			if (this.wheelViewModeNavigationInverseDirection) {
795
				mode = -mode;
796
			}
797

    
798
			this.showMode(mode);
799

    
800
			setTimeout($.proxy(function () {
801

    
802
				this.wheelPause = false
803

    
804
			}, this), this.wheelViewModeNavigationDelay);
805

    
806
		},
807

    
808
		click: function (e) {
809
			e.stopPropagation();
810
			e.preventDefault();
811
			var target = $(e.target).closest('span, td, th, legend');
812
			if (target.is('.glyphicon')) {
813
				target = $(target).parent().closest('span, td, th, legend');
814
			}
815
			if (target.length == 1) {
816
				if (target.is('.disabled')) {
817
					this.element.trigger({
818
						type:      'outOfRange',
819
						date:      this.viewDate,
820
						startDate: this.startDate,
821
						endDate:   this.endDate
822
					});
823
					return;
824
				}
825
				switch (target[0].nodeName.toLowerCase()) {
826
					case 'th':
827
						switch (target[0].className) {
828
							case 'switch':
829
								this.showMode(1);
830
								break;
831
							case 'prev':
832
							case 'next':
833
								var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
834
								switch (this.viewMode) {
835
									case 0:
836
										this.viewDate = this.moveHour(this.viewDate, dir);
837
										break;
838
									case 1:
839
										this.viewDate = this.moveDate(this.viewDate, dir);
840
										break;
841
									case 2:
842
										this.viewDate = this.moveMonth(this.viewDate, dir);
843
										break;
844
									case 3:
845
									case 4:
846
										this.viewDate = this.moveYear(this.viewDate, dir);
847
										break;
848
								}
849
								this.fill();
850
								this.element.trigger({
851
									type:      target[0].className + ':' + this.convertViewModeText(this.viewMode),
852
									date:      this.viewDate,
853
									startDate: this.startDate,
854
									endDate:   this.endDate
855
								});
856
								break;
857
							case 'today':
858
								var date = new Date();
859
								date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), 0);
860

    
861
								// Respect startDate and endDate.
862
								if (date < this.startDate) date = this.startDate;
863
								else if (date > this.endDate) date = this.endDate;
864

    
865
								this.viewMode = this.startViewMode;
866
								this.showMode(0);
867
								this._setDate(date);
868
								this.fill();
869
								if (this.autoclose) {
870
									this.hide();
871
								}
872
								break;
873
						}
874
						break;
875
					case 'span':
876
						if (!target.is('.disabled')) {
877
							var year = this.viewDate.getUTCFullYear(),
878
								month = this.viewDate.getUTCMonth(),
879
								day = this.viewDate.getUTCDate(),
880
								hours = this.viewDate.getUTCHours(),
881
								minutes = this.viewDate.getUTCMinutes(),
882
								seconds = this.viewDate.getUTCSeconds();
883

    
884
							if (target.is('.month')) {
885
								this.viewDate.setUTCDate(1);
886
								month = target.parent().find('span').index(target);
887
								day = this.viewDate.getUTCDate();
888
								this.viewDate.setUTCMonth(month);
889
								this.element.trigger({
890
									type: 'changeMonth',
891
									date: this.viewDate
892
								});
893
								if (this.viewSelect >= 3) {
894
									this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0));
895
								}
896
							} else if (target.is('.year')) {
897
								this.viewDate.setUTCDate(1);
898
								year = parseInt(target.text(), 10) || 0;
899
								this.viewDate.setUTCFullYear(year);
900
								this.element.trigger({
901
									type: 'changeYear',
902
									date: this.viewDate
903
								});
904
								if (this.viewSelect >= 4) {
905
									this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0));
906
								}
907
							} else if (target.is('.hour')) {
908
								hours = parseInt(target.text(), 10) || 0;
909
								if (target.hasClass('hour_am') || target.hasClass('hour_pm')) {
910
									if (hours == 12 && target.hasClass('hour_am')) {
911
										hours = 0;
912
									} else if (hours != 12 && target.hasClass('hour_pm')) {
913
										hours += 12;
914
									}
915
								}
916
								this.viewDate.setUTCHours(hours);
917
								this.element.trigger({
918
									type: 'changeHour',
919
									date: this.viewDate
920
								});
921
								if (this.viewSelect >= 1) {
922
									this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0));
923
								}
924
							} else if (target.is('.minute')) {
925
								minutes = parseInt(target.text().substr(target.text().indexOf(':') + 1), 10) || 0;
926
								this.viewDate.setUTCMinutes(minutes);
927
								this.element.trigger({
928
									type: 'changeMinute',
929
									date: this.viewDate
930
								});
931
								if (this.viewSelect >= 0) {
932
									this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0));
933
								}
934
							}
935
							if (this.viewMode != 0) {
936
								var oldViewMode = this.viewMode;
937
								this.showMode(-1);
938
								this.fill();
939
								if (oldViewMode == this.viewMode && this.autoclose) {
940
									this.hide();
941
								}
942
							} else {
943
								this.fill();
944
								if (this.autoclose) {
945
									this.hide();
946
								}
947
							}
948
						}
949
						break;
950
					case 'td':
951
						if (target.is('.day') && !target.is('.disabled')) {
952
							var day = parseInt(target.text(), 10) || 1;
953
							var year = this.viewDate.getUTCFullYear(),
954
								month = this.viewDate.getUTCMonth(),
955
								hours = this.viewDate.getUTCHours(),
956
								minutes = this.viewDate.getUTCMinutes(),
957
								seconds = this.viewDate.getUTCSeconds();
958
							if (target.is('.old')) {
959
								if (month === 0) {
960
									month = 11;
961
									year -= 1;
962
								} else {
963
									month -= 1;
964
								}
965
							} else if (target.is('.new')) {
966
								if (month == 11) {
967
									month = 0;
968
									year += 1;
969
								} else {
970
									month += 1;
971
								}
972
							}
973
							this.viewDate.setUTCFullYear(year);
974
							this.viewDate.setUTCMonth(month, day);
975
							this.element.trigger({
976
								type: 'changeDay',
977
								date: this.viewDate
978
							});
979
							if (this.viewSelect >= 2) {
980
								this._setDate(UTCDate(year, month, day, hours, minutes, seconds, 0));
981
							}
982
						}
983
						var oldViewMode = this.viewMode;
984
						this.showMode(-1);
985
						this.fill();
986
						if (oldViewMode == this.viewMode && this.autoclose) {
987
							this.hide();
988
						}
989
						break;
990
				}
991
			}
992
		},
993

    
994
		_setDate: function (date, which) {
995
			if (!which || which == 'date')
996
				this.date = date;
997
			if (!which || which == 'view')
998
				this.viewDate = date;
999
			this.fill();
1000
			this.setValue();
1001
			var element;
1002
			if (this.isInput) {
1003
				element = this.element;
1004
			} else if (this.component) {
1005
				element = this.element.find('input');
1006
			}
1007
			if (element) {
1008
				element.change();
1009
				if (this.autoclose && (!which || which == 'date')) {
1010
					//this.hide();
1011
				}
1012
			}
1013
			this.element.trigger({
1014
				type: 'changeDate',
1015
				date: this.date
1016
			});
1017
		},
1018

    
1019
		moveMinute: function (date, dir) {
1020
			if (!dir) return date;
1021
			var new_date = new Date(date.valueOf());
1022
			//dir = dir > 0 ? 1 : -1;
1023
			new_date.setUTCMinutes(new_date.getUTCMinutes() + (dir * this.minuteStep));
1024
			return new_date;
1025
		},
1026

    
1027
		moveHour: function (date, dir) {
1028
			if (!dir) return date;
1029
			var new_date = new Date(date.valueOf());
1030
			//dir = dir > 0 ? 1 : -1;
1031
			new_date.setUTCHours(new_date.getUTCHours() + dir);
1032
			return new_date;
1033
		},
1034

    
1035
		moveDate: function (date, dir) {
1036
			if (!dir) return date;
1037
			var new_date = new Date(date.valueOf());
1038
			//dir = dir > 0 ? 1 : -1;
1039
			new_date.setUTCDate(new_date.getUTCDate() + dir);
1040
			return new_date;
1041
		},
1042

    
1043
		moveMonth: function (date, dir) {
1044
			if (!dir) return date;
1045
			var new_date = new Date(date.valueOf()),
1046
				day = new_date.getUTCDate(),
1047
				month = new_date.getUTCMonth(),
1048
				mag = Math.abs(dir),
1049
				new_month, test;
1050
			dir = dir > 0 ? 1 : -1;
1051
			if (mag == 1) {
1052
				test = dir == -1
1053
					// If going back one month, make sure month is not current month
1054
					// (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
1055
					? function () {
1056
					return new_date.getUTCMonth() == month;
1057
				}
1058
					// If going forward one month, make sure month is as expected
1059
					// (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
1060
					: function () {
1061
					return new_date.getUTCMonth() != new_month;
1062
				};
1063
				new_month = month + dir;
1064
				new_date.setUTCMonth(new_month);
1065
				// Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
1066
				if (new_month < 0 || new_month > 11)
1067
					new_month = (new_month + 12) % 12;
1068
			} else {
1069
				// For magnitudes >1, move one month at a time...
1070
				for (var i = 0; i < mag; i++)
1071
					// ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
1072
					new_date = this.moveMonth(new_date, dir);
1073
				// ...then reset the day, keeping it in the new month
1074
				new_month = new_date.getUTCMonth();
1075
				new_date.setUTCDate(day);
1076
				test = function () {
1077
					return new_month != new_date.getUTCMonth();
1078
				};
1079
			}
1080
			// Common date-resetting loop -- if date is beyond end of month, make it
1081
			// end of month
1082
			while (test()) {
1083
				new_date.setUTCDate(--day);
1084
				new_date.setUTCMonth(new_month);
1085
			}
1086
			return new_date;
1087
		},
1088

    
1089
		moveYear: function (date, dir) {
1090
			return this.moveMonth(date, dir * 12);
1091
		},
1092

    
1093
		dateWithinRange: function (date) {
1094
			return date >= this.startDate && date <= this.endDate;
1095
		},
1096

    
1097
		keydown: function (e) {
1098
			if (this.picker.is(':not(:visible)')) {
1099
				if (e.keyCode == 27) // allow escape to hide and re-show picker
1100
					this.show();
1101
				return;
1102
			}
1103
			var dateChanged = false,
1104
				dir, day, month,
1105
				newDate, newViewDate;
1106
			switch (e.keyCode) {
1107
				case 27: // escape
1108
					this.hide();
1109
					e.preventDefault();
1110
					break;
1111
				case 37: // left
1112
				case 39: // right
1113
					if (!this.keyboardNavigation) break;
1114
					dir = e.keyCode == 37 ? -1 : 1;
1115
					viewMode = this.viewMode;
1116
					if (e.ctrlKey) {
1117
						viewMode += 2;
1118
					} else if (e.shiftKey) {
1119
						viewMode += 1;
1120
					}
1121
					if (viewMode == 4) {
1122
						newDate = this.moveYear(this.date, dir);
1123
						newViewDate = this.moveYear(this.viewDate, dir);
1124
					} else if (viewMode == 3) {
1125
						newDate = this.moveMonth(this.date, dir);
1126
						newViewDate = this.moveMonth(this.viewDate, dir);
1127
					} else if (viewMode == 2) {
1128
						newDate = this.moveDate(this.date, dir);
1129
						newViewDate = this.moveDate(this.viewDate, dir);
1130
					} else if (viewMode == 1) {
1131
						newDate = this.moveHour(this.date, dir);
1132
						newViewDate = this.moveHour(this.viewDate, dir);
1133
					} else if (viewMode == 0) {
1134
						newDate = this.moveMinute(this.date, dir);
1135
						newViewDate = this.moveMinute(this.viewDate, dir);
1136
					}
1137
					if (this.dateWithinRange(newDate)) {
1138
						this.date = newDate;
1139
						this.viewDate = newViewDate;
1140
						this.setValue();
1141
						this.update();
1142
						e.preventDefault();
1143
						dateChanged = true;
1144
					}
1145
					break;
1146
				case 38: // up
1147
				case 40: // down
1148
					if (!this.keyboardNavigation) break;
1149
					dir = e.keyCode == 38 ? -1 : 1;
1150
					viewMode = this.viewMode;
1151
					if (e.ctrlKey) {
1152
						viewMode += 2;
1153
					} else if (e.shiftKey) {
1154
						viewMode += 1;
1155
					}
1156
					if (viewMode == 4) {
1157
						newDate = this.moveYear(this.date, dir);
1158
						newViewDate = this.moveYear(this.viewDate, dir);
1159
					} else if (viewMode == 3) {
1160
						newDate = this.moveMonth(this.date, dir);
1161
						newViewDate = this.moveMonth(this.viewDate, dir);
1162
					} else if (viewMode == 2) {
1163
						newDate = this.moveDate(this.date, dir * 7);
1164
						newViewDate = this.moveDate(this.viewDate, dir * 7);
1165
					} else if (viewMode == 1) {
1166
						if (this.showMeridian) {
1167
							newDate = this.moveHour(this.date, dir * 6);
1168
							newViewDate = this.moveHour(this.viewDate, dir * 6);
1169
						} else {
1170
							newDate = this.moveHour(this.date, dir * 4);
1171
							newViewDate = this.moveHour(this.viewDate, dir * 4);
1172
						}
1173
					} else if (viewMode == 0) {
1174
						newDate = this.moveMinute(this.date, dir * 4);
1175
						newViewDate = this.moveMinute(this.viewDate, dir * 4);
1176
					}
1177
					if (this.dateWithinRange(newDate)) {
1178
						this.date = newDate;
1179
						this.viewDate = newViewDate;
1180
						this.setValue();
1181
						this.update();
1182
						e.preventDefault();
1183
						dateChanged = true;
1184
					}
1185
					break;
1186
				case 13: // enter
1187
					if (this.viewMode != 0) {
1188
						var oldViewMode = this.viewMode;
1189
						this.showMode(-1);
1190
						this.fill();
1191
						if (oldViewMode == this.viewMode && this.autoclose) {
1192
							this.hide();
1193
						}
1194
					} else {
1195
						this.fill();
1196
						if (this.autoclose) {
1197
							this.hide();
1198
						}
1199
					}
1200
					e.preventDefault();
1201
					break;
1202
				case 9: // tab
1203
					this.hide();
1204
					break;
1205
			}
1206
			if (dateChanged) {
1207
				var element;
1208
				if (this.isInput) {
1209
					element = this.element;
1210
				} else if (this.component) {
1211
					element = this.element.find('input');
1212
				}
1213
				if (element) {
1214
					element.change();
1215
				}
1216
				this.element.trigger({
1217
					type: 'changeDate',
1218
					date: this.date
1219
				});
1220
			}
1221
		},
1222

    
1223
		showMode: function (dir) {
1224
			if (dir) {
1225
				var newViewMode = Math.max(0, Math.min(DPGlobal.modes.length - 1, this.viewMode + dir));
1226
				if (newViewMode >= this.minView && newViewMode <= this.maxView) {
1227
					this.element.trigger({
1228
						type:        'changeMode',
1229
						date:        this.viewDate,
1230
						oldViewMode: this.viewMode,
1231
						newViewMode: newViewMode
1232
					});
1233

    
1234
					this.viewMode = newViewMode;
1235
				}
1236
			}
1237
			/*
1238
			 vitalets: fixing bug of very special conditions:
1239
			 jquery 1.7.1 + webkit + show inline datetimepicker in bootstrap popover.
1240
			 Method show() does not set display css correctly and datetimepicker is not shown.
1241
			 Changed to .css('display', 'block') solve the problem.
1242
			 See https://github.com/vitalets/x-editable/issues/37
1243

    
1244
			 In jquery 1.7.2+ everything works fine.
1245
			 */
1246
			//this.picker.find('>div').hide().filter('.datetimepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
1247
			this.picker.find('>div').hide().filter('.datetimepicker-' + DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
1248
			this.updateNavArrows();
1249
		},
1250

    
1251
		reset: function (e) {
1252
			this._setDate(null, 'date');
1253
		},
1254

    
1255
		convertViewModeText:  function (viewMode) {
1256
			switch (viewMode) {
1257
				case 4:
1258
					return 'decade';
1259
				case 3:
1260
					return 'year';
1261
				case 2:
1262
					return 'month';
1263
				case 1:
1264
					return 'day';
1265
				case 0:
1266
					return 'hour';
1267
			}
1268
		}
1269
	};
1270

    
1271
	$.fn.datetimepicker = function (option) {
1272
		var args = Array.apply(null, arguments);
1273
		args.shift();
1274
		var internal_return;
1275
		this.each(function () {
1276
			var $this = $(this),
1277
				data = $this.data('datetimepicker'),
1278
				options = typeof option == 'object' && option;
1279
			if (!data) {
1280
				$this.data('datetimepicker', (data = new Datetimepicker(this, $.extend({}, $.fn.datetimepicker.defaults, options))));
1281
			}
1282
			if (typeof option == 'string' && typeof data[option] == 'function') {
1283
				internal_return = data[option].apply(data, args);
1284
				if (internal_return !== undefined) {
1285
					return false;
1286
				}
1287
			}
1288
		});
1289
		if (internal_return !== undefined)
1290
			return internal_return;
1291
		else
1292
			return this;
1293
	};
1294

    
1295
	$.fn.datetimepicker.defaults = {
1296
	};
1297
	$.fn.datetimepicker.Constructor = Datetimepicker;
1298
	var dates = $.fn.datetimepicker.dates = {
1299
		en: {
1300
			days:        ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
1301
			daysShort:   ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
1302
			daysMin:     ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
1303
			months:      ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
1304
			monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
1305
			meridiem:    ["am", "pm"],
1306
			suffix:      ["st", "nd", "rd", "th"],
1307
			today:       "Today"
1308
		}
1309
	};
1310

    
1311
	var DPGlobal = {
1312
		modes:            [
1313
			{
1314
				clsName: 'minutes',
1315
				navFnc:  'Hours',
1316
				navStep: 1
1317
			},
1318
			{
1319
				clsName: 'hours',
1320
				navFnc:  'Date',
1321
				navStep: 1
1322
			},
1323
			{
1324
				clsName: 'days',
1325
				navFnc:  'Month',
1326
				navStep: 1
1327
			},
1328
			{
1329
				clsName: 'months',
1330
				navFnc:  'FullYear',
1331
				navStep: 1
1332
			},
1333
			{
1334
				clsName: 'years',
1335
				navFnc:  'FullYear',
1336
				navStep: 10
1337
			}
1338
		],
1339
		isLeapYear:       function (year) {
1340
			return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
1341
		},
1342
		getDaysInMonth:   function (year, month) {
1343
			return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
1344
		},
1345
		getDefaultFormat: function (type, field) {
1346
			if (type == "standard") {
1347
				if (field == 'input')
1348
					return 'yyyy-mm-dd hh:ii';
1349
				else
1350
					return 'yyyy-mm-dd hh:ii:ss';
1351
			} else if (type == "php") {
1352
				if (field == 'input')
1353
					return 'Y-m-d H:i';
1354
				else
1355
					return 'Y-m-d H:i:s';
1356
			} else {
1357
				throw new Error("Invalid format type.");
1358
			}
1359
		},
1360
		validParts:       function (type) {
1361
			if (type == "standard") {
1362
				return /hh?|HH?|p|P|ii?|ss?|dd?|DD?|mm?|MM?|yy(?:yy)?/g;
1363
			} else if (type == "php") {
1364
				return /[dDjlNwzFmMnStyYaABgGhHis]/g;
1365
			} else {
1366
				throw new Error("Invalid format type.");
1367
			}
1368
		},
1369
		nonpunctuation:   /[^ -\/:-@\[-`{-~\t\n\rTZ]+/g,
1370
		parseFormat:      function (format, type) {
1371
			// IE treats \0 as a string end in inputs (truncating the value),
1372
			// so it's a bad format delimiter, anyway
1373
			var separators = format.replace(this.validParts(type), '\0').split('\0'),
1374
				parts = format.match(this.validParts(type));
1375
			if (!separators || !separators.length || !parts || parts.length == 0) {
1376
				throw new Error("Invalid date format.");
1377
			}
1378
			return {separators: separators, parts: parts};
1379
		},
1380
		parseDate:        function (date, format, language, type) {
1381
			if (date instanceof Date) {
1382
				var dateUTC = new Date(date.valueOf() - date.getTimezoneOffset() * 60000);
1383
				dateUTC.setMilliseconds(0);
1384
				return dateUTC;
1385
			}
1386
			if (/^\d{4}\-\d{1,2}\-\d{1,2}$/.test(date)) {
1387
				format = this.parseFormat('yyyy-mm-dd', type);
1388
			}
1389
			if (/^\d{4}\-\d{1,2}\-\d{1,2}[T ]\d{1,2}\:\d{1,2}$/.test(date)) {
1390
				format = this.parseFormat('yyyy-mm-dd hh:ii', type);
1391
			}
1392
			if (/^\d{4}\-\d{1,2}\-\d{1,2}[T ]\d{1,2}\:\d{1,2}\:\d{1,2}[Z]{0,1}$/.test(date)) {
1393
				format = this.parseFormat('yyyy-mm-dd hh:ii:ss', type);
1394
			}
1395
			if (/^[-+]\d+[dmwy]([\s,]+[-+]\d+[dmwy])*$/.test(date)) {
1396
				var part_re = /([-+]\d+)([dmwy])/,
1397
					parts = date.match(/([-+]\d+)([dmwy])/g),
1398
					part, dir;
1399
				date = new Date();
1400
				for (var i = 0; i < parts.length; i++) {
1401
					part = part_re.exec(parts[i]);
1402
					dir = parseInt(part[1]);
1403
					switch (part[2]) {
1404
						case 'd':
1405
							date.setUTCDate(date.getUTCDate() + dir);
1406
							break;
1407
						case 'm':
1408
							date = Datetimepicker.prototype.moveMonth.call(Datetimepicker.prototype, date, dir);
1409
							break;
1410
						case 'w':
1411
							date.setUTCDate(date.getUTCDate() + dir * 7);
1412
							break;
1413
						case 'y':
1414
							date = Datetimepicker.prototype.moveYear.call(Datetimepicker.prototype, date, dir);
1415
							break;
1416
					}
1417
				}
1418
				return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), 0);
1419
			}
1420
			var parts = date && date.match(this.nonpunctuation) || [],
1421
				date = new Date(0, 0, 0, 0, 0, 0, 0),
1422
				parsed = {},
1423
				setters_order = ['hh', 'h', 'ii', 'i', 'ss', 's', 'yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'D', 'DD', 'd', 'dd', 'H', 'HH', 'p', 'P'],
1424
				setters_map = {
1425
					hh:   function (d, v) {
1426
						return d.setUTCHours(v);
1427
					},
1428
					h:    function (d, v) {
1429
						return d.setUTCHours(v);
1430
					},
1431
					HH:   function (d, v) {
1432
						return d.setUTCHours(v == 12 ? 0 : v);
1433
					},
1434
					H:    function (d, v) {
1435
						return d.setUTCHours(v == 12 ? 0 : v);
1436
					},
1437
					ii:   function (d, v) {
1438
						return d.setUTCMinutes(v);
1439
					},
1440
					i:    function (d, v) {
1441
						return d.setUTCMinutes(v);
1442
					},
1443
					ss:   function (d, v) {
1444
						return d.setUTCSeconds(v);
1445
					},
1446
					s:    function (d, v) {
1447
						return d.setUTCSeconds(v);
1448
					},
1449
					yyyy: function (d, v) {
1450
						return d.setUTCFullYear(v);
1451
					},
1452
					yy:   function (d, v) {
1453
						return d.setUTCFullYear(2000 + v);
1454
					},
1455
					m:    function (d, v) {
1456
						v -= 1;
1457
						while (v < 0) v += 12;
1458
						v %= 12;
1459
						d.setUTCMonth(v);
1460
						while (d.getUTCMonth() != v)
1461
							if (isNaN(d.getUTCMonth()))
1462
								return d;
1463
							else
1464
								d.setUTCDate(d.getUTCDate() - 1);
1465
						return d;
1466
					},
1467
					d:    function (d, v) {
1468
						return d.setUTCDate(v);
1469
					},
1470
					p:    function (d, v) {
1471
						return d.setUTCHours(v == 1 ? d.getUTCHours() + 12 : d.getUTCHours());
1472
					}
1473
				},
1474
				val, filtered, part;
1475
			setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
1476
			setters_map['dd'] = setters_map['d'];
1477
			setters_map['P'] = setters_map['p'];
1478
			date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds());
1479
			if (parts.length == format.parts.length) {
1480
				for (var i = 0, cnt = format.parts.length; i < cnt; i++) {
1481
					val = parseInt(parts[i], 10);
1482
					part = format.parts[i];
1483
					if (isNaN(val)) {
1484
						switch (part) {
1485
							case 'MM':
1486
								filtered = $(dates[language].months).filter(function () {
1487
									var m = this.slice(0, parts[i].length),
1488
										p = parts[i].slice(0, m.length);
1489
									return m == p;
1490
								});
1491
								val = $.inArray(filtered[0], dates[language].months) + 1;
1492
								break;
1493
							case 'M':
1494
								filtered = $(dates[language].monthsShort).filter(function () {
1495
									var m = this.slice(0, parts[i].length),
1496
										p = parts[i].slice(0, m.length);
1497
									return m.toLowerCase() == p.toLowerCase();
1498
								});
1499
								val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
1500
								break;
1501
							case 'p':
1502
							case 'P':
1503
								val = $.inArray(parts[i].toLowerCase(), dates[language].meridiem);
1504
								break;
1505
						}
1506
					}
1507
					parsed[part] = val;
1508
				}
1509
				for (var i = 0, s; i < setters_order.length; i++) {
1510
					s = setters_order[i];
1511
					if (s in parsed && !isNaN(parsed[s]))
1512
						setters_map[s](date, parsed[s])
1513
				}
1514
			}
1515
			return date;
1516
		},
1517
		formatDate:       function (date, format, language, type) {
1518
			if (date == null) {
1519
				return '';
1520
			}
1521
			var val;
1522
			if (type == 'standard') {
1523
				val = {
1524
					// year
1525
					yy:   date.getUTCFullYear().toString().substring(2),
1526
					yyyy: date.getUTCFullYear(),
1527
					// month
1528
					m:    date.getUTCMonth() + 1,
1529
					M:    dates[language].monthsShort[date.getUTCMonth()],
1530
					MM:   dates[language].months[date.getUTCMonth()],
1531
					// day
1532
					d:    date.getUTCDate(),
1533
					D:    dates[language].daysShort[date.getUTCDay()],
1534
					DD:   dates[language].days[date.getUTCDay()],
1535
					p:    (dates[language].meridiem.length == 2 ? dates[language].meridiem[date.getUTCHours() < 12 ? 0 : 1] : ''),
1536
					// hour
1537
					h:    date.getUTCHours(),
1538
					// minute
1539
					i:    date.getUTCMinutes(),
1540
					// second
1541
					s:    date.getUTCSeconds()
1542
				};
1543

    
1544
				if (dates[language].meridiem.length == 2) {
1545
					val.H = (val.h % 12 == 0 ? 12 : val.h % 12);
1546
				}
1547
				else {
1548
					val.H = val.h;
1549
				}
1550
				val.HH = (val.H < 10 ? '0' : '') + val.H;
1551
				val.P = val.p.toUpperCase();
1552
				val.hh = (val.h < 10 ? '0' : '') + val.h;
1553
				val.ii = (val.i < 10 ? '0' : '') + val.i;
1554
				val.ss = (val.s < 10 ? '0' : '') + val.s;
1555
				val.dd = (val.d < 10 ? '0' : '') + val.d;
1556
				val.mm = (val.m < 10 ? '0' : '') + val.m;
1557
			} else if (type == 'php') {
1558
				// php format
1559
				val = {
1560
					// year
1561
					y: date.getUTCFullYear().toString().substring(2),
1562
					Y: date.getUTCFullYear(),
1563
					// month
1564
					F: dates[language].months[date.getUTCMonth()],
1565
					M: dates[language].monthsShort[date.getUTCMonth()],
1566
					n: date.getUTCMonth() + 1,
1567
					t: DPGlobal.getDaysInMonth(date.getUTCFullYear(), date.getUTCMonth()),
1568
					// day
1569
					j: date.getUTCDate(),
1570
					l: dates[language].days[date.getUTCDay()],
1571
					D: dates[language].daysShort[date.getUTCDay()],
1572
					w: date.getUTCDay(), // 0 -> 6
1573
					N: (date.getUTCDay() == 0 ? 7 : date.getUTCDay()),       // 1 -> 7
1574
					S: (date.getUTCDate() % 10 <= dates[language].suffix.length ? dates[language].suffix[date.getUTCDate() % 10 - 1] : ''),
1575
					// hour
1576
					a: (dates[language].meridiem.length == 2 ? dates[language].meridiem[date.getUTCHours() < 12 ? 0 : 1] : ''),
1577
					g: (date.getUTCHours() % 12 == 0 ? 12 : date.getUTCHours() % 12),
1578
					G: date.getUTCHours(),
1579
					// minute
1580
					i: date.getUTCMinutes(),
1581
					// second
1582
					s: date.getUTCSeconds()
1583
				};
1584
				val.m = (val.n < 10 ? '0' : '') + val.n;
1585
				val.d = (val.j < 10 ? '0' : '') + val.j;
1586
				val.A = val.a.toString().toUpperCase();
1587
				val.h = (val.g < 10 ? '0' : '') + val.g;
1588
				val.H = (val.G < 10 ? '0' : '') + val.G;
1589
				val.i = (val.i < 10 ? '0' : '') + val.i;
1590
				val.s = (val.s < 10 ? '0' : '') + val.s;
1591
			} else {
1592
				throw new Error("Invalid format type.");
1593
			}
1594
			var date = [],
1595
				seps = $.extend([], format.separators);
1596
			for (var i = 0, cnt = format.parts.length; i < cnt; i++) {
1597
				if (seps.length) {
1598
					date.push(seps.shift());
1599
				}
1600
				date.push(val[format.parts[i]]);
1601
			}
1602
			if (seps.length) {
1603
				date.push(seps.shift());
1604
			}
1605
			return date.join('');
1606
		},
1607
		convertViewMode:  function (viewMode) {
1608
			switch (viewMode) {
1609
				case 4:
1610
				case 'decade':
1611
					viewMode = 4;
1612
					break;
1613
				case 3:
1614
				case 'year':
1615
					viewMode = 3;
1616
					break;
1617
				case 2:
1618
				case 'month':
1619
					viewMode = 2;
1620
					break;
1621
				case 1:
1622
				case 'day':
1623
					viewMode = 1;
1624
					break;
1625
				case 0:
1626
				case 'hour':
1627
					viewMode = 0;
1628
					break;
1629
			}
1630

    
1631
			return viewMode;
1632
		},
1633
		headTemplate:     '<thead>' +
1634
							  '<tr>' +
1635
							  '<th class="prev"><i class="icon-arrow-left"/></th>' +
1636
							  '<th colspan="5" class="switch"></th>' +
1637
							  '<th class="next"><i class="icon-arrow-right"/></th>' +
1638
							  '</tr>' +
1639
			'</thead>',
1640
		headTemplateV3:   '<thead>' +
1641
							  '<tr>' +
1642
							  '<th class="prev"><span class="glyphicon glyphicon-arrow-left"></span> </th>' +
1643
							  '<th colspan="5" class="switch"></th>' +
1644
							  '<th class="next"><span class="glyphicon glyphicon-arrow-right"></span> </th>' +
1645
							  '</tr>' +
1646
			'</thead>',
1647
		contTemplate:     '<tbody><tr><td colspan="7"></td></tr></tbody>',
1648
		footTemplate:     '<tfoot><tr><th colspan="7" class="today"></th></tr></tfoot>'
1649
	};
1650
	DPGlobal.template = '<div class="datetimepicker">' +
1651
		'<div class="datetimepicker-minutes">' +
1652
		'<table class=" table-condensed">' +
1653
		DPGlobal.headTemplate +
1654
		DPGlobal.contTemplate +
1655
		DPGlobal.footTemplate +
1656
		'</table>' +
1657
		'</div>' +
1658
		'<div class="datetimepicker-hours">' +
1659
		'<table class=" table-condensed">' +
1660
		DPGlobal.headTemplate +
1661
		DPGlobal.contTemplate +
1662
		DPGlobal.footTemplate +
1663
		'</table>' +
1664
		'</div>' +
1665
		'<div class="datetimepicker-days">' +
1666
		'<table class=" table-condensed">' +
1667
		DPGlobal.headTemplate +
1668
		'<tbody></tbody>' +
1669
		DPGlobal.footTemplate +
1670
		'</table>' +
1671
		'</div>' +
1672
		'<div class="datetimepicker-months">' +
1673
		'<table class="table-condensed">' +
1674
		DPGlobal.headTemplate +
1675
		DPGlobal.contTemplate +
1676
		DPGlobal.footTemplate +
1677
		'</table>' +
1678
		'</div>' +
1679
		'<div class="datetimepicker-years">' +
1680
		'<table class="table-condensed">' +
1681
		DPGlobal.headTemplate +
1682
		DPGlobal.contTemplate +
1683
		DPGlobal.footTemplate +
1684
		'</table>' +
1685
		'</div>' +
1686
		'</div>';
1687
	DPGlobal.templateV3 = '<div class="datetimepicker">' +
1688
		'<div class="datetimepicker-minutes">' +
1689
		'<table class=" table-condensed">' +
1690
		DPGlobal.headTemplateV3 +
1691
		DPGlobal.contTemplate +
1692
		DPGlobal.footTemplate +
1693
		'</table>' +
1694
		'</div>' +
1695
		'<div class="datetimepicker-hours">' +
1696
		'<table class=" table-condensed">' +
1697
		DPGlobal.headTemplateV3 +
1698
		DPGlobal.contTemplate +
1699
		DPGlobal.footTemplate +
1700
		'</table>' +
1701
		'</div>' +
1702
		'<div class="datetimepicker-days">' +
1703
		'<table class=" table-condensed">' +
1704
		DPGlobal.headTemplateV3 +
1705
		'<tbody></tbody>' +
1706
		DPGlobal.footTemplate +
1707
		'</table>' +
1708
		'</div>' +
1709
		'<div class="datetimepicker-months">' +
1710
		'<table class="table-condensed">' +
1711
		DPGlobal.headTemplateV3 +
1712
		DPGlobal.contTemplate +
1713
		DPGlobal.footTemplate +
1714
		'</table>' +
1715
		'</div>' +
1716
		'<div class="datetimepicker-years">' +
1717
		'<table class="table-condensed">' +
1718
		DPGlobal.headTemplateV3 +
1719
		DPGlobal.contTemplate +
1720
		DPGlobal.footTemplate +
1721
		'</table>' +
1722
		'</div>' +
1723
		'</div>';
1724
	$.fn.datetimepicker.DPGlobal = DPGlobal;
1725

    
1726
	/* DATETIMEPICKER NO CONFLICT
1727
	 * =================== */
1728

    
1729
	$.fn.datetimepicker.noConflict = function () {
1730
		$.fn.datetimepicker = old;
1731
		return this;
1732
	};
1733

    
1734
	/* DATETIMEPICKER DATA-API
1735
	 * ================== */
1736

    
1737
	$(document).on(
1738
		'focus.datetimepicker.data-api click.datetimepicker.data-api',
1739
		'[data-provide="datetimepicker"]',
1740
		function (e) {
1741
			var $this = $(this);
1742
			if ($this.data('datetimepicker')) return;
1743
			e.preventDefault();
1744
			// component click requires us to explicitly show it
1745
			$this.datetimepicker('show');
1746
		}
1747
	);
1748
	$(function () {
1749
		$('[data-provide="datetimepicker-inline"]').datetimepicker();
1750
	});
1751

    
1752
}(window.jQuery);
(1-1/2)