Issues (75)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

js/dateTimePicker.js (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
/*! jQuery Timepicker Addon - v1.4.6 - 2014-08-09
2
* http://trentrichardson.com/examples/timepicker
3
* Copyright (c) 2014 Trent Richardson; Licensed MIT */
4
(function ($) {
5
6
	/*
7
	* Lets not redefine timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded"
8
	*/
9
	$.ui.timepicker = $.ui.timepicker || {};
10
	if ($.ui.timepicker.version) {
11
		return;
12
	}
13
14
	/*
15
	* Extend jQueryUI, get it started with our version number
16
	*/
17
	$.extend($.ui, {
18
		timepicker: {
19
			version: "1.4.6"
20
		}
21
	});
22
23
	/* 
24
	* Timepicker manager.
25
	* Use the singleton instance of this class, $.timepicker, to interact with the time picker.
26
	* Settings for (groups of) time pickers are maintained in an instance object,
27
	* allowing multiple different settings on the same page.
28
	*/
29
	var Timepicker = function () {
30
		this.regional = []; // Available regional settings, indexed by language code
31
		this.regional[''] = { // Default regional settings
32
			currentText: 'Now',
33
			closeText: 'Done',
34
			amNames: ['AM', 'A'],
35
			pmNames: ['PM', 'P'],
36
			timeFormat: 'HH:mm',
37
			timeSuffix: '',
38
			timeOnlyTitle: 'Choose Time',
39
			timeText: 'Time',
40
			hourText: 'Hour',
41
			minuteText: 'Minute',
42
			secondText: 'Second',
43
			millisecText: 'Millisecond',
44
			microsecText: 'Microsecond',
45
			timezoneText: 'Time Zone',
46
			isRTL: false
47
		};
48
		this._defaults = { // Global defaults for all the datetime picker instances
49
			showButtonPanel: true,
50
			timeOnly: false,
51
			timeOnlyShowDate: false,
52
			showHour: null,
53
			showMinute: null,
54
			showSecond: null,
55
			showMillisec: null,
56
			showMicrosec: null,
57
			showTimezone: null,
58
			showTime: true,
59
			stepHour: 1,
60
			stepMinute: 1,
61
			stepSecond: 1,
62
			stepMillisec: 1,
63
			stepMicrosec: 1,
64
			hour: 0,
65
			minute: 0,
66
			second: 0,
67
			millisec: 0,
68
			microsec: 0,
69
			timezone: null,
70
			hourMin: 0,
71
			minuteMin: 0,
72
			secondMin: 0,
73
			millisecMin: 0,
74
			microsecMin: 0,
75
			hourMax: 23,
76
			minuteMax: 59,
77
			secondMax: 59,
78
			millisecMax: 999,
79
			microsecMax: 999,
80
			minDateTime: null,
81
			maxDateTime: null,
82
			maxTime: null,
83
			minTime: null,
84
			onSelect: null,
85
			hourGrid: 0,
86
			minuteGrid: 0,
87
			secondGrid: 0,
88
			millisecGrid: 0,
89
			microsecGrid: 0,
90
			alwaysSetTime: true,
91
			separator: ' ',
92
			altFieldTimeOnly: true,
93
			altTimeFormat: null,
94
			altSeparator: null,
95
			altTimeSuffix: null,
96
			altRedirectFocus: true,
97
			pickerTimeFormat: null,
98
			pickerTimeSuffix: null,
99
			showTimepicker: true,
100
			timezoneList: null,
101
			addSliderAccess: false,
102
			sliderAccessArgs: null,
103
			controlType: 'slider',
104
			defaultValue: null,
105
			parse: 'strict'
106
		};
107
		$.extend(this._defaults, this.regional['']);
108
	};
109
110
	$.extend(Timepicker.prototype, {
111
		$input: null,
112
		$altInput: null,
113
		$timeObj: null,
114
		inst: null,
115
		hour_slider: null,
116
		minute_slider: null,
117
		second_slider: null,
118
		millisec_slider: null,
119
		microsec_slider: null,
120
		timezone_select: null,
121
		maxTime: null,
122
		minTime: null,
123
		hour: 0,
124
		minute: 0,
125
		second: 0,
126
		millisec: 0,
127
		microsec: 0,
128
		timezone: null,
129
		hourMinOriginal: null,
130
		minuteMinOriginal: null,
131
		secondMinOriginal: null,
132
		millisecMinOriginal: null,
133
		microsecMinOriginal: null,
134
		hourMaxOriginal: null,
135
		minuteMaxOriginal: null,
136
		secondMaxOriginal: null,
137
		millisecMaxOriginal: null,
138
		microsecMaxOriginal: null,
139
		ampm: '',
140
		formattedDate: '',
141
		formattedTime: '',
142
		formattedDateTime: '',
143
		timezoneList: null,
144
		units: ['hour', 'minute', 'second', 'millisec', 'microsec'],
145
		support: {},
146
		control: null,
147
148
		/* 
149
		* Override the default settings for all instances of the time picker.
150
		* @param  {Object} settings  object - the new settings to use as defaults (anonymous object)
151
		* @return {Object} the manager object
152
		*/
153
		setDefaults: function (settings) {
154
			extendRemove(this._defaults, settings || {});
155
			return this;
156
		},
157
158
		/*
159
		* Create a new Timepicker instance
160
		*/
161
		_newInst: function ($input, opts) {
162
			var tp_inst = new Timepicker(),
163
				inlineSettings = {},
164
				fns = {},
165
				overrides, i;
166
167
			for (var attrName in this._defaults) {
168
				if (this._defaults.hasOwnProperty(attrName)) {
169
					var attrValue = $input.attr('time:' + attrName);
170
					if (attrValue) {
171
						try {
172
							inlineSettings[attrName] = eval(attrValue);
0 ignored issues
show
Coding Style Security introduced by
The use of eval is generally not recommended.

The use of eval is discouraged because it can potentially make your code vulnerable to various injection attacks.

Besides it will also prevent certain optimizations that runtimes otherwise could make.

Loading history...
173
						} catch (err) {
174
							inlineSettings[attrName] = attrValue;
175
						}
176
					}
177
				}
178
			}
179
180
			overrides = {
181
				beforeShow: function (input, dp_inst) {
182
					if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) {
183
						return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst);
184
					}
185
				},
186
				onChangeMonthYear: function (year, month, dp_inst) {
187
					// Update the time as well : this prevents the time from disappearing from the $input field.
188
					tp_inst._updateDateTime(dp_inst);
189
					if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) {
190
						tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);
191
					}
192
				},
193
				onClose: function (dateText, dp_inst) {
194
					if (tp_inst.timeDefined === true && $input.val() !== '') {
195
						tp_inst._updateDateTime(dp_inst);
196
					}
197
					if ($.isFunction(tp_inst._defaults.evnts.onClose)) {
198
						tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst);
199
					}
200
				}
201
			};
202
			for (i in overrides) {
203
				if (overrides.hasOwnProperty(i)) {
204
					fns[i] = opts[i] || null;
205
				}
206
			}
207
208
			tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, {
209
				evnts: fns,
210
				timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');
211
			});
212
			tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) {
213
				return val.toUpperCase();
214
			});
215
			tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) {
216
				return val.toUpperCase();
217
			});
218
219
			// detect which units are supported
220
			tp_inst.support = detectSupport(
221
					tp_inst._defaults.timeFormat + 
222
					(tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') +
223
					(tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : ''));
224
225
			// controlType is string - key to our this._controls
226
			if (typeof(tp_inst._defaults.controlType) === 'string') {
227
				if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') {
228
					tp_inst._defaults.controlType = 'select';
229
				}
230
				tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType];
231
			}
232
			// controlType is an object and must implement create, options, value methods
233
			else {
234
				tp_inst.control = tp_inst._defaults.controlType;
235
			}
236
237
			// prep the timezone options
238
			var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60,
239
					0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840];
240
			if (tp_inst._defaults.timezoneList !== null) {
241
				timezoneList = tp_inst._defaults.timezoneList;
242
			}
243
			var tzl = timezoneList.length, tzi = 0, tzv = null;
244
			if (tzl > 0 && typeof timezoneList[0] !== 'object') {
245
				for (; tzi < tzl; tzi++) {
246
					tzv = timezoneList[tzi];
247
					timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) };
248
				}
249
			}
250
			tp_inst._defaults.timezoneList = timezoneList;
251
252
			// set the default units
253
			tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) :
254
							((new Date()).getTimezoneOffset() * -1);
255
			tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin :
256
							tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour;
257
			tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin :
258
							tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute;
259
			tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin :
260
							tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second;
261
			tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin :
262
							tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec;
263
			tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin :
264
							tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec;
265
			tp_inst.ampm = '';
266
			tp_inst.$input = $input;
267
268
			if (tp_inst._defaults.altField) {
269
				tp_inst.$altInput = $(tp_inst._defaults.altField);
270
				if (tp_inst._defaults.altRedirectFocus === true) {
271
					tp_inst.$altInput.css({
272
						cursor: 'pointer'
273
					}).focus(function () {
274
						$input.trigger("focus");
275
					});
276
				}
277
			}
278
279
			if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) {
280
				tp_inst._defaults.minDate = new Date();
281
			}
282
			if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) {
283
				tp_inst._defaults.maxDate = new Date();
284
			}
285
286
			// datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..
287
			if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) {
288
				tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());
289
			}
290
			if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) {
291
				tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());
292
			}
293
			if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) {
294
				tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());
295
			}
296
			if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) {
297
				tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());
298
			}
299
			tp_inst.$input.bind('focus', function () {
300
				tp_inst._onFocus();
301
			});
302
303
			return tp_inst;
304
		},
305
306
		/*
307
		* add our sliders to the calendar
308
		*/
309
		_addTimePicker: function (dp_inst) {
310
			var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val();
311
312
			this.timeDefined = this._parseTime(currDT);
313
			this._limitMinMaxDateTime(dp_inst, false);
314
			this._injectTimePicker();
315
		},
316
317
		/*
318
		* parse the time string from input value or _setTime
319
		*/
320
		_parseTime: function (timeString, withDate) {
321
			if (!this.inst) {
322
				this.inst = $.datepicker._getInst(this.$input[0]);
323
			}
324
325
			if (withDate || !this._defaults.timeOnly) {
326
				var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');
327
				try {
328
					var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults);
329
					if (!parseRes.timeObj) {
330
						return false;
331
					}
332
					$.extend(this, parseRes.timeObj);
333
				} catch (err) {
334
					$.timepicker.log("Error parsing the date/time string: " + err +
335
									"\ndate/time string = " + timeString +
336
									"\ntimeFormat = " + this._defaults.timeFormat +
337
									"\ndateFormat = " + dp_dateFormat);
338
					return false;
339
				}
340
				return true;
341
			} else {
342
				var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults);
343
				if (!timeObj) {
344
					return false;
345
				}
346
				$.extend(this, timeObj);
347
				return true;
348
			}
349
		},
350
351
		/*
352
		* generate and inject html for timepicker into ui datepicker
353
		*/
354
		_injectTimePicker: function () {
355
			var $dp = this.inst.dpDiv,
356
				o = this.inst.settings,
357
				tp_inst = this,
358
				litem = '',
359
				uitem = '',
360
				show = null,
361
				max = {},
362
				gridSize = {},
363
				size = null,
364
				i = 0,
365
				l = 0;
366
367
			// Prevent displaying twice
368
			if ($dp.find("div.ui-timepicker-div").length === 0 && o.showTimepicker) {
369
				var noDisplay = ' style="display:none;"',
370
					html = '<div class="ui-timepicker-div' + (o.isRTL ? ' ui-timepicker-rtl' : '') + '"><dl>' + '<dt class="ui_tpicker_time_label"' + ((o.showTime) ? '' : noDisplay) + '>' + o.timeText + '</dt>' +
371
								'<dd class="ui_tpicker_time"' + ((o.showTime) ? '' : noDisplay) + '></dd>';
372
373
				// Create the markup
374
				for (i = 0, l = this.units.length; i < l; i++) {
375
					litem = this.units[i];
376
					uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);
377
					show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];
378
379
					// Added by Peter Medeiros:
380
					// - Figure out what the hour/minute/second max should be based on the step values.
381
					// - Example: if stepMinute is 15, then minMax is 45.
382
					max[litem] = parseInt((o[litem + 'Max'] - ((o[litem + 'Max'] - o[litem + 'Min']) % o['step' + uitem])), 10);
383
					gridSize[litem] = 0;
384
385
					html += '<dt class="ui_tpicker_' + litem + '_label"' + (show ? '' : noDisplay) + '>' + o[litem + 'Text'] + '</dt>' +
386
								'<dd class="ui_tpicker_' + litem + '"><div class="ui_tpicker_' + litem + '_slider"' + (show ? '' : noDisplay) + '></div>';
387
388
					if (show && o[litem + 'Grid'] > 0) {
389
						html += '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>';
390
391
						if (litem === 'hour') {
392
							for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) {
393
								gridSize[litem]++;
394
								var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o);
395
								html += '<td data-for="' + litem + '">' + tmph + '</td>';
396
							}
397
						}
398
						else {
399
							for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) {
400
								gridSize[litem]++;
401
								html += '<td data-for="' + litem + '">' + ((m < 10) ? '0' : '') + m + '</td>';
402
							}
403
						}
404
405
						html += '</tr></table></div>';
406
					}
407
					html += '</dd>';
408
				}
409
				
410
				// Timezone
411
				var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone;
412
				html += '<dt class="ui_tpicker_timezone_label"' + (showTz ? '' : noDisplay) + '>' + o.timezoneText + '</dt>';
413
				html += '<dd class="ui_tpicker_timezone" ' + (showTz ? '' : noDisplay) + '></dd>';
414
415
				// Create the elements from string
416
				html += '</dl></div>';
417
				var $tp = $(html);
418
419
				// if we only want time picker...
420
				if (o.timeOnly === true) {
421
					$tp.prepend('<div class="ui-widget-header ui-helper-clearfix ui-corner-all">' + '<div class="ui-datepicker-title">' + o.timeOnlyTitle + '</div>' + '</div>');
422
					$dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();
423
				}
424
				
425
				// add sliders, adjust grids, add events
426
				for (i = 0, l = tp_inst.units.length; i < l; i++) {
427
					litem = tp_inst.units[i];
428
					uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1);
429
					show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem];
430
431
					// add the slider
432
					tp_inst[litem + '_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_' + litem + '_slider'), litem, tp_inst[litem], o[litem + 'Min'], max[litem], o['step' + uitem]);
433
434
					// adjust the grid and add click event
435
					if (show && o[litem + 'Grid'] > 0) {
436
						size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']);
437
						$tp.find('.ui_tpicker_' + litem + ' table').css({
438
							width: size + "%",
439
							marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + "%"),
440
							marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + "%") : '0',
441
							borderCollapse: 'collapse'
442
						}).find("td").click(function (e) {
0 ignored issues
show
It is generally not recommended to make functions within a loop.

While making functions in a loop will not lead to any runtime error, the code might not behave as you expect as the variables in the scope are not imported by value, but by reference. Let’s take a look at an example:

var funcs = [];
for (var i=0; i<10; i++) {
    funcs.push(function() {
        alert(i);
    });
}

funcs[0](); // alert(10);
funcs[1](); // alert(10);
/// ...
funcs[9](); // alert(10);

If you would instead like to bind the function inside the loop to the value of the variable during that specific iteration, you can create the function from another function:

var createFunc = function(i) {
    return function() {
        alert(i);
    };
};

var funcs = [];
for (var i=0; i<10; i++) {
    funcs.push(createFunc(i));
}

funcs[0](); // alert(0)
funcs[1](); // alert(1)
// ...
funcs[9](); // alert(9)
Loading history...
443
								var $t = $(this),
444
									h = $t.html(),
445
									n = parseInt(h.replace(/[^0-9]/g), 10),
446
									ap = h.replace(/[^apm]/ig),
447
									f = $t.data('for'); // loses scope, so we use data-for
448
449
								if (f === 'hour') {
450
									if (ap.indexOf('p') !== -1 && n < 12) {
451
										n += 12;
452
									}
453
									else {
454
										if (ap.indexOf('a') !== -1 && n === 12) {
455
											n = 0;
456
										}
457
									}
458
								}
459
								
460
								tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n);
461
462
								tp_inst._onTimeChange();
463
								tp_inst._onSelectHandler();
464
							}).css({
465
								cursor: 'pointer',
466
								width: (100 / gridSize[litem]) + '%',
467
								textAlign: 'center',
468
								overflow: 'hidden'
469
							});
470
					} // end if grid > 0
471
				} // end for loop
472
473
				// Add timezone options
474
				this.timezone_select = $tp.find('.ui_tpicker_timezone').append('<select></select>').find("select");
475
				$.fn.append.apply(this.timezone_select,
476
				$.map(o.timezoneList, function (val, idx) {
477
					return $("<option />").val(typeof val === "object" ? val.value : val).text(typeof val === "object" ? val.label : val);
478
				}));
479
				if (typeof(this.timezone) !== "undefined" && this.timezone !== null && this.timezone !== "") {
480
					var local_timezone = (new Date(this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12)).getTimezoneOffset() * -1;
481
					if (local_timezone === this.timezone) {
482
						selectLocalTimezone(tp_inst);
483
					} else {
484
						this.timezone_select.val(this.timezone);
485
					}
486
				} else {
487
					if (typeof(this.hour) !== "undefined" && this.hour !== null && this.hour !== "") {
488
						this.timezone_select.val(o.timezone);
489
					} else {
490
						selectLocalTimezone(tp_inst);
491
					}
492
				}
493
				this.timezone_select.change(function () {
494
					tp_inst._onTimeChange();
495
					tp_inst._onSelectHandler();
496
				});
497
				// End timezone options
498
				
499
				// inject timepicker into datepicker
500
				var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');
501
				if ($buttonPanel.length) {
502
					$buttonPanel.before($tp);
503
				} else {
504
					$dp.append($tp);
505
				}
506
507
				this.$timeObj = $tp.find('.ui_tpicker_time');
508
509
				if (this.inst !== null) {
510
					var timeDefined = this.timeDefined;
511
					this._onTimeChange();
512
					this.timeDefined = timeDefined;
513
				}
514
515
				// slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/
516
				if (this._defaults.addSliderAccess) {
517
					var sliderAccessArgs = this._defaults.sliderAccessArgs,
518
						rtl = this._defaults.isRTL;
519
					sliderAccessArgs.isRTL = rtl;
520
						
521
					setTimeout(function () { // fix for inline mode
522
						if ($tp.find('.ui-slider-access').length === 0) {
523
							$tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs);
524
525
							// fix any grids since sliders are shorter
526
							var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true);
527
							if (sliderAccessWidth) {
528
								$tp.find('table:visible').each(function () {
529
									var $g = $(this),
530
										oldWidth = $g.outerWidth(),
531
										oldMarginLeft = $g.css(rtl ? 'marginRight' : 'marginLeft').toString().replace('%', ''),
532
										newWidth = oldWidth - sliderAccessWidth,
533
										newMarginLeft = ((oldMarginLeft * newWidth) / oldWidth) + '%',
534
										css = { width: newWidth, marginRight: 0, marginLeft: 0 };
535
									css[rtl ? 'marginRight' : 'marginLeft'] = newMarginLeft;
536
									$g.css(css);
537
								});
538
							}
539
						}
540
					}, 10);
541
				}
542
				// end slideAccess integration
543
544
				tp_inst._limitMinMaxDateTime(this.inst, true);
545
			}
546
		},
547
548
		/*
549
		* This function tries to limit the ability to go outside the
550
		* min/max date range
551
		*/
552
		_limitMinMaxDateTime: function (dp_inst, adjustSliders) {
553
			var o = this._defaults,
554
				dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);
555
556
			if (!this._defaults.showTimepicker) {
557
				return;
558
			} // No time so nothing to check here
559
560
			if ($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date) {
561
				var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'),
562
					minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);
563
564
				if (this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null || this.microsecMinOriginal === null) {
565
					this.hourMinOriginal = o.hourMin;
566
					this.minuteMinOriginal = o.minuteMin;
567
					this.secondMinOriginal = o.secondMin;
568
					this.millisecMinOriginal = o.millisecMin;
569
					this.microsecMinOriginal = o.microsecMin;
570
				}
571
572
				if (dp_inst.settings.timeOnly || minDateTimeDate.getTime() === dp_date.getTime()) {
573
					this._defaults.hourMin = minDateTime.getHours();
574
					if (this.hour <= this._defaults.hourMin) {
575
						this.hour = this._defaults.hourMin;
576
						this._defaults.minuteMin = minDateTime.getMinutes();
577
						if (this.minute <= this._defaults.minuteMin) {
578
							this.minute = this._defaults.minuteMin;
579
							this._defaults.secondMin = minDateTime.getSeconds();
580
							if (this.second <= this._defaults.secondMin) {
581
								this.second = this._defaults.secondMin;
582
								this._defaults.millisecMin = minDateTime.getMilliseconds();
583
								if (this.millisec <= this._defaults.millisecMin) {
584
									this.millisec = this._defaults.millisecMin;
585
									this._defaults.microsecMin = minDateTime.getMicroseconds();
586
								} else {
587
									if (this.microsec < this._defaults.microsecMin) {
588
										this.microsec = this._defaults.microsecMin;
589
									}
590
									this._defaults.microsecMin = this.microsecMinOriginal;
591
								}
592
							} else {
593
								this._defaults.millisecMin = this.millisecMinOriginal;
594
								this._defaults.microsecMin = this.microsecMinOriginal;
595
							}
596
						} else {
597
							this._defaults.secondMin = this.secondMinOriginal;
598
							this._defaults.millisecMin = this.millisecMinOriginal;
599
							this._defaults.microsecMin = this.microsecMinOriginal;
600
						}
601
					} else {
602
						this._defaults.minuteMin = this.minuteMinOriginal;
603
						this._defaults.secondMin = this.secondMinOriginal;
604
						this._defaults.millisecMin = this.millisecMinOriginal;
605
						this._defaults.microsecMin = this.microsecMinOriginal;
606
					}
607
				} else {
608
					this._defaults.hourMin = this.hourMinOriginal;
609
					this._defaults.minuteMin = this.minuteMinOriginal;
610
					this._defaults.secondMin = this.secondMinOriginal;
611
					this._defaults.millisecMin = this.millisecMinOriginal;
612
					this._defaults.microsecMin = this.microsecMinOriginal;
613
				}
614
			}
615
616
			if ($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date) {
617
				var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'),
618
					maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);
619
620
				if (this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null || this.millisecMaxOriginal === null) {
621
					this.hourMaxOriginal = o.hourMax;
622
					this.minuteMaxOriginal = o.minuteMax;
623
					this.secondMaxOriginal = o.secondMax;
624
					this.millisecMaxOriginal = o.millisecMax;
625
					this.microsecMaxOriginal = o.microsecMax;
626
				}
627
628
				if (dp_inst.settings.timeOnly || maxDateTimeDate.getTime() === dp_date.getTime()) {
629
					this._defaults.hourMax = maxDateTime.getHours();
630
					if (this.hour >= this._defaults.hourMax) {
631
						this.hour = this._defaults.hourMax;
632
						this._defaults.minuteMax = maxDateTime.getMinutes();
633
						if (this.minute >= this._defaults.minuteMax) {
634
							this.minute = this._defaults.minuteMax;
635
							this._defaults.secondMax = maxDateTime.getSeconds();
636
							if (this.second >= this._defaults.secondMax) {
637
								this.second = this._defaults.secondMax;
638
								this._defaults.millisecMax = maxDateTime.getMilliseconds();
639
								if (this.millisec >= this._defaults.millisecMax) {
640
									this.millisec = this._defaults.millisecMax;
641
									this._defaults.microsecMax = maxDateTime.getMicroseconds();
642
								} else {
643
									if (this.microsec > this._defaults.microsecMax) {
644
										this.microsec = this._defaults.microsecMax;
645
									}
646
									this._defaults.microsecMax = this.microsecMaxOriginal;
647
								}
648
							} else {
649
								this._defaults.millisecMax = this.millisecMaxOriginal;
650
								this._defaults.microsecMax = this.microsecMaxOriginal;
651
							}
652
						} else {
653
							this._defaults.secondMax = this.secondMaxOriginal;
654
							this._defaults.millisecMax = this.millisecMaxOriginal;
655
							this._defaults.microsecMax = this.microsecMaxOriginal;
656
						}
657
					} else {
658
						this._defaults.minuteMax = this.minuteMaxOriginal;
659
						this._defaults.secondMax = this.secondMaxOriginal;
660
						this._defaults.millisecMax = this.millisecMaxOriginal;
661
						this._defaults.microsecMax = this.microsecMaxOriginal;
662
					}
663
				} else {
664
					this._defaults.hourMax = this.hourMaxOriginal;
665
					this._defaults.minuteMax = this.minuteMaxOriginal;
666
					this._defaults.secondMax = this.secondMaxOriginal;
667
					this._defaults.millisecMax = this.millisecMaxOriginal;
668
					this._defaults.microsecMax = this.microsecMaxOriginal;
669
				}
670
			}
671
672
			if (dp_inst.settings.minTime!==null) {				
673
				var tempMinTime=new Date("01/01/1970 " + dp_inst.settings.minTime);				
674
				if (this.hour<tempMinTime.getHours()) {
675
					this.hour=this._defaults.hourMin=tempMinTime.getHours();
676
					this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();							
677
				} else if (this.hour===tempMinTime.getHours() && this.minute<tempMinTime.getMinutes()) {
678
					this.minute=this._defaults.minuteMin=tempMinTime.getMinutes();
679
				} else {						
680
					if (this._defaults.hourMin<tempMinTime.getHours()) {
681
						this._defaults.hourMin=tempMinTime.getHours();
682
						this._defaults.minuteMin=tempMinTime.getMinutes();					
683
					} else if (this._defaults.hourMin===tempMinTime.getHours()===this.hour && this._defaults.minuteMin<tempMinTime.getMinutes()) {
684
						this._defaults.minuteMin=tempMinTime.getMinutes();						
685
					} else {
686
						this._defaults.minuteMin=0;
687
					}
688
				}				
689
			}
690
			
691
			if (dp_inst.settings.maxTime!==null) {				
692
				var tempMaxTime=new Date("01/01/1970 " + dp_inst.settings.maxTime);
693
				if (this.hour>tempMaxTime.getHours()) {
694
					this.hour=this._defaults.hourMax=tempMaxTime.getHours();						
695
					this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();
696
				} else if (this.hour===tempMaxTime.getHours() && this.minute>tempMaxTime.getMinutes()) {							
697
					this.minute=this._defaults.minuteMax=tempMaxTime.getMinutes();						
698
				} else {
699
					if (this._defaults.hourMax>tempMaxTime.getHours()) {
700
						this._defaults.hourMax=tempMaxTime.getHours();
701
						this._defaults.minuteMax=tempMaxTime.getMinutes();					
702
					} else if (this._defaults.hourMax===tempMaxTime.getHours()===this.hour && this._defaults.minuteMax>tempMaxTime.getMinutes()) {
703
						this._defaults.minuteMax=tempMaxTime.getMinutes();						
704
					} else {
705
						this._defaults.minuteMax=59;
706
					}
707
				}						
708
			}
709
			
710
			if (adjustSliders !== undefined && adjustSliders === true) {
711
				var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)), 10),
712
					minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)), 10),
713
					secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)), 10),
714
					millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)), 10),
715
					microsecMax = parseInt((this._defaults.microsecMax - ((this._defaults.microsecMax - this._defaults.microsecMin) % this._defaults.stepMicrosec)), 10);
716
717
				if (this.hour_slider) {
718
					this.control.options(this, this.hour_slider, 'hour', { min: this._defaults.hourMin, max: hourMax, step: this._defaults.stepHour });
719
					this.control.value(this, this.hour_slider, 'hour', this.hour - (this.hour % this._defaults.stepHour));
720
				}
721
				if (this.minute_slider) {
722
					this.control.options(this, this.minute_slider, 'minute', { min: this._defaults.minuteMin, max: minMax, step: this._defaults.stepMinute });
723
					this.control.value(this, this.minute_slider, 'minute', this.minute - (this.minute % this._defaults.stepMinute));
724
				}
725
				if (this.second_slider) {
726
					this.control.options(this, this.second_slider, 'second', { min: this._defaults.secondMin, max: secMax, step: this._defaults.stepSecond });
727
					this.control.value(this, this.second_slider, 'second', this.second - (this.second % this._defaults.stepSecond));
728
				}
729
				if (this.millisec_slider) {
730
					this.control.options(this, this.millisec_slider, 'millisec', { min: this._defaults.millisecMin, max: millisecMax, step: this._defaults.stepMillisec });
731
					this.control.value(this, this.millisec_slider, 'millisec', this.millisec - (this.millisec % this._defaults.stepMillisec));
732
				}
733
				if (this.microsec_slider) {
734
					this.control.options(this, this.microsec_slider, 'microsec', { min: this._defaults.microsecMin, max: microsecMax, step: this._defaults.stepMicrosec });
735
					this.control.value(this, this.microsec_slider, 'microsec', this.microsec - (this.microsec % this._defaults.stepMicrosec));
736
				}
737
			}
738
739
		},
740
741
		/*
742
		* when a slider moves, set the internal time...
743
		* on time change is also called when the time is updated in the text field
744
		*/
745
		_onTimeChange: function () {
746
			if (!this._defaults.showTimepicker) {
747
                                return;
748
			}
749
			var hour = (this.hour_slider) ? this.control.value(this, this.hour_slider, 'hour') : false,
750
				minute = (this.minute_slider) ? this.control.value(this, this.minute_slider, 'minute') : false,
751
				second = (this.second_slider) ? this.control.value(this, this.second_slider, 'second') : false,
752
				millisec = (this.millisec_slider) ? this.control.value(this, this.millisec_slider, 'millisec') : false,
753
				microsec = (this.microsec_slider) ? this.control.value(this, this.microsec_slider, 'microsec') : false,
754
				timezone = (this.timezone_select) ? this.timezone_select.val() : false,
755
				o = this._defaults,
756
				pickerTimeFormat = o.pickerTimeFormat || o.timeFormat,
757
				pickerTimeSuffix = o.pickerTimeSuffix || o.timeSuffix;
758
759
			if (typeof(hour) === 'object') {
760
				hour = false;
761
			}
762
			if (typeof(minute) === 'object') {
763
				minute = false;
764
			}
765
			if (typeof(second) === 'object') {
766
				second = false;
767
			}
768
			if (typeof(millisec) === 'object') {
769
				millisec = false;
770
			}
771
			if (typeof(microsec) === 'object') {
772
				microsec = false;
773
			}
774
			if (typeof(timezone) === 'object') {
775
				timezone = false;
776
			}
777
778
			if (hour !== false) {
779
				hour = parseInt(hour, 10);
780
			}
781
			if (minute !== false) {
782
				minute = parseInt(minute, 10);
783
			}
784
			if (second !== false) {
785
				second = parseInt(second, 10);
786
			}
787
			if (millisec !== false) {
788
				millisec = parseInt(millisec, 10);
789
			}
790
			if (microsec !== false) {
791
				microsec = parseInt(microsec, 10);
792
			}
793
			if (timezone !== false) {
794
				timezone = timezone.toString();
795
			}
796
797
			var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0];
798
799
			// If the update was done in the input field, the input field should not be updated.
800
			// If the update was done using the sliders, update the input field.
801
			var hasChanged = (
802
						hour !== parseInt(this.hour,10) || // sliders should all be numeric
803
						minute !== parseInt(this.minute,10) || 
804
						second !== parseInt(this.second,10) || 
805
						millisec !== parseInt(this.millisec,10) || 
806
						microsec !== parseInt(this.microsec,10) || 
807
						(this.ampm.length > 0 && (hour < 12) !== ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1)) || 
808
						(this.timezone !== null && timezone !== this.timezone.toString()) // could be numeric or "EST" format, so use toString()
809
					);
810
811
			if (hasChanged) {
812
813
				if (hour !== false) {
814
					this.hour = hour;
815
				}
816
				if (minute !== false) {
817
					this.minute = minute;
818
				}
819
				if (second !== false) {
820
					this.second = second;
821
				}
822
				if (millisec !== false) {
823
					this.millisec = millisec;
824
				}
825
				if (microsec !== false) {
826
					this.microsec = microsec;
827
				}
828
				if (timezone !== false) {
829
					this.timezone = timezone;
830
				}
831
832
				if (!this.inst) {
833
					this.inst = $.datepicker._getInst(this.$input[0]);
834
				}
835
836
				this._limitMinMaxDateTime(this.inst, true);
837
			}
838
			if (this.support.ampm) {
839
				this.ampm = ampm;
840
			}
841
842
			// Updates the time within the timepicker
843
			this.formattedTime = $.datepicker.formatTime(o.timeFormat, this, o);
844
			if (this.$timeObj) {
845
				if (pickerTimeFormat === o.timeFormat) {
846
					this.$timeObj.text(this.formattedTime + pickerTimeSuffix);
847
				}
848
				else {
849
					this.$timeObj.text($.datepicker.formatTime(pickerTimeFormat, this, o) + pickerTimeSuffix);
850
				}
851
			}
852
853
			this.timeDefined = true;
854
			if (hasChanged) {
855
				this._updateDateTime();
856
				//this.$input.focus(); // may automatically open the picker on setDate
857
			}
858
		},
859
860
		/*
861
		* call custom onSelect.
862
		* bind to sliders slidestop, and grid click.
863
		*/
864
		_onSelectHandler: function () {
865
			var onSelect = this._defaults.onSelect || this.inst.settings.onSelect;
866
			var inputEl = this.$input ? this.$input[0] : null;
867
			if (onSelect && inputEl) {
868
				onSelect.apply(inputEl, [this.formattedDateTime, this]);
869
			}
870
		},
871
872
		/*
873
		* update our input with the new date time..
874
		*/
875
		_updateDateTime: function (dp_inst) {
876
			dp_inst = this.inst || dp_inst;
877
			var dtTmp = (dp_inst.currentYear > 0? 
878
							new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay) : 
879
							new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
880
				dt = $.datepicker._daylightSavingAdjust(dtTmp),
881
				//dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),
882
				//dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.currentYear, dp_inst.currentMonth, dp_inst.currentDay)),
883
				dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),
884
				formatCfg = $.datepicker._getFormatConfig(dp_inst),
885
				timeAvailable = dt !== null && this.timeDefined;
886
			this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);
887
			var formattedDateTime = this.formattedDate;
888
			
889
			// if a slider was changed but datepicker doesn't have a value yet, set it
890
			if (dp_inst.lastVal === "") {
891
                dp_inst.currentYear = dp_inst.selectedYear;
892
                dp_inst.currentMonth = dp_inst.selectedMonth;
893
                dp_inst.currentDay = dp_inst.selectedDay;
894
            }
895
896
			/*
897
			* remove following lines to force every changes in date picker to change the input value
898
			* Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker. 
899
			* If the user manually empty the value in the input field, the date picker will never change selected value.
900
			*/
901
			//if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) {
902
			//	return;
903
			//}
904
905
			if (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === false) {
906
				formattedDateTime = this.formattedTime;
907
			} else if ((this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) || (this._defaults.timeOnly === true && this._defaults.timeOnlyShowDate === true)) {
908
				formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;
909
			}
910
911
			this.formattedDateTime = formattedDateTime;
912
913
			if (!this._defaults.showTimepicker) {
914
				this.$input.val(this.formattedDate);
915
			} else if (this.$altInput && this._defaults.timeOnly === false && this._defaults.altFieldTimeOnly === true) {
916
				this.$altInput.val(this.formattedTime);
917
				this.$input.val(this.formattedDate);
918
			} else if (this.$altInput) {
919
				this.$input.val(formattedDateTime);
920
				var altFormattedDateTime = '',
921
					altSeparator = this._defaults.altSeparator !== null ? this._defaults.altSeparator : this._defaults.separator,
922
					altTimeSuffix = this._defaults.altTimeSuffix !== null ? this._defaults.altTimeSuffix : this._defaults.timeSuffix;
923
				
924
				if (!this._defaults.timeOnly) {
925
					if (this._defaults.altFormat) {
926
						altFormattedDateTime = $.datepicker.formatDate(this._defaults.altFormat, (dt === null ? new Date() : dt), formatCfg);
927
					}
928
					else {
929
						altFormattedDateTime = this.formattedDate;
930
					}
931
932
					if (altFormattedDateTime) {
933
						altFormattedDateTime += altSeparator;
934
					}
935
				}
936
937
				if (this._defaults.altTimeFormat !== null) {
938
					altFormattedDateTime += $.datepicker.formatTime(this._defaults.altTimeFormat, this, this._defaults) + altTimeSuffix;
939
				}
940
				else {
941
					altFormattedDateTime += this.formattedTime + altTimeSuffix;
942
				}
943
				this.$altInput.val(altFormattedDateTime);
944
			} else {
945
				this.$input.val(formattedDateTime);
946
			}
947
948
			this.$input.trigger("change");
949
		},
950
951
		_onFocus: function () {
952
			if (!this.$input.val() && this._defaults.defaultValue) {
953
				this.$input.val(this._defaults.defaultValue);
954
				var inst = $.datepicker._getInst(this.$input.get(0)),
955
					tp_inst = $.datepicker._get(inst, 'timepicker');
956
				if (tp_inst) {
957
					if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {
958
						try {
959
							$.datepicker._updateDatepicker(inst);
960
						} catch (err) {
961
							$.timepicker.log(err);
962
						}
963
					}
964
				}
965
			}
966
		},
967
968
		/*
969
		* Small abstraction to control types
970
		* We can add more, just be sure to follow the pattern: create, options, value
971
		*/
972
		_controls: {
973
			// slider methods
974
			slider: {
975
				create: function (tp_inst, obj, unit, val, min, max, step) {
976
					var rtl = tp_inst._defaults.isRTL; // if rtl go -60->0 instead of 0->60
977
					return obj.prop('slide', null).slider({
978
						orientation: "horizontal",
979
						value: rtl ? val * -1 : val,
980
						min: rtl ? max * -1 : min,
981
						max: rtl ? min * -1 : max,
982
						step: step,
983
						slide: function (event, ui) {
984
							tp_inst.control.value(tp_inst, $(this), unit, rtl ? ui.value * -1 : ui.value);
985
							tp_inst._onTimeChange();
986
						},
987
						stop: function (event, ui) {
988
							tp_inst._onSelectHandler();
989
						}
990
					});	
991
				},
992
				options: function (tp_inst, obj, unit, opts, val) {
993
					if (tp_inst._defaults.isRTL) {
994
						if (typeof(opts) === 'string') {
995
							if (opts === 'min' || opts === 'max') {
996
								if (val !== undefined) {
997
									return obj.slider(opts, val * -1);
998
								}
999
								return Math.abs(obj.slider(opts));
1000
							}
1001
							return obj.slider(opts);
1002
						}
1003
						var min = opts.min, 
1004
							max = opts.max;
1005
						opts.min = opts.max = null;
1006
						if (min !== undefined) {
1007
							opts.max = min * -1;
1008
						}
1009
						if (max !== undefined) {
1010
							opts.min = max * -1;
1011
						}
1012
						return obj.slider(opts);
1013
					}
1014
					if (typeof(opts) === 'string' && val !== undefined) {
1015
						return obj.slider(opts, val);
1016
					}
1017
					return obj.slider(opts);
1018
				},
1019
				value: function (tp_inst, obj, unit, val) {
1020
					if (tp_inst._defaults.isRTL) {
1021
						if (val !== undefined) {
1022
							return obj.slider('value', val * -1);
1023
						}
1024
						return Math.abs(obj.slider('value'));
1025
					}
1026
					if (val !== undefined) {
1027
						return obj.slider('value', val);
1028
					}
1029
					return obj.slider('value');
1030
				}
1031
			},
1032
			// select methods
1033
			select: {
1034
				create: function (tp_inst, obj, unit, val, min, max, step) {
1035
					var sel = '<select class="ui-timepicker-select" data-unit="' + unit + '" data-min="' + min + '" data-max="' + max + '" data-step="' + step + '">',
1036
						format = tp_inst._defaults.pickerTimeFormat || tp_inst._defaults.timeFormat;
1037
1038
					for (var i = min; i <= max; i += step) {
1039
						sel += '<option value="' + i + '"' + (i === val ? ' selected' : '') + '>';
1040
						if (unit === 'hour') {
1041
							sel += $.datepicker.formatTime($.trim(format.replace(/[^ht ]/ig, '')), {hour: i}, tp_inst._defaults);
1042
						}
1043
						else if (unit === 'millisec' || unit === 'microsec' || i >= 10) { sel += i; }
1044
						else {sel += '0' + i.toString(); }
1045
						sel += '</option>';
1046
					}
1047
					sel += '</select>';
1048
1049
					obj.children('select').remove();
1050
1051
					$(sel).appendTo(obj).change(function (e) {
1052
						tp_inst._onTimeChange();
1053
						tp_inst._onSelectHandler();
1054
					});
1055
1056
					return obj;
1057
				},
1058
				options: function (tp_inst, obj, unit, opts, val) {
1059
					var o = {},
1060
						$t = obj.children('select');
1061
					if (typeof(opts) === 'string') {
1062
						if (val === undefined) {
1063
							return $t.data(opts);
1064
						}
1065
						o[opts] = val;	
1066
					}
1067
					else { o = opts; }
1068
					return tp_inst.control.create(tp_inst, obj, $t.data('unit'), $t.val(), o.min || $t.data('min'), o.max || $t.data('max'), o.step || $t.data('step'));
1069
				},
1070
				value: function (tp_inst, obj, unit, val) {
1071
					var $t = obj.children('select');
1072
					if (val !== undefined) {
1073
						return $t.val(val);
1074
					}
1075
					return $t.val();
1076
				}
1077
			}
1078
		} // end _controls
1079
1080
	});
1081
1082
	$.fn.extend({
1083
		/*
1084
		* shorthand just to use timepicker.
1085
		*/
1086
		timepicker: function (o) {
1087
			o = o || {};
1088
			var tmp_args = Array.prototype.slice.call(arguments);
1089
1090
			if (typeof o === 'object') {
1091
				tmp_args[0] = $.extend(o, {
1092
					timeOnly: true
1093
				});
1094
			}
1095
1096
			return $(this).each(function () {
1097
				$.fn.datetimepicker.apply($(this), tmp_args);
1098
			});
1099
		},
1100
1101
		/*
1102
		* extend timepicker to datepicker
1103
		*/
1104
		datetimepicker: function (o) {
1105
			o = o || {};
1106
			var tmp_args = arguments;
1107
1108
			if (typeof(o) === 'string') {
1109
				if (o === 'getDate'  || (o === 'option' && tmp_args.length === 2 && typeof (tmp_args[1]) === 'string')) {
1110
					return $.fn.datepicker.apply($(this[0]), tmp_args);
1111
				} else {
1112
					return this.each(function () {
1113
						var $t = $(this);
1114
						$t.datepicker.apply($t, tmp_args);
1115
					});
1116
				}
1117
			} else {
1118
				return this.each(function () {
1119
					var $t = $(this);
1120
					$t.datepicker($.timepicker._newInst($t, o)._defaults);
1121
				});
1122
			}
1123
		}
1124
	});
1125
1126
	/*
1127
	* Public Utility to parse date and time
1128
	*/
1129
	$.datepicker.parseDateTime = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {
1130
		var parseRes = parseDateTimeInternal(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings);
1131
		if (parseRes.timeObj) {
1132
			var t = parseRes.timeObj;
1133
			parseRes.date.setHours(t.hour, t.minute, t.second, t.millisec);
1134
			parseRes.date.setMicroseconds(t.microsec);
1135
		}
1136
1137
		return parseRes.date;
1138
	};
1139
1140
	/*
1141
	* Public utility to parse time
1142
	*/
1143
	$.datepicker.parseTime = function (timeFormat, timeString, options) {
1144
		var o = extendRemove(extendRemove({}, $.timepicker._defaults), options || {}),
1145
			iso8601 = (timeFormat.replace(/\'.*?\'/g, '').indexOf('Z') !== -1);
1146
1147
		// Strict parse requires the timeString to match the timeFormat exactly
1148
		var strictParse = function (f, s, o) {
1149
1150
			// pattern for standard and localized AM/PM markers
1151
			var getPatternAmpm = function (amNames, pmNames) {
1152
				var markers = [];
1153
				if (amNames) {
1154
					$.merge(markers, amNames);
1155
				}
1156
				if (pmNames) {
1157
					$.merge(markers, pmNames);
1158
				}
1159
				markers = $.map(markers, function (val) {
1160
					return val.replace(/[.*+?|()\[\]{}\\]/g, '\\$&');
1161
				});
1162
				return '(' + markers.join('|') + ')?';
1163
			};
1164
1165
			// figure out position of time elements.. cause js cant do named captures
1166
			var getFormatPositions = function (timeFormat) {
1167
				var finds = timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|c{1}|t{1,2}|z|'.*?')/g),
1168
					orders = {
1169
						h: -1,
1170
						m: -1,
1171
						s: -1,
1172
						l: -1,
1173
						c: -1,
1174
						t: -1,
1175
						z: -1
1176
					};
1177
1178
				if (finds) {
1179
					for (var i = 0; i < finds.length; i++) {
1180
						if (orders[finds[i].toString().charAt(0)] === -1) {
1181
							orders[finds[i].toString().charAt(0)] = i + 1;
1182
						}
1183
					}
1184
				}
1185
				return orders;
1186
			};
1187
1188
			var regstr = '^' + f.toString()
1189
					.replace(/([hH]{1,2}|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {
1190
							var ml = match.length;
1191
							switch (match.charAt(0).toLowerCase()) {
1192
							case 'h':
1193
								return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
1194
							case 'm':
1195
								return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
1196
							case 's':
1197
								return ml === 1 ? '(\\d?\\d)' : '(\\d{' + ml + '})';
1198
							case 'l':
1199
								return '(\\d?\\d?\\d)';
1200
							case 'c':
1201
								return '(\\d?\\d?\\d)';
1202
							case 'z':
1203
								return '(z|[-+]\\d\\d:?\\d\\d|\\S+)?';
1204
							case 't':
1205
								return getPatternAmpm(o.amNames, o.pmNames);
1206
							default:    // literal escaped in quotes
1207
								return '(' + match.replace(/\'/g, "").replace(/(\.|\$|\^|\\|\/|\(|\)|\[|\]|\?|\+|\*)/g, function (m) { return "\\" + m; }) + ')?';
1208
							}
1209
						})
1210
					.replace(/\s/g, '\\s?') +
1211
					o.timeSuffix + '$',
1212
				order = getFormatPositions(f),
1213
				ampm = '',
1214
				treg;
1215
1216
			treg = s.match(new RegExp(regstr, 'i'));
1217
1218
			var resTime = {
1219
				hour: 0,
1220
				minute: 0,
1221
				second: 0,
1222
				millisec: 0,
1223
				microsec: 0
1224
			};
1225
1226
			if (treg) {
1227
				if (order.t !== -1) {
1228
					if (treg[order.t] === undefined || treg[order.t].length === 0) {
1229
						ampm = '';
1230
						resTime.ampm = '';
1231
					} else {
1232
						ampm = $.inArray(treg[order.t].toUpperCase(), o.amNames) !== -1 ? 'AM' : 'PM';
1233
						resTime.ampm = o[ampm === 'AM' ? 'amNames' : 'pmNames'][0];
1234
					}
1235
				}
1236
1237
				if (order.h !== -1) {
1238
					if (ampm === 'AM' && treg[order.h] === '12') {
1239
						resTime.hour = 0; // 12am = 0 hour
1240
					} else {
1241
						if (ampm === 'PM' && treg[order.h] !== '12') {
1242
							resTime.hour = parseInt(treg[order.h], 10) + 12; // 12pm = 12 hour, any other pm = hour + 12
1243
						} else {
1244
							resTime.hour = Number(treg[order.h]);
1245
						}
1246
					}
1247
				}
1248
1249
				if (order.m !== -1) {
1250
					resTime.minute = Number(treg[order.m]);
1251
				}
1252
				if (order.s !== -1) {
1253
					resTime.second = Number(treg[order.s]);
1254
				}
1255
				if (order.l !== -1) {
1256
					resTime.millisec = Number(treg[order.l]);
1257
				}
1258
				if (order.c !== -1) {
1259
					resTime.microsec = Number(treg[order.c]);
1260
				}
1261
				if (order.z !== -1 && treg[order.z] !== undefined) {
1262
					resTime.timezone = $.timepicker.timezoneOffsetNumber(treg[order.z]);
1263
				}
1264
1265
1266
				return resTime;
1267
			}
1268
			return false;
1269
		};// end strictParse
1270
1271
		// First try JS Date, if that fails, use strictParse
1272
		var looseParse = function (f, s, o) {
1273
			try {
1274
				var d = new Date('2012-01-01 ' + s);
1275
				if (isNaN(d.getTime())) {
1276
					d = new Date('2012-01-01T' + s);
1277
					if (isNaN(d.getTime())) {
1278
						d = new Date('01/01/2012 ' + s);
1279
						if (isNaN(d.getTime())) {
1280
							throw "Unable to parse time with native Date: " + s;
1281
						}
1282
					}
1283
				}
1284
1285
				return {
1286
					hour: d.getHours(),
1287
					minute: d.getMinutes(),
1288
					second: d.getSeconds(),
1289
					millisec: d.getMilliseconds(),
1290
					microsec: d.getMicroseconds(),
1291
					timezone: d.getTimezoneOffset() * -1
1292
				};
1293
			}
1294
			catch (err) {
1295
				try {
1296
					return strictParse(f, s, o);
1297
				}
1298
				catch (err2) {
1299
					$.timepicker.log("Unable to parse \ntimeString: " + s + "\ntimeFormat: " + f);
1300
				}				
1301
			}
1302
			return false;
1303
		}; // end looseParse
1304
		
1305
		if (typeof o.parse === "function") {
1306
			return o.parse(timeFormat, timeString, o);
1307
		}
1308
		if (o.parse === 'loose') {
1309
			return looseParse(timeFormat, timeString, o);
1310
		}
1311
		return strictParse(timeFormat, timeString, o);
1312
	};
1313
1314
	/**
1315
	 * Public utility to format the time
1316
	 * @param {string} format format of the time
1317
	 * @param {Object} time Object not a Date for timezones
1318
	 * @param {Object} [options] essentially the regional[].. amNames, pmNames, ampm
1319
	 * @returns {string} the formatted time
1320
	 */
1321
	$.datepicker.formatTime = function (format, time, options) {
1322
		options = options || {};
1323
		options = $.extend({}, $.timepicker._defaults, options);
1324
		time = $.extend({
1325
			hour: 0,
1326
			minute: 0,
1327
			second: 0,
1328
			millisec: 0,
1329
			microsec: 0,
1330
			timezone: null
1331
		}, time);
1332
1333
		var tmptime = format,
1334
			ampmName = options.amNames[0],
1335
			hour = parseInt(time.hour, 10);
1336
1337
		if (hour > 11) {
1338
			ampmName = options.pmNames[0];
1339
		}
1340
1341
		tmptime = tmptime.replace(/(?:HH?|hh?|mm?|ss?|[tT]{1,2}|[zZ]|[lc]|'.*?')/g, function (match) {
1342
			switch (match) {
1343
			case 'HH':
1344
				return ('0' + hour).slice(-2);
1345
			case 'H':
1346
				return hour;
1347
			case 'hh':
1348
				return ('0' + convert24to12(hour)).slice(-2);
1349
			case 'h':
1350
				return convert24to12(hour);
1351
			case 'mm':
1352
				return ('0' + time.minute).slice(-2);
1353
			case 'm':
1354
				return time.minute;
1355
			case 'ss':
1356
				return ('0' + time.second).slice(-2);
1357
			case 's':
1358
				return time.second;
1359
			case 'l':
1360
				return ('00' + time.millisec).slice(-3);
1361
			case 'c':
1362
				return ('00' + time.microsec).slice(-3);
1363
			case 'z':
1364
				return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, false);
1365
			case 'Z':
1366
				return $.timepicker.timezoneOffsetString(time.timezone === null ? options.timezone : time.timezone, true);
1367
			case 'T':
1368
				return ampmName.charAt(0).toUpperCase();
1369
			case 'TT':
1370
				return ampmName.toUpperCase();
1371
			case 't':
1372
				return ampmName.charAt(0).toLowerCase();
1373
			case 'tt':
1374
				return ampmName.toLowerCase();
1375
			default:
1376
				return match.replace(/'/g, "");
1377
			}
1378
		});
1379
1380
		return tmptime;
1381
	};
1382
1383
	/*
1384
	* the bad hack :/ override datepicker so it doesn't close on select
1385
	// inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378
1386
	*/
1387
	$.datepicker._base_selectDate = $.datepicker._selectDate;
1388
	$.datepicker._selectDate = function (id, dateStr) {
1389
		var inst = this._getInst($(id)[0]),
1390
			tp_inst = this._get(inst, 'timepicker');
1391
1392
		if (tp_inst && inst.settings.showTimepicker) {
1393
			tp_inst._limitMinMaxDateTime(inst, true);
1394
			inst.inline = inst.stay_open = true;
1395
			//This way the onSelect handler called from calendarpicker get the full dateTime
1396
			this._base_selectDate(id, dateStr);
1397
			inst.inline = inst.stay_open = false;
1398
			this._notifyChange(inst);
1399
			this._updateDatepicker(inst);
1400
		} else {
1401
			this._base_selectDate(id, dateStr);
1402
		}
1403
	};
1404
1405
	/*
1406
	* second bad hack :/ override datepicker so it triggers an event when changing the input field
1407
	* and does not redraw the datepicker on every selectDate event
1408
	*/
1409
	$.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;
1410
	$.datepicker._updateDatepicker = function (inst) {
1411
1412
		// don't popup the datepicker if there is another instance already opened
1413
		var input = inst.input[0];
1414
		if ($.datepicker._curInst && $.datepicker._curInst !== inst && $.datepicker._datepickerShowing && $.datepicker._lastInput !== input) {
1415
			return;
1416
		}
1417
1418
		if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {
1419
1420
			this._base_updateDatepicker(inst);
1421
1422
			// Reload the time control when changing something in the input text field.
1423
			var tp_inst = this._get(inst, 'timepicker');
1424
			if (tp_inst) {
1425
				tp_inst._addTimePicker(inst);
1426
			}
1427
		}
1428
	};
1429
1430
	/*
1431
	* third bad hack :/ override datepicker so it allows spaces and colon in the input field
1432
	*/
1433
	$.datepicker._base_doKeyPress = $.datepicker._doKeyPress;
1434
	$.datepicker._doKeyPress = function (event) {
1435
		var inst = $.datepicker._getInst(event.target),
1436
			tp_inst = $.datepicker._get(inst, 'timepicker');
1437
1438
		if (tp_inst) {
1439
			if ($.datepicker._get(inst, 'constrainInput')) {
1440
				var ampm = tp_inst.support.ampm,
1441
					tz = tp_inst._defaults.showTimezone !== null ? tp_inst._defaults.showTimezone : tp_inst.support.timezone,
1442
					dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),
1443
					datetimeChars = tp_inst._defaults.timeFormat.toString()
1444
											.replace(/[hms]/g, '')
1445
											.replace(/TT/g, ampm ? 'APM' : '')
1446
											.replace(/Tt/g, ampm ? 'AaPpMm' : '')
1447
											.replace(/tT/g, ampm ? 'AaPpMm' : '')
1448
											.replace(/T/g, ampm ? 'AP' : '')
1449
											.replace(/tt/g, ampm ? 'apm' : '')
1450
											.replace(/t/g, ampm ? 'ap' : '') + 
1451
											" " + tp_inst._defaults.separator + 
1452
											tp_inst._defaults.timeSuffix + 
1453
											(tz ? tp_inst._defaults.timezoneList.join('') : '') + 
1454
											(tp_inst._defaults.amNames.join('')) + (tp_inst._defaults.pmNames.join('')) + 
1455
											dateChars,
1456
					chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);
1457
				return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1);
1458
			}
1459
		}
1460
1461
		return $.datepicker._base_doKeyPress(event);
1462
	};
1463
1464
	/*
1465
	* Fourth bad hack :/ override _updateAlternate function used in inline mode to init altField
1466
	* Update any alternate field to synchronise with the main field.
1467
	*/
1468
	$.datepicker._base_updateAlternate = $.datepicker._updateAlternate;
1469
	$.datepicker._updateAlternate = function (inst) {
1470
		var tp_inst = this._get(inst, 'timepicker');
1471
		if (tp_inst) {
1472
			var altField = tp_inst._defaults.altField;
1473
			if (altField) { // update alternate field too
1474
				var altFormat = tp_inst._defaults.altFormat || tp_inst._defaults.dateFormat,
1475
					date = this._getDate(inst),
1476
					formatCfg = $.datepicker._getFormatConfig(inst),
1477
					altFormattedDateTime = '', 
1478
					altSeparator = tp_inst._defaults.altSeparator ? tp_inst._defaults.altSeparator : tp_inst._defaults.separator, 
1479
					altTimeSuffix = tp_inst._defaults.altTimeSuffix ? tp_inst._defaults.altTimeSuffix : tp_inst._defaults.timeSuffix,
1480
					altTimeFormat = tp_inst._defaults.altTimeFormat !== null ? tp_inst._defaults.altTimeFormat : tp_inst._defaults.timeFormat;
1481
				
1482
				altFormattedDateTime += $.datepicker.formatTime(altTimeFormat, tp_inst, tp_inst._defaults) + altTimeSuffix;
1483
				if (!tp_inst._defaults.timeOnly && !tp_inst._defaults.altFieldTimeOnly && date !== null) {
1484
					if (tp_inst._defaults.altFormat) {
1485
						altFormattedDateTime = $.datepicker.formatDate(tp_inst._defaults.altFormat, date, formatCfg) + altSeparator + altFormattedDateTime;
1486
					}
1487
					else {
1488
						altFormattedDateTime = tp_inst.formattedDate + altSeparator + altFormattedDateTime;
1489
					}
1490
				}
1491
				$(altField).val( inst.input.val() ? altFormattedDateTime : "");
1492
			}
1493
		}
1494
		else {
1495
			$.datepicker._base_updateAlternate(inst);	
1496
		}
1497
	};
1498
1499
	/*
1500
	* Override key up event to sync manual input changes.
1501
	*/
1502
	$.datepicker._base_doKeyUp = $.datepicker._doKeyUp;
1503
	$.datepicker._doKeyUp = function (event) {
1504
		var inst = $.datepicker._getInst(event.target),
1505
			tp_inst = $.datepicker._get(inst, 'timepicker');
1506
1507
		if (tp_inst) {
1508
			if (tp_inst._defaults.timeOnly && (inst.input.val() !== inst.lastVal)) {
1509
				try {
1510
					$.datepicker._updateDatepicker(inst);
1511
				} catch (err) {
1512
					$.timepicker.log(err);
1513
				}
1514
			}
1515
		}
1516
1517
		return $.datepicker._base_doKeyUp(event);
1518
	};
1519
1520
	/*
1521
	* override "Today" button to also grab the time.
1522
	*/
1523
	$.datepicker._base_gotoToday = $.datepicker._gotoToday;
1524
	$.datepicker._gotoToday = function (id) {
1525
		var inst = this._getInst($(id)[0]),
1526
			$dp = inst.dpDiv;
1527
		this._base_gotoToday(id);
1528
		var tp_inst = this._get(inst, 'timepicker');
1529
		selectLocalTimezone(tp_inst);
1530
		var now = new Date();
1531
		this._setTime(inst, now);
1532
		$('.ui-datepicker-today', $dp).click();
1533
	};
1534
1535
	/*
1536
	* Disable & enable the Time in the datetimepicker
1537
	*/
1538
	$.datepicker._disableTimepickerDatepicker = function (target) {
1539
		var inst = this._getInst(target);
1540
		if (!inst) {
1541
			return;
1542
		}
1543
1544
		var tp_inst = this._get(inst, 'timepicker');
1545
		$(target).datepicker('getDate'); // Init selected[Year|Month|Day]
1546
		if (tp_inst) {
1547
			inst.settings.showTimepicker = false;
1548
			tp_inst._defaults.showTimepicker = false;
1549
			tp_inst._updateDateTime(inst);
1550
		}
1551
	};
1552
1553
	$.datepicker._enableTimepickerDatepicker = function (target) {
1554
		var inst = this._getInst(target);
1555
		if (!inst) {
1556
			return;
1557
		}
1558
1559
		var tp_inst = this._get(inst, 'timepicker');
1560
		$(target).datepicker('getDate'); // Init selected[Year|Month|Day]
1561
		if (tp_inst) {
1562
			inst.settings.showTimepicker = true;
1563
			tp_inst._defaults.showTimepicker = true;
1564
			tp_inst._addTimePicker(inst); // Could be disabled on page load
1565
			tp_inst._updateDateTime(inst);
1566
		}
1567
	};
1568
1569
	/*
1570
	* Create our own set time function
1571
	*/
1572
	$.datepicker._setTime = function (inst, date) {
1573
		var tp_inst = this._get(inst, 'timepicker');
1574
		if (tp_inst) {
1575
			var defaults = tp_inst._defaults;
1576
1577
			// calling _setTime with no date sets time to defaults
1578
			tp_inst.hour = date ? date.getHours() : defaults.hour;
1579
			tp_inst.minute = date ? date.getMinutes() : defaults.minute;
1580
			tp_inst.second = date ? date.getSeconds() : defaults.second;
1581
			tp_inst.millisec = date ? date.getMilliseconds() : defaults.millisec;
1582
			tp_inst.microsec = date ? date.getMicroseconds() : defaults.microsec;
1583
1584
			//check if within min/max times.. 
1585
			tp_inst._limitMinMaxDateTime(inst, true);
1586
1587
			tp_inst._onTimeChange();
1588
			tp_inst._updateDateTime(inst);
1589
		}
1590
	};
1591
1592
	/*
1593
	* Create new public method to set only time, callable as $().datepicker('setTime', date)
1594
	*/
1595
	$.datepicker._setTimeDatepicker = function (target, date, withDate) {
1596
		var inst = this._getInst(target);
1597
		if (!inst) {
1598
			return;
1599
		}
1600
1601
		var tp_inst = this._get(inst, 'timepicker');
1602
1603
		if (tp_inst) {
1604
			this._setDateFromField(inst);
1605
			var tp_date;
1606
			if (date) {
1607
				if (typeof date === "string") {
1608
					tp_inst._parseTime(date, withDate);
1609
					tp_date = new Date();
1610
					tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
1611
					tp_date.setMicroseconds(tp_inst.microsec);
1612
				} else {
1613
					tp_date = new Date(date.getTime());
1614
					tp_date.setMicroseconds(date.getMicroseconds());
1615
				}
1616
				if (tp_date.toString() === 'Invalid Date') {
1617
					tp_date = undefined;
1618
				}
1619
				this._setTime(inst, tp_date);
1620
			}
1621
		}
1622
1623
	};
1624
1625
	/*
1626
	* override setDate() to allow setting time too within Date object
1627
	*/
1628
	$.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;
1629
	$.datepicker._setDateDatepicker = function (target, _date) {
1630
		var inst = this._getInst(target);
1631
		var date = _date;
1632
		if (!inst) {
1633
			return;
1634
		}
1635
1636
		if (typeof(_date) === 'string') {
1637
			date = new Date(_date);
1638
			if (!date.getTime()) {
1639
				this._base_setDateDatepicker.apply(this, arguments);
1640
				date = $(target).datepicker('getDate');
1641
			}
1642
		}
1643
1644
		var tp_inst = this._get(inst, 'timepicker');
1645
		var tp_date;
1646
		if (date instanceof Date) {
1647
			tp_date = new Date(date.getTime());
1648
			tp_date.setMicroseconds(date.getMicroseconds());
1649
		} else {
1650
			tp_date = date;
1651
		}
1652
		
1653
		// This is important if you are using the timezone option, javascript's Date 
1654
		// object will only return the timezone offset for the current locale, so we 
1655
		// adjust it accordingly.  If not using timezone option this won't matter..
1656
		// If a timezone is different in tp, keep the timezone as is
1657
		if (tp_inst && tp_date) {
1658
			// look out for DST if tz wasn't specified
1659
			if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {
1660
				tp_inst.timezone = tp_date.getTimezoneOffset() * -1;
1661
			}
1662
			date = $.timepicker.timezoneAdjust(date, tp_inst.timezone);
1663
			tp_date = $.timepicker.timezoneAdjust(tp_date, tp_inst.timezone);
1664
		}
1665
1666
		this._updateDatepicker(inst);
1667
		this._base_setDateDatepicker.apply(this, arguments);
1668
		this._setTimeDatepicker(target, tp_date, true);
1669
	};
1670
1671
	/*
1672
	* override getDate() to allow getting time too within Date object
1673
	*/
1674
	$.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;
1675
	$.datepicker._getDateDatepicker = function (target, noDefault) {
1676
		var inst = this._getInst(target);
1677
		if (!inst) {
1678
			return;
1679
		}
1680
1681
		var tp_inst = this._get(inst, 'timepicker');
1682
1683
		if (tp_inst) {
1684
			// if it hasn't yet been defined, grab from field
1685
			if (inst.lastVal === undefined) {
1686
				this._setDateFromField(inst, noDefault);
1687
			}
1688
1689
			var date = this._getDate(inst);
1690
			if (date && tp_inst._parseTime($(target).val(), tp_inst.timeOnly)) {
1691
				date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);
1692
				date.setMicroseconds(tp_inst.microsec);
1693
1694
				// This is important if you are using the timezone option, javascript's Date 
1695
				// object will only return the timezone offset for the current locale, so we 
1696
				// adjust it accordingly.  If not using timezone option this won't matter..
1697
				if (tp_inst.timezone != null) {
0 ignored issues
show
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
1698
					// look out for DST if tz wasn't specified
1699
					if (!tp_inst.support.timezone && tp_inst._defaults.timezone === null) {
1700
						tp_inst.timezone = date.getTimezoneOffset() * -1;
1701
					}
1702
					date = $.timepicker.timezoneAdjust(date, tp_inst.timezone);
1703
				}
1704
			}
1705
			return date;
1706
		}
1707
		return this._base_getDateDatepicker(target, noDefault);
1708
	};
1709
1710
	/*
1711
	* override parseDate() because UI 1.8.14 throws an error about "Extra characters"
1712
	* An option in datapicker to ignore extra format characters would be nicer.
1713
	*/
1714
	$.datepicker._base_parseDate = $.datepicker.parseDate;
1715
	$.datepicker.parseDate = function (format, value, settings) {
1716
		var date;
1717
		try {
1718
			date = this._base_parseDate(format, value, settings);
1719
		} catch (err) {
1720
			// Hack!  The error message ends with a colon, a space, and
1721
			// the "extra" characters.  We rely on that instead of
1722
			// attempting to perfectly reproduce the parsing algorithm.
1723
			if (err.indexOf(":") >= 0) {
1724
				date = this._base_parseDate(format, value.substring(0, value.length - (err.length - err.indexOf(':') - 2)), settings);
1725
				$.timepicker.log("Error parsing the date string: " + err + "\ndate string = " + value + "\ndate format = " + format);
1726
			} else {
1727
				throw err;
1728
			}
1729
		}
1730
		return date;
1731
	};
1732
1733
	/*
1734
	* override formatDate to set date with time to the input
1735
	*/
1736
	$.datepicker._base_formatDate = $.datepicker._formatDate;
1737
	$.datepicker._formatDate = function (inst, day, month, year) {
1738
		var tp_inst = this._get(inst, 'timepicker');
1739
		if (tp_inst) {
1740
			tp_inst._updateDateTime(inst);
1741
			return tp_inst.$input.val();
1742
		}
1743
		return this._base_formatDate(inst);
1744
	};
1745
1746
	/*
1747
	* override options setter to add time to maxDate(Time) and minDate(Time). MaxDate
1748
	*/
1749
	$.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker;
1750
	$.datepicker._optionDatepicker = function (target, name, value) {
1751
		var inst = this._getInst(target),
1752
			name_clone;
1753
		if (!inst) {
1754
			return null;
1755
		}
1756
1757
		var tp_inst = this._get(inst, 'timepicker');
1758
		if (tp_inst) {
1759
			var min = null,
1760
				max = null,
1761
				onselect = null,
1762
				overrides = tp_inst._defaults.evnts,
1763
				fns = {},
1764
				prop;
1765
			if (typeof name === 'string') { // if min/max was set with the string
1766
				if (name === 'minDate' || name === 'minDateTime') {
1767
					min = value;
1768
				} else if (name === 'maxDate' || name === 'maxDateTime') {
1769
					max = value;
1770
				} else if (name === 'onSelect') {
1771
					onselect = value;
1772
				} else if (overrides.hasOwnProperty(name)) {
1773
					if (typeof (value) === 'undefined') {
1774
						return overrides[name];
1775
					}
1776
					fns[name] = value;
1777
					name_clone = {}; //empty results in exiting function after overrides updated
1778
				}
1779
			} else if (typeof name === 'object') { //if min/max was set with the JSON
1780
				if (name.minDate) {
1781
					min = name.minDate;
1782
				} else if (name.minDateTime) {
1783
					min = name.minDateTime;
1784
				} else if (name.maxDate) {
1785
					max = name.maxDate;
1786
				} else if (name.maxDateTime) {
1787
					max = name.maxDateTime;
1788
				}
1789
				for (prop in overrides) {
1790
					if (overrides.hasOwnProperty(prop) && name[prop]) {
1791
						fns[prop] = name[prop];
1792
					}
1793
				}
1794
			}
1795
			for (prop in fns) {
1796
				if (fns.hasOwnProperty(prop)) {
1797
					overrides[prop] = fns[prop];
1798
					if (!name_clone) { name_clone = $.extend({}, name); }
1799
					delete name_clone[prop];
1800
				}
1801
			}
1802
			if (name_clone && isEmptyObject(name_clone)) { return; }
1803
			if (min) { //if min was set
1804
				if (min === 0) {
1805
					min = new Date();
1806
				} else {
1807
					min = new Date(min);
1808
				}
1809
				tp_inst._defaults.minDate = min;
1810
				tp_inst._defaults.minDateTime = min;
1811
			} else if (max) { //if max was set
1812
				if (max === 0) {
1813
					max = new Date();
1814
				} else {
1815
					max = new Date(max);
1816
				}
1817
				tp_inst._defaults.maxDate = max;
1818
				tp_inst._defaults.maxDateTime = max;
1819
			} else if (onselect) {
1820
				tp_inst._defaults.onSelect = onselect;
1821
			}
1822
		}
1823
		if (value === undefined) {
1824
			return this._base_optionDatepicker.call($.datepicker, target, name);
1825
		}
1826
		return this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);
1827
	};
1828
	
1829
	/*
1830
	* jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype,
1831
	* it will return false for all objects
1832
	*/
1833
	var isEmptyObject = function (obj) {
1834
		var prop;
1835
		for (prop in obj) {
1836
			if (obj.hasOwnProperty(prop)) {
1837
				return false;
1838
			}
1839
		}
1840
		return true;
1841
	};
1842
1843
	/*
1844
	* jQuery extend now ignores nulls!
1845
	*/
1846
	var extendRemove = function (target, props) {
1847
		$.extend(target, props);
1848
		for (var name in props) {
1849
			if (props[name] === null || props[name] === undefined) {
1850
				target[name] = props[name];
1851
			}
1852
		}
1853
		return target;
1854
	};
1855
1856
	/*
1857
	* Determine by the time format which units are supported
1858
	* Returns an object of booleans for each unit
1859
	*/
1860
	var detectSupport = function (timeFormat) {
1861
		var tf = timeFormat.replace(/'.*?'/g, '').toLowerCase(), // removes literals
1862
			isIn = function (f, t) { // does the format contain the token?
1863
					return f.indexOf(t) !== -1 ? true : false;
1864
				};
1865
		return {
1866
				hour: isIn(tf, 'h'),
1867
				minute: isIn(tf, 'm'),
1868
				second: isIn(tf, 's'),
1869
				millisec: isIn(tf, 'l'),
1870
				microsec: isIn(tf, 'c'),
1871
				timezone: isIn(tf, 'z'),
1872
				ampm: isIn(tf, 't') && isIn(timeFormat, 'h'),
1873
				iso8601: isIn(timeFormat, 'Z')
1874
			};
1875
	};
1876
1877
	/*
1878
	* Converts 24 hour format into 12 hour
1879
	* Returns 12 hour without leading 0
1880
	*/
1881
	var convert24to12 = function (hour) {
1882
		hour %= 12;
1883
1884
		if (hour === 0) {
1885
			hour = 12;
1886
		}
1887
1888
		return String(hour);
1889
	};
1890
1891
	var computeEffectiveSetting = function (settings, property) {
1892
		return settings && settings[property] ? settings[property] : $.timepicker._defaults[property];
1893
	};
1894
1895
	/*
1896
	* Splits datetime string into date and time substrings.
1897
	* Throws exception when date can't be parsed
1898
	* Returns {dateString: dateString, timeString: timeString}
1899
	*/
1900
	var splitDateTime = function (dateTimeString, timeSettings) {
1901
		// The idea is to get the number separator occurrences in datetime and the time format requested (since time has
1902
		// fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split.
1903
		var separator = computeEffectiveSetting(timeSettings, 'separator'),
1904
			format = computeEffectiveSetting(timeSettings, 'timeFormat'),
1905
			timeParts = format.split(separator), // how many occurrences of separator may be in our format?
1906
			timePartsLen = timeParts.length,
1907
			allParts = dateTimeString.split(separator),
1908
			allPartsLen = allParts.length;
1909
1910
		if (allPartsLen > 1) {
1911
			return {
1912
				dateString: allParts.splice(0, allPartsLen - timePartsLen).join(separator),
1913
				timeString: allParts.splice(0, timePartsLen).join(separator)
1914
			};
1915
		}
1916
1917
		return {
1918
			dateString: dateTimeString,
1919
			timeString: ''
1920
		};
1921
	};
1922
1923
	/*
1924
	* Internal function to parse datetime interval
1925
	* Returns: {date: Date, timeObj: Object}, where
1926
	*   date - parsed date without time (type Date)
1927
	*   timeObj = {hour: , minute: , second: , millisec: , microsec: } - parsed time. Optional
1928
	*/
1929
	var parseDateTimeInternal = function (dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {
1930
		var date,
1931
			parts,
1932
			parsedTime;
1933
1934
		parts = splitDateTime(dateTimeString, timeSettings);
1935
		date = $.datepicker._base_parseDate(dateFormat, parts.dateString, dateSettings);
1936
1937
		if (parts.timeString === '') {
1938
			return {
1939
				date: date
1940
			};
1941
		}
1942
1943
		parsedTime = $.datepicker.parseTime(timeFormat, parts.timeString, timeSettings);
1944
1945
		if (!parsedTime) {
1946
			throw 'Wrong time format';
1947
		}
1948
1949
		return {
1950
			date: date,
1951
			timeObj: parsedTime
1952
		};
1953
	};
1954
1955
	/*
1956
	* Internal function to set timezone_select to the local timezone
1957
	*/
1958
	var selectLocalTimezone = function (tp_inst, date) {
1959
		if (tp_inst && tp_inst.timezone_select) {
1960
			var now = date || new Date();
1961
			tp_inst.timezone_select.val(-now.getTimezoneOffset());
1962
		}
1963
	};
1964
1965
	/*
1966
	* Create a Singleton Instance
1967
	*/
1968
	$.timepicker = new Timepicker();
1969
1970
	/**
1971
	 * Get the timezone offset as string from a date object (eg '+0530' for UTC+5.5)
1972
	 * @param {number} tzMinutes if not a number, less than -720 (-1200), or greater than 840 (+1400) this value is returned
1973
	 * @param {boolean} iso8601 if true formats in accordance to iso8601 "+12:45"
1974
	 * @return {string}
1975
	 */
1976
	$.timepicker.timezoneOffsetString = function (tzMinutes, iso8601) {
1977
		if (isNaN(tzMinutes) || tzMinutes > 840 || tzMinutes < -720) {
1978
			return tzMinutes;
1979
		}
1980
1981
		var off = tzMinutes,
1982
			minutes = off % 60,
1983
			hours = (off - minutes) / 60,
1984
			iso = iso8601 ? ':' : '',
1985
			tz = (off >= 0 ? '+' : '-') + ('0' + Math.abs(hours)).slice(-2) + iso + ('0' + Math.abs(minutes)).slice(-2);
1986
		
1987
		if (tz === '+00:00') {
1988
			return 'Z';
1989
		}
1990
		return tz;
1991
	};
1992
1993
	/**
1994
	 * Get the number in minutes that represents a timezone string
1995
	 * @param  {string} tzString formatted like "+0500", "-1245", "Z"
1996
	 * @return {number} the offset minutes or the original string if it doesn't match expectations
1997
	 */
1998
	$.timepicker.timezoneOffsetNumber = function (tzString) {
1999
		var normalized = tzString.toString().replace(':', ''); // excuse any iso8601, end up with "+1245"
2000
2001
		if (normalized.toUpperCase() === 'Z') { // if iso8601 with Z, its 0 minute offset
2002
			return 0;
2003
		}
2004
2005
		if (!/^(\-|\+)\d{4}$/.test(normalized)) { // possibly a user defined tz, so just give it back
2006
			return tzString;
2007
		}
2008
2009
		return ((normalized.substr(0, 1) === '-' ? -1 : 1) * // plus or minus
2010
					((parseInt(normalized.substr(1, 2), 10) * 60) + // hours (converted to minutes)
2011
					parseInt(normalized.substr(3, 2), 10))); // minutes
2012
	};
2013
2014
	/**
2015
	 * No way to set timezone in js Date, so we must adjust the minutes to compensate. (think setDate, getDate)
2016
	 * @param  {Date} date
2017
	 * @param  {string} toTimezone formatted like "+0500", "-1245"
2018
	 * @return {Date}
2019
	 */
2020
	$.timepicker.timezoneAdjust = function (date, toTimezone) {
2021
		var toTz = $.timepicker.timezoneOffsetNumber(toTimezone);
2022
		if (!isNaN(toTz)) {
2023
			date.setMinutes(date.getMinutes() + -date.getTimezoneOffset() - toTz);
2024
		}
2025
		return date;
2026
	};
2027
2028
	/**
2029
	 * Calls `timepicker()` on the `startTime` and `endTime` elements, and configures them to
2030
	 * enforce date range limits.
2031
	 * n.b. The input value must be correctly formatted (reformatting is not supported)
2032
	 * @param  {Element} startTime
2033
	 * @param  {Element} endTime
2034
	 * @param  {Object} options Options for the timepicker() call
2035
	 * @return {jQuery}
2036
	 */
2037
	$.timepicker.timeRange = function (startTime, endTime, options) {
2038
		return $.timepicker.handleRange('timepicker', startTime, endTime, options);
2039
	};
2040
2041
	/**
2042
	 * Calls `datetimepicker` on the `startTime` and `endTime` elements, and configures them to
2043
	 * enforce date range limits.
2044
	 * @param  {Element} startTime
2045
	 * @param  {Element} endTime
2046
	 * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,
2047
	 *   a boolean value that can be used to reformat the input values to the `dateFormat`.
2048
	 * @param  {string} method Can be used to specify the type of picker to be added
2049
	 * @return {jQuery}
2050
	 */
2051
	$.timepicker.datetimeRange = function (startTime, endTime, options) {
2052
		$.timepicker.handleRange('datetimepicker', startTime, endTime, options);
2053
	};
2054
2055
	/**
2056
	 * Calls `datepicker` on the `startTime` and `endTime` elements, and configures them to
2057
	 * enforce date range limits.
2058
	 * @param  {Element} startTime
2059
	 * @param  {Element} endTime
2060
	 * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,
2061
	 *   a boolean value that can be used to reformat the input values to the `dateFormat`.
2062
	 * @return {jQuery}
2063
	 */
2064
	$.timepicker.dateRange = function (startTime, endTime, options) {
2065
		$.timepicker.handleRange('datepicker', startTime, endTime, options);
2066
	};
2067
2068
	/**
2069
	 * Calls `method` on the `startTime` and `endTime` elements, and configures them to
2070
	 * enforce date range limits.
2071
	 * @param  {string} method Can be used to specify the type of picker to be added
2072
	 * @param  {Element} startTime
2073
	 * @param  {Element} endTime
2074
	 * @param  {Object} options Options for the `timepicker()` call. Also supports `reformat`,
2075
	 *   a boolean value that can be used to reformat the input values to the `dateFormat`.
2076
	 * @return {jQuery}
2077
	 */
2078
	$.timepicker.handleRange = function (method, startTime, endTime, options) {
2079
		options = $.extend({}, {
2080
			minInterval: 0, // min allowed interval in milliseconds
2081
			maxInterval: 0, // max allowed interval in milliseconds
2082
			start: {},      // options for start picker
2083
			end: {}         // options for end picker
2084
		}, options);
2085
2086
		// for the mean time this fixes an issue with calling getDate with timepicker()
2087
		var timeOnly = false;
2088
		if(method === 'timepicker'){
2089
			timeOnly = true;
2090
			method = 'datetimepicker';
2091
		}
2092
2093
		function checkDates(changed, other) {
2094
			var startdt = startTime[method]('getDate'),
2095
				enddt = endTime[method]('getDate'),
2096
				changeddt = changed[method]('getDate');
2097
2098
			if (startdt !== null) {
2099
				var minDate = new Date(startdt.getTime()),
2100
					maxDate = new Date(startdt.getTime());
2101
2102
				minDate.setMilliseconds(minDate.getMilliseconds() + options.minInterval);
2103
				maxDate.setMilliseconds(maxDate.getMilliseconds() + options.maxInterval);
2104
2105
				if (options.minInterval > 0 && minDate > enddt) { // minInterval check
2106
					endTime[method]('setDate', minDate);
2107
				}
2108
				else if (options.maxInterval > 0 && maxDate < enddt) { // max interval check
2109
					endTime[method]('setDate', maxDate);
2110
				}
2111
				else if (startdt > enddt) {
2112
					other[method]('setDate', changeddt);
2113
				}
2114
			}
2115
		}
2116
2117
		function selected(changed, other, option) {
2118
			if (!changed.val()) {
2119
				return;
2120
			}
2121
			var date = changed[method].call(changed, 'getDate');
2122
			if (date !== null && options.minInterval > 0) {
2123
				if (option === 'minDate') {
2124
					date.setMilliseconds(date.getMilliseconds() + options.minInterval);
2125
				}
2126
				if (option === 'maxDate') {
2127
					date.setMilliseconds(date.getMilliseconds() - options.minInterval);
2128
				}
2129
			}
2130
			if (date.getTime) {
2131
				other[method].call(other, 'option', option, date);
2132
			}
2133
		}
2134
2135
		$.fn[method].call(startTime, $.extend({
2136
			timeOnly: timeOnly,
2137
			onClose: function (dateText, inst) {
2138
				checkDates($(this), endTime);
2139
			},
2140
			onSelect: function (selectedDateTime) {
2141
				selected($(this), endTime, 'minDate');
2142
			}
2143
		}, options, options.start));
2144
		$.fn[method].call(endTime, $.extend({
2145
			timeOnly: timeOnly,
2146
			onClose: function (dateText, inst) {
2147
				checkDates($(this), startTime);
2148
			},
2149
			onSelect: function (selectedDateTime) {
2150
				selected($(this), startTime, 'maxDate');
2151
			}
2152
		}, options, options.end));
2153
2154
		checkDates(startTime, endTime);
2155
		selected(startTime, endTime, 'minDate');
2156
		selected(endTime, startTime, 'maxDate');
2157
		return $([startTime.get(0), endTime.get(0)]);
2158
	};
2159
2160
	/**
2161
	 * Log error or data to the console during error or debugging
2162
	 * @param  {Object} err pass any type object to log to the console during error or debugging
2163
	 * @return {void}
2164
	 */
2165
	$.timepicker.log = function (err) {
2166
		if (window.console) {
2167
			window.console.log(err);
2168
		}
2169
	};
2170
2171
	/*
2172
	 * Add util object to allow access to private methods for testability.
2173
	 */
2174
	$.timepicker._util = {
2175
		_extendRemove: extendRemove,
2176
		_isEmptyObject: isEmptyObject,
2177
		_convert24to12: convert24to12,
2178
		_detectSupport: detectSupport,
2179
		_selectLocalTimezone: selectLocalTimezone,
2180
		_computeEffectiveSetting: computeEffectiveSetting,
2181
		_splitDateTime: splitDateTime,
2182
		_parseDateTimeInternal: parseDateTimeInternal
2183
	};
2184
2185
	/*
2186
	* Microsecond support
2187
	*/
2188
	if (!Date.prototype.getMicroseconds) {
2189
		Date.prototype.microseconds = 0;
2190
		Date.prototype.getMicroseconds = function () { return this.microseconds; };
2191
		Date.prototype.setMicroseconds = function (m) {
2192
			this.setMilliseconds(this.getMilliseconds() + Math.floor(m / 1000));
2193
			this.microseconds = m % 1000;
2194
			return this;
2195
		};
2196
	}
2197
2198
	/*
2199
	* Keep up with the version
2200
	*/
2201
	$.timepicker.version = "1.4.6";
2202
2203
})(jQuery);