2 * jQuery timepicker addon
3 * By: Trent Richardson [http://trentrichardson.com]
5 * Last Modified: 07/20/2011
7 * Copyright 2011 Trent Richardson
8 * Dual licensed under the MIT and GPL licenses.
9 * http://trentrichardson.com/Impromptu/GPL-LICENSE.txt
10 * http://trentrichardson.com/Impromptu/MIT-LICENSE.txt
13 * .ui-timepicker-div .ui-widget-header{ margin-bottom: 8px; }
14 * .ui-timepicker-div dl{ text-align: left; }
15 * .ui-timepicker-div dl dt{ height: 25px; }
16 * .ui-timepicker-div dl dd{ margin: -25px 10px 10px 65px; }
17 * .ui-timepicker-div td { font-size: 90%; }
22 $.extend($.ui
, { timepicker
: { version
: "0.9.6" } });
24 /* Time picker 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. */
29 function Timepicker() {
30 this.regional
= []; // Available regional settings, indexed by language code
31 this.regional
[''] = { // Default regional settings
35 timeFormat
: 'hh:mm tt',
37 timeOnlyTitle
: 'Choose Time',
42 timezoneText
: 'Time Zone'
44 this._defaults
= { // Global defaults for all the datetime picker instances
45 showButtonPanel
: true,
72 altFieldTimeOnly
: true,
74 timezoneList
: ["-1100", "-1000", "-0900", "-0800", "-0700", "-0600",
75 "-0500", "-0400", "-0300", "-0200", "-0100", "+0000",
76 "+0100", "+0200", "+0300", "+0400", "+0500", "+0600",
77 "+0700", "+0800", "+0900", "+1000", "+1100", "+1200"]
79 $.extend(this._defaults
, this.regional
['']);
82 $.extend(Timepicker
.prototype, {
90 timezone_select
: null,
95 hourMinOriginal
: null,
96 minuteMinOriginal
: null,
97 secondMinOriginal
: null,
98 hourMaxOriginal
: null,
99 minuteMaxOriginal
: null,
100 secondMaxOriginal
: null,
104 formattedDateTime
: '',
105 timezoneList
: ["-1100", "-1000", "-0900", "-0800", "-0700", "-0600",
106 "-0500", "-0400", "-0300", "-0200", "-0100", "+0000",
107 "+0100", "+0200", "+0300", "+0400", "+0500", "+0600",
108 "+0700", "+0800", "+0900", "+1000", "+1100", "+1200"],
110 /* Override the default settings for all instances of the time picker.
111 @param settings object - the new settings to use as defaults (anonymous object)
112 @return the manager object */
113 setDefaults: function(settings
) {
114 extendRemove(this._defaults
, settings
|| {});
118 //########################################################################
119 // Create a new Timepicker instance
120 //########################################################################
121 _newInst: function($input
, o
) {
122 var tp_inst
= new Timepicker(),
125 for (var attrName
in this._defaults
) {
126 var attrValue
= $input
.attr('time:' + attrName
);
129 inlineSettings
[attrName
] = eval(attrValue
);
131 inlineSettings
[attrName
] = attrValue
;
135 tp_inst
._defaults
= $.extend({}, this._defaults
, inlineSettings
, o
, {
136 beforeShow: function(input
, dp_inst
) {
137 if ($.isFunction(o
.beforeShow
))
138 o
.beforeShow(input
, dp_inst
, tp_inst
);
140 onChangeMonthYear: function(year
, month
, dp_inst
) {
141 // Update the time as well : this prevents the time from disappearing from the $input field.
142 tp_inst
._updateDateTime(dp_inst
);
143 if ($.isFunction(o
.onChangeMonthYear
))
144 o
.onChangeMonthYear
.call($input
[0], year
, month
, dp_inst
, tp_inst
);
146 onClose: function(dateText
, dp_inst
) {
147 if (tp_inst
.timeDefined
=== true && $input
.val() != '')
148 tp_inst
._updateDateTime(dp_inst
);
149 if ($.isFunction(o
.onClose
))
150 o
.onClose
.call($input
[0], dateText
, dp_inst
, tp_inst
);
152 timepicker
: tp_inst
// add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');
155 tp_inst
.hour
= tp_inst
._defaults
.hour
;
156 tp_inst
.minute
= tp_inst
._defaults
.minute
;
157 tp_inst
.second
= tp_inst
._defaults
.second
;
159 tp_inst
.$input
= $input
;
162 tp_inst
.$altInput
= $(o
.altField
)
163 .css({ cursor
: 'pointer' })
164 .focus(function(){ $input
.trigger("focus"); });
166 // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..
167 if(tp_inst
._defaults
.minDate
!== undefined && tp_inst
._defaults
.minDate
instanceof Date
)
168 tp_inst
._defaults
.minDateTime
= new Date(tp_inst
._defaults
.minDate
.getTime());
169 if(tp_inst
._defaults
.minDateTime
!== undefined && tp_inst
._defaults
.minDateTime
instanceof Date
)
170 tp_inst
._defaults
.minDate
= new Date(tp_inst
._defaults
.minDateTime
.getTime());
171 if(tp_inst
._defaults
.maxDate
!== undefined && tp_inst
._defaults
.maxDate
instanceof Date
)
172 tp_inst
._defaults
.maxDateTime
= new Date(tp_inst
._defaults
.maxDate
.getTime());
173 if(tp_inst
._defaults
.maxDateTime
!== undefined && tp_inst
._defaults
.maxDateTime
instanceof Date
)
174 tp_inst
._defaults
.maxDate
= new Date(tp_inst
._defaults
.maxDateTime
.getTime());
179 //########################################################################
180 // add our sliders to the calendar
181 //########################################################################
182 _addTimePicker: function(dp_inst
) {
183 var currDT
= (this.$altInput
&& this._defaults
.altFieldTimeOnly
) ?
184 this.$input
.val() + ' ' + this.$altInput
.val() :
187 this.timeDefined
= this._parseTime(currDT
);
188 this._limitMinMaxDateTime(dp_inst
, false);
189 this._injectTimePicker();
192 //########################################################################
193 // parse the time string from input value or _setTime
194 //########################################################################
195 _parseTime: function(timeString
, withDate
) {
196 var regstr
= this._defaults
.timeFormat
.toString()
197 .replace(/h{1,2}/ig, '(\\d?\\d)')
198 .replace(/m{1,2}/ig, '(\\d?\\d)')
199 .replace(/s{1,2}/ig, '(\\d?\\d)')
200 .replace(/t{1,2}/ig, '(am|pm|a|p)?')
201 .replace(/z{1}/ig, '((\\+|-)\\d\\d\\d\\d)?')
202 .replace(/\s/g, '\\s?') + this._defaults
.timeSuffix
+ '$',
203 order
= this._getFormatPositions(),
206 if (!this.inst
) this.inst
= $.datepicker
._getInst(this.$input
[0]);
208 if (withDate
|| !this._defaults
.timeOnly
) {
209 // the time should come after x number of characters and a space.
210 // x = at least the length of text specified by the date format
211 var dp_dateFormat
= $.datepicker
._get(this.inst
, 'dateFormat');
212 // escape special regex characters in the seperator
213 var specials
= new RegExp("[.*+?|()\\[\\]{}\\\\]", "g");
214 regstr
= '.{' + dp_dateFormat
.length
+ ',}' + this._defaults
.separator
.replace(specials
, "\\$&") + regstr
;
217 treg
= timeString
.match(new RegExp(regstr
, 'i'));
221 this.ampm
= ((treg
[order
.t
] === undefined || treg
[order
.t
].length
=== 0) ?
223 (treg
[order
.t
].charAt(0).toUpperCase() == 'A') ? 'AM' : 'PM').toUpperCase();
225 if (order
.h
!== -1) {
226 if (this.ampm
== 'AM' && treg
[order
.h
] == '12')
227 this.hour
= 0; // 12am = 0 hour
228 else if (this.ampm
== 'PM' && treg
[order
.h
] != '12')
229 this.hour
= (parseFloat(treg
[order
.h
]) + 12).toFixed(0); // 12pm = 12 hour, any other pm = hour + 12
230 else this.hour
= Number(treg
[order
.h
]);
233 if (order
.m
!== -1) this.minute
= Number(treg
[order
.m
]);
234 if (order
.s
!== -1) this.second
= Number(treg
[order
.s
]);
235 if (order
.z
!== -1) this.timezone
= treg
[order
.z
];
243 //########################################################################
244 // figure out position of time elements.. cause js cant do named captures
245 //########################################################################
246 _getFormatPositions: function() {
247 var finds
= this._defaults
.timeFormat
.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|t{1,2}|z)/g),
248 orders
= { h
: -1, m
: -1, s
: -1, t
: -1, z
: -1 };
251 for (var i
= 0; i
< finds
.length
; i
++)
252 if (orders
[finds
[i
].toString().charAt(0)] == -1)
253 orders
[finds
[i
].toString().charAt(0)] = i
+ 1;
258 //########################################################################
259 // generate and inject html for timepicker into ui datepicker
260 //########################################################################
261 _injectTimePicker: function() {
262 var $dp
= this.inst
.dpDiv
,
265 // Added by Peter Medeiros:
266 // - Figure out what the hour/minute/second max should be based on the step values.
267 // - Example: if stepMinute is 15, then minMax is 45.
268 hourMax
= (o
.hourMax
- (o
.hourMax
% o
.stepHour
)).toFixed(0),
269 minMax
= (o
.minuteMax
- (o
.minuteMax
% o
.stepMinute
)).toFixed(0),
270 secMax
= (o
.secondMax
- (o
.secondMax
% o
.stepSecond
)).toFixed(0),
271 dp_id
= this.inst
.id
.toString().replace(/([^A-Za-z0-9_])/g, '');
273 // Prevent displaying twice
274 //if ($dp.find("div#ui-timepicker-div-"+ dp_id).length === 0) {
275 if ($dp
.find("div#ui-timepicker-div-"+ dp_id
).length
=== 0 && o
.showTimepicker
) {
276 var noDisplay
= ' style="display:none;"',
277 html
= '<div class="ui-timepicker-div" id="ui-timepicker-div-' + dp_id
+ '"><dl>' +
278 '<dt class="ui_tpicker_time_label" id="ui_tpicker_time_label_' + dp_id
+ '"' +
279 ((o
.showTime
) ? '' : noDisplay
) + '>' + o
.timeText
+ '</dt>' +
280 '<dd class="ui_tpicker_time" id="ui_tpicker_time_' + dp_id
+ '"' +
281 ((o
.showTime
) ? '' : noDisplay
) + '></dd>' +
282 '<dt class="ui_tpicker_hour_label" id="ui_tpicker_hour_label_' + dp_id
+ '"' +
283 ((o
.showHour
) ? '' : noDisplay
) + '>' + o
.hourText
+ '</dt>',
289 if (o
.showHour
&& o
.hourGrid
> 0) {
290 html
+= '<dd class="ui_tpicker_hour">' +
291 '<div id="ui_tpicker_hour_' + dp_id
+ '"' + ((o
.showHour
) ? '' : noDisplay
) + '></div>' +
292 '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>';
294 for (var h
= o
.hourMin
; h
<= hourMax
; h
+= o
.hourGrid
) {
296 var tmph
= (o
.ampm
&& h
> 12) ? h
-12 : h
;
297 if (tmph
< 10) tmph
= '0' + tmph
;
299 if (h
== 0) tmph
= 12 +'a';
300 else if (h
< 12) tmph
+= 'a';
303 html
+= '<td>' + tmph
+ '</td>';
306 html
+= '</tr></table></div>' +
308 } else html
+= '<dd class="ui_tpicker_hour" id="ui_tpicker_hour_' + dp_id
+ '"' +
309 ((o
.showHour
) ? '' : noDisplay
) + '></dd>';
311 html
+= '<dt class="ui_tpicker_minute_label" id="ui_tpicker_minute_label_' + dp_id
+ '"' +
312 ((o
.showMinute
) ? '' : noDisplay
) + '>' + o
.minuteText
+ '</dt>';
314 if (o
.showMinute
&& o
.minuteGrid
> 0) {
315 html
+= '<dd class="ui_tpicker_minute ui_tpicker_minute_' + o
.minuteGrid
+ '">' +
316 '<div id="ui_tpicker_minute_' + dp_id
+ '"' +
317 ((o
.showMinute
) ? '' : noDisplay
) + '></div>' +
318 '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>';
320 for (var m
= o
.minuteMin
; m
<= minMax
; m
+= o
.minuteGrid
) {
322 html
+= '<td>' + ((m
< 10) ? '0' : '') + m
+ '</td>';
325 html
+= '</tr></table></div>' +
327 } else html
+= '<dd class="ui_tpicker_minute" id="ui_tpicker_minute_' + dp_id
+ '"' +
328 ((o
.showMinute
) ? '' : noDisplay
) + '></dd>';
330 html
+= '<dt class="ui_tpicker_second_label" id="ui_tpicker_second_label_' + dp_id
+ '"' +
331 ((o
.showSecond
) ? '' : noDisplay
) + '>' + o
.secondText
+ '</dt>';
333 if (o
.showSecond
&& o
.secondGrid
> 0) {
334 html
+= '<dd class="ui_tpicker_second ui_tpicker_second_' + o
.secondGrid
+ '">' +
335 '<div id="ui_tpicker_second_' + dp_id
+ '"' +
336 ((o
.showSecond
) ? '' : noDisplay
) + '></div>' +
337 '<div style="padding-left: 1px"><table><tr>';
339 for (var s
= o
.secondMin
; s
<= secMax
; s
+= o
.secondGrid
) {
341 html
+= '<td>' + ((s
< 10) ? '0' : '') + s
+ '</td>';
344 html
+= '</tr></table></div>' +
346 } else html
+= '<dd class="ui_tpicker_second" id="ui_tpicker_second_' + dp_id
+ '"' +
347 ((o
.showSecond
) ? '' : noDisplay
) + '></dd>';
349 html
+= '<dt class="ui_tpicker_timezone_label" id="ui_tpicker_timezone_label_' + dp_id
+ '"' +
350 ((o
.showTimezone
) ? '' : noDisplay
) + '>' + o
.timezoneText
+ '</dt>';
351 html
+= '<dd class="ui_tpicker_timezone" id="ui_tpicker_timezone_' + dp_id
+ '"' +
352 ((o
.showTimezone
) ? '' : noDisplay
) + '></dd>';
354 html
+= '</dl></div>';
357 // if we only want time picker...
358 if (o
.timeOnly
=== true) {
360 '<div class="ui-widget-header ui-helper-clearfix ui-corner-all">' +
361 '<div class="ui-datepicker-title">' + o
.timeOnlyTitle
+ '</div>' +
363 $dp
.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();
366 this.hour_slider
= $tp
.find('#ui_tpicker_hour_'+ dp_id
).slider({
367 orientation
: "horizontal",
372 slide: function(event
, ui
) {
373 tp_inst
.hour_slider
.slider( "option", "value", ui
.value
);
374 tp_inst
._onTimeChange();
378 // Updated by Peter Medeiros:
379 // - Pass in Event and UI instance into slide function
380 this.minute_slider
= $tp
.find('#ui_tpicker_minute_'+ dp_id
).slider({
381 orientation
: "horizontal",
386 slide: function(event
, ui
) {
387 // update the global minute slider instance value with the current slider value
388 tp_inst
.minute_slider
.slider( "option", "value", ui
.value
);
389 tp_inst
._onTimeChange();
393 this.second_slider
= $tp
.find('#ui_tpicker_second_'+ dp_id
).slider({
394 orientation
: "horizontal",
399 slide: function(event
, ui
) {
400 tp_inst
.second_slider
.slider( "option", "value", ui
.value
);
401 tp_inst
._onTimeChange();
406 this.timezone_select
= $tp
.find('#ui_tpicker_timezone_'+ dp_id
).append('<select></select>').find("select");
407 $.fn
.append
.apply(this.timezone_select
,
408 $.map(o
.timezoneList
, function(val
, idx
) {
409 return $("<option />")
410 .val(typeof val
== "object" ? val
.value
: val
)
411 .text(typeof val
== "object" ? val
.label
: val
);
414 this.timezone_select
.val((typeof this.timezone
!= "undefined" && this.timezone
!= null && this.timezone
!= "") ? this.timezone
: o
.timezone
);
415 this.timezone_select
.change(function() {
416 tp_inst
._onTimeChange();
419 // Add grid functionality
420 if (o
.showHour
&& o
.hourGrid
> 0) {
421 size
= 100 * hourGridSize
* o
.hourGrid
/ (hourMax
- o
.hourMin
);
423 $tp
.find(".ui_tpicker_hour table").css({
425 marginLeft
: (size
/ (-2 * hourGridSize
)) + "%",
426 borderCollapse
: 'collapse'
427 }).find("td").each( function(index
) {
428 $(this).click(function() {
429 var h
= $(this).html();
431 var ap
= h
.substring(2).toLowerCase(),
432 aph
= parseInt(h
.substring(0,2), 10);
434 if (aph
== 12) h
= 0;
436 } else if (aph
== 12) h
= 12;
439 tp_inst
.hour_slider
.slider("option", "value", h
);
440 tp_inst
._onTimeChange();
441 tp_inst
._onSelectHandler();
444 width
: (100 / hourGridSize
) + '%',
451 if (o
.showMinute
&& o
.minuteGrid
> 0) {
452 size
= 100 * minuteGridSize
* o
.minuteGrid
/ (minMax
- o
.minuteMin
);
453 $tp
.find(".ui_tpicker_minute table").css({
455 marginLeft
: (size
/ (-2 * minuteGridSize
)) + "%",
456 borderCollapse
: 'collapse'
457 }).find("td").each(function(index
) {
458 $(this).click(function() {
459 tp_inst
.minute_slider
.slider("option", "value", $(this).html());
460 tp_inst
._onTimeChange();
461 tp_inst
._onSelectHandler();
464 width
: (100 / minuteGridSize
) + '%',
471 if (o
.showSecond
&& o
.secondGrid
> 0) {
472 $tp
.find(".ui_tpicker_second table").css({
474 marginLeft
: (size
/ (-2 * secondGridSize
)) + "%",
475 borderCollapse
: 'collapse'
476 }).find("td").each(function(index
) {
477 $(this).click(function() {
478 tp_inst
.second_slider
.slider("option", "value", $(this).html());
479 tp_inst
._onTimeChange();
480 tp_inst
._onSelectHandler();
483 width
: (100 / secondGridSize
) + '%',
490 var $buttonPanel
= $dp
.find('.ui-datepicker-buttonpane');
491 if ($buttonPanel
.length
) $buttonPanel
.before($tp
);
492 else $dp
.append($tp
);
494 this.$timeObj
= $tp
.find('#ui_tpicker_time_'+ dp_id
);
496 if (this.inst
!== null) {
497 var timeDefined
= this.timeDefined
;
498 this._onTimeChange();
499 this.timeDefined
= timeDefined
;
502 //Emulate datepicker onSelect behavior. Call on slidestop.
503 var onSelectDelegate = function() {
504 tp_inst
._onSelectHandler();
506 this.hour_slider
.bind('slidestop',onSelectDelegate
);
507 this.minute_slider
.bind('slidestop',onSelectDelegate
);
508 this.second_slider
.bind('slidestop',onSelectDelegate
);
512 //########################################################################
513 // This function tries to limit the ability to go outside the
514 // min/max date range
515 //########################################################################
516 _limitMinMaxDateTime: function(dp_inst
, adjustSliders
){
517 var o
= this._defaults
,
518 dp_date
= new Date(dp_inst
.selectedYear
, dp_inst
.selectedMonth
, dp_inst
.selectedDay
);
520 if(!this._defaults
.showTimepicker
) return; // No time so nothing to check here
522 if($.datepicker
._get(dp_inst
, 'minDateTime') !== null && dp_date
){
523 var minDateTime
= $.datepicker
._get(dp_inst
, 'minDateTime'),
524 minDateTimeDate
= new Date(minDateTime
.getFullYear(), minDateTime
.getMonth(), minDateTime
.getDate(), 0, 0, 0, 0);
526 if(this.hourMinOriginal
=== null || this.minuteMinOriginal
=== null || this.secondMinOriginal
=== null){
527 this.hourMinOriginal
= o
.hourMin
;
528 this.minuteMinOriginal
= o
.minuteMin
;
529 this.secondMinOriginal
= o
.secondMin
;
532 if(dp_inst
.settings
.timeOnly
|| minDateTimeDate
.getTime() == dp_date
.getTime()) {
533 this._defaults
.hourMin
= minDateTime
.getHours();
534 if (this.hour
<= this._defaults
.hourMin
) {
535 this.hour
= this._defaults
.hourMin
;
536 this._defaults
.minuteMin
= minDateTime
.getMinutes();
537 if (this.minute
<= this._defaults
.minuteMin
) {
538 this.minute
= this._defaults
.minuteMin
;
539 this._defaults
.secondMin
= minDateTime
.getSeconds();
541 if(this.second
< this._defaults
.secondMin
) this.second
= this._defaults
.secondMin
;
542 this._defaults
.secondMin
= this.secondMinOriginal
;
545 this._defaults
.minuteMin
= this.minuteMinOriginal
;
546 this._defaults
.secondMin
= this.secondMinOriginal
;
549 this._defaults
.hourMin
= this.hourMinOriginal
;
550 this._defaults
.minuteMin
= this.minuteMinOriginal
;
551 this._defaults
.secondMin
= this.secondMinOriginal
;
555 if($.datepicker
._get(dp_inst
, 'maxDateTime') !== null && dp_date
){
556 var maxDateTime
= $.datepicker
._get(dp_inst
, 'maxDateTime'),
557 maxDateTimeDate
= new Date(maxDateTime
.getFullYear(), maxDateTime
.getMonth(), maxDateTime
.getDate(), 0, 0, 0, 0);
559 if(this.hourMaxOriginal
=== null || this.minuteMaxOriginal
=== null || this.secondMaxOriginal
=== null){
560 this.hourMaxOriginal
= o
.hourMax
;
561 this.minuteMaxOriginal
= o
.minuteMax
;
562 this.secondMaxOriginal
= o
.secondMax
;
565 if(dp_inst
.settings
.timeOnly
|| maxDateTimeDate
.getTime() == dp_date
.getTime()){
566 this._defaults
.hourMax
= maxDateTime
.getHours();
567 if (this.hour
>= this._defaults
.hourMax
) {
568 this.hour
= this._defaults
.hourMax
;
569 this._defaults
.minuteMax
= maxDateTime
.getMinutes();
570 if (this.minute
>= this._defaults
.minuteMax
) {
571 this.minute
= this._defaults
.minuteMax
;
572 this._defaults
.secondMax
= maxDateTime
.getSeconds();
574 if(this.second
> this._defaults
.secondMax
) this.second
= this._defaults
.secondMax
;
575 this._defaults
.secondMax
= this.secondMaxOriginal
;
578 this._defaults
.minuteMax
= this.minuteMaxOriginal
;
579 this._defaults
.secondMax
= this.secondMaxOriginal
;
582 this._defaults
.hourMax
= this.hourMaxOriginal
;
583 this._defaults
.minuteMax
= this.minuteMaxOriginal
;
584 this._defaults
.secondMax
= this.secondMaxOriginal
;
588 if(adjustSliders
!== undefined && adjustSliders
=== true){
589 var hourMax
= (this._defaults
.hourMax
- (this._defaults
.hourMax
% this._defaults
.stepHour
)).toFixed(0),
590 minMax
= (this._defaults
.minuteMax
- (this._defaults
.minuteMax
% this._defaults
.stepMinute
)).toFixed(0),
591 secMax
= (this._defaults
.secondMax
- (this._defaults
.secondMax
% this._defaults
.stepSecond
)).toFixed(0);
594 this.hour_slider
.slider("option", { min
: this._defaults
.hourMin
, max
: hourMax
}).slider('value', this.hour
);
595 if(this.minute_slider
)
596 this.minute_slider
.slider("option", { min
: this._defaults
.minuteMin
, max
: minMax
}).slider('value', this.minute
);
597 if(this.second_slider
)
598 this.second_slider
.slider("option", { min
: this._defaults
.secondMin
, max
: secMax
}).slider('value', this.second
);
604 //########################################################################
605 // when a slider moves, set the internal time...
606 // on time change is also called when the time is updated in the text field
607 //########################################################################
608 _onTimeChange: function() {
609 var hour
= (this.hour_slider
) ? this.hour_slider
.slider('value') : false,
610 minute
= (this.minute_slider
) ? this.minute_slider
.slider('value') : false,
611 second
= (this.second_slider
) ? this.second_slider
.slider('value') : false,
612 timezone
= (this.timezone_select
) ? this.timezone_select
.val() : false;
614 if (typeof(hour
) == 'object') hour
= false;
615 if (typeof(minute
) == 'object') minute
= false;
616 if (typeof(second
) == 'object') second
= false;
617 if (typeof(timezone
) == 'object') timezone
= false;
619 if (hour
!== false) hour
= parseInt(hour
,10);
620 if (minute
!== false) minute
= parseInt(minute
,10);
621 if (second
!== false) second
= parseInt(second
,10);
623 var ampm
= (hour
< 12) ? 'AM' : 'PM';
625 // If the update was done in the input field, the input field should not be updated.
626 // If the update was done using the sliders, update the input field.
627 var hasChanged
= (hour
!= this.hour
|| minute
!= this.minute
|| second
!= this.second
|| (this.ampm
.length
> 0 && this.ampm
!= ampm
) || timezone
!= this.timezone
);
631 if (hour
!== false)this.hour
= hour
;
632 if (minute
!== false) this.minute
= minute
;
633 if (second
!== false) this.second
= second
;
634 if (timezone
!== false) this.timezone
= timezone
;
636 if (!this.inst
) this.inst
= $.datepicker
._getInst(this.$input
[0]);
638 this._limitMinMaxDateTime(this.inst
, true);
640 if (this._defaults
.ampm
) this.ampm
= ampm
;
643 if (this.$timeObj
) this.$timeObj
.text(this.formattedTime
+ this._defaults
.timeSuffix
);
644 this.timeDefined
= true;
645 if (hasChanged
) this._updateDateTime();
648 //########################################################################
649 // call custom onSelect.
650 // bind to sliders slidestop, and grid click.
651 //########################################################################
652 _onSelectHandler: function() {
653 var onSelect
= this._defaults
['onSelect'];
654 var inputEl
= this.$input
? this.$input
[0] : null;
655 if (onSelect
&& inputEl
) {
656 onSelect
.apply(inputEl
, [this.formattedDateTime
, this]);
660 //########################################################################
661 // format the time all pretty...
662 //########################################################################
663 _formatTime: function(time
, format
, ampm
) {
664 if (ampm
== undefined) ampm
= this._defaults
.ampm
;
665 time
= time
|| { hour
: this.hour
, minute
: this.minute
, second
: this.second
, ampm
: this.ampm
, timezone
: this.timezone
};
666 var tmptime
= format
|| this._defaults
.timeFormat
.toString();
669 var hour12
= ((time
.ampm
== 'AM') ? (time
.hour
) : (time
.hour
% 12));
670 hour12
= (Number(hour12
) === 0) ? 12 : hour12
;
671 tmptime
= tmptime
.toString()
672 .replace(/hh/g, ((hour12
< 10) ? '0' : '') + hour12
)
673 .replace(/h
/g
, hour12
)
674 .replace(/mm/g, ((time
.minute
< 10) ? '0' : '') + time
.minute
)
675 .replace(/m
/g
, time
.minute
)
676 .replace(/ss/g, ((time
.second
< 10) ? '0' : '') + time
.second
)
677 .replace(/s
/g
, time
.second
)
678 .replace(/TT/g, time
.ampm
.toUpperCase())
679 .replace(/Tt/g, time
.ampm
.toUpperCase())
680 .replace(/tT/g, time
.ampm
.toLowerCase())
681 .replace(/tt/g, time
.ampm
.toLowerCase())
682 .replace(/T
/g
, time
.ampm
.charAt(0).toUpperCase())
683 .replace(/t
/g
, time
.ampm
.charAt(0).toLowerCase())
684 .replace(/z
/g
, time
.timezone
);
686 tmptime
= tmptime
.toString()
687 .replace(/hh/g, ((time
.hour
< 10) ? '0' : '') + time
.hour
)
688 .replace(/h
/g
, time
.hour
)
689 .replace(/mm/g, ((time
.minute
< 10) ? '0' : '') + time
.minute
)
690 .replace(/m
/g
, time
.minute
)
691 .replace(/ss/g, ((time
.second
< 10) ? '0' : '') + time
.second
)
692 .replace(/s
/g
, time
.second
)
693 .replace(/z
/g
, time
.timezone
);
694 tmptime
= $.trim(tmptime
.replace(/t
/gi
, ''));
697 if (arguments
.length
) return tmptime
;
698 else this.formattedTime
= tmptime
;
701 //########################################################################
702 // update our input with the new date time..
703 //########################################################################
704 _updateDateTime: function(dp_inst
) {
705 dp_inst
= this.inst
|| dp_inst
,
706 dt
= new Date(dp_inst
.selectedYear
, dp_inst
.selectedMonth
, dp_inst
.selectedDay
),
707 dateFmt
= $.datepicker
._get(dp_inst
, 'dateFormat'),
708 formatCfg
= $.datepicker
._getFormatConfig(dp_inst
),
709 timeAvailable
= dt
!== null && this.timeDefined
;
710 this.formattedDate
= $.datepicker
.formatDate(dateFmt
, (dt
=== null ? new Date() : dt
), formatCfg
);
711 var formattedDateTime
= this.formattedDate
;
712 if (dp_inst
.lastVal
!== undefined && (dp_inst
.lastVal
.length
> 0 && this.$input
.val().length
=== 0))
715 if (this._defaults
.timeOnly
=== true) {
716 formattedDateTime
= this.formattedTime
;
717 } else if (this._defaults
.timeOnly
!== true && (this._defaults
.alwaysSetTime
|| timeAvailable
)) {
718 formattedDateTime
+= this._defaults
.separator
+ this.formattedTime
+ this._defaults
.timeSuffix
;
721 this.formattedDateTime
= formattedDateTime
;
723 if(!this._defaults
.showTimepicker
) {
724 this.$input
.val(this.formattedDate
);
725 } else if (this.$altInput
&& this._defaults
.altFieldTimeOnly
=== true) {
726 this.$altInput
.val(this.formattedTime
);
727 this.$input
.val(this.formattedDate
);
728 } else if(this.$altInput
) {
729 this.$altInput
.val(formattedDateTime
);
730 this.$input
.val(formattedDateTime
);
732 this.$input
.val(formattedDateTime
);
735 this.$input
.trigger("change");
741 //########################################################################
742 // shorthand just to use timepicker..
743 //########################################################################
744 timepicker: function(o
) {
746 var tmp_args
= arguments
;
748 if (typeof o
== 'object') tmp_args
[0] = $.extend(o
, { timeOnly
: true });
750 return $(this).each(function() {
751 $.fn
.datetimepicker
.apply($(this), tmp_args
);
755 //########################################################################
756 // extend timepicker to datepicker
757 //########################################################################
758 datetimepicker: function(o
) {
761 tmp_args
= arguments
;
763 if (typeof(o
) == 'string'){
765 return $.fn
.datepicker
.apply($(this[0]), tmp_args
);
767 return this.each(function() {
769 $t
.datepicker
.apply($t
, tmp_args
);
773 return this.each(function() {
775 $t
.datepicker($.timepicker
._newInst($t
, o
)._defaults
);
780 //########################################################################
781 // the bad hack :/ override datepicker so it doesnt close on select
782 // inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378
783 //########################################################################
784 $.datepicker
._base_selectDate
= $.datepicker
._selectDate
;
785 $.datepicker
._selectDate = function (id
, dateStr
) {
786 var inst
= this._getInst($(id
)[0]),
787 tp_inst
= this._get(inst
, 'timepicker');
790 tp_inst
._limitMinMaxDateTime(inst
, true);
791 inst
.inline
= inst
.stay_open
= true;
792 //This way the onSelect handler called from calendarpicker get the full dateTime
793 this._base_selectDate(id
, dateStr
+ tp_inst
._defaults
.separator
+ tp_inst
.formattedTime
+ tp_inst
._defaults
.timeSuffix
);
794 inst
.inline
= inst
.stay_open
= false;
795 this._notifyChange(inst
);
796 this._updateDatepicker(inst
);
798 else this._base_selectDate(id
, dateStr
);
801 //#############################################################################################
802 // second bad hack :/ override datepicker so it triggers an event when changing the input field
803 // and does not redraw the datepicker on every selectDate event
804 //#############################################################################################
805 $.datepicker
._base_updateDatepicker
= $.datepicker
._updateDatepicker
;
806 $.datepicker
._updateDatepicker = function(inst
) {
808 // don't popup the datepicker if there is another instance already opened
809 var input
= inst
.input
[0];
810 if($.datepicker
._curInst
&&
811 $.datepicker
._curInst
!= inst
&&
812 $.datepicker
._datepickerShowing
&&
813 $.datepicker
._lastInput
!= input
) {
817 if (typeof(inst
.stay_open
) !== 'boolean' || inst
.stay_open
=== false) {
819 this._base_updateDatepicker(inst
);
821 // Reload the time control when changing something in the input text field.
822 var tp_inst
= this._get(inst
, 'timepicker');
823 if(tp_inst
) tp_inst
._addTimePicker(inst
);
827 //#######################################################################################
828 // third bad hack :/ override datepicker so it allows spaces and colon in the input field
829 //#######################################################################################
830 $.datepicker
._base_doKeyPress
= $.datepicker
._doKeyPress
;
831 $.datepicker
._doKeyPress = function(event
) {
832 var inst
= $.datepicker
._getInst(event
.target
),
833 tp_inst
= $.datepicker
._get(inst
, 'timepicker');
836 if ($.datepicker
._get(inst
, 'constrainInput')) {
837 var ampm
= tp_inst
._defaults
.ampm
,
838 dateChars
= $.datepicker
._possibleChars($.datepicker
._get(inst
, 'dateFormat')),
839 datetimeChars
= tp_inst
._defaults
.timeFormat
.toString()
840 .replace(/[hms]/g, '')
841 .replace(/TT/g, ampm
? 'APM' : '')
842 .replace(/Tt/g, ampm
? 'AaPpMm' : '')
843 .replace(/tT/g, ampm
? 'AaPpMm' : '')
844 .replace(/T
/g
, ampm
? 'AP' : '')
845 .replace(/tt/g, ampm
? 'apm' : '')
846 .replace(/t
/g
, ampm
? 'ap' : '') +
848 tp_inst
._defaults
.separator
+
849 tp_inst
._defaults
.timeSuffix
+
850 (tp_inst
._defaults
.showTimezone
? tp_inst
._defaults
.timezoneList
.join('') : '') +
852 chr
= String
.fromCharCode(event
.charCode
=== undefined ? event
.keyCode
: event
.charCode
);
853 return event
.ctrlKey
|| (chr
< ' ' || !dateChars
|| datetimeChars
.indexOf(chr
) > -1);
857 return $.datepicker
._base_doKeyPress(event
);
860 //#######################################################################################
861 // Override key up event to sync manual input changes.
862 //#######################################################################################
863 $.datepicker
._base_doKeyUp
= $.datepicker
._doKeyUp
;
864 $.datepicker
._doKeyUp = function (event
) {
865 var inst
= $.datepicker
._getInst(event
.target
),
866 tp_inst
= $.datepicker
._get(inst
, 'timepicker');
869 if (tp_inst
._defaults
.timeOnly
&& (inst
.input
.val() != inst
.lastVal
)) {
871 $.datepicker
._updateDatepicker(inst
);
874 $.datepicker
.log(err
);
879 return $.datepicker
._base_doKeyUp(event
);
882 //#######################################################################################
883 // override "Today" button to also grab the time.
884 //#######################################################################################
885 $.datepicker
._base_gotoToday
= $.datepicker
._gotoToday
;
886 $.datepicker
._gotoToday = function(id
) {
887 this._base_gotoToday(id
);
888 this._setTime(this._getInst($(id
)[0]), new Date());
891 //#######################################################################################
892 // Disable & enable the Time in the datetimepicker
893 //#######################################################################################
894 $.datepicker
._disableTimepickerDatepicker = function(target
, date
, withDate
) {
895 var inst
= this._getInst(target
),
896 tp_inst
= this._get(inst
, 'timepicker');
897 $(target
).datepicker('getDate'); // Init selected[Year|Month|Day]
899 tp_inst
._defaults
.showTimepicker
= false;
900 tp_inst
._updateDateTime(inst
);
904 $.datepicker
._enableTimepickerDatepicker = function(target
, date
, withDate
) {
905 var inst
= this._getInst(target
),
906 tp_inst
= this._get(inst
, 'timepicker');
907 $(target
).datepicker('getDate'); // Init selected[Year|Month|Day]
909 tp_inst
._defaults
.showTimepicker
= true;
910 tp_inst
._addTimePicker(inst
); // Could be disabled on page load
911 tp_inst
._updateDateTime(inst
);
915 //#######################################################################################
916 // Create our own set time function
917 //#######################################################################################
918 $.datepicker
._setTime = function(inst
, date
) {
919 var tp_inst
= this._get(inst
, 'timepicker');
921 var defaults
= tp_inst
._defaults
,
922 // calling _setTime with no date sets time to defaults
923 hour
= date
? date
.getHours() : defaults
.hour
,
924 minute
= date
? date
.getMinutes() : defaults
.minute
,
925 second
= date
? date
.getSeconds() : defaults
.second
;
927 //check if within min/max times..
928 if ((hour
< defaults
.hourMin
|| hour
> defaults
.hourMax
) || (minute
< defaults
.minuteMin
|| minute
> defaults
.minuteMax
) || (second
< defaults
.secondMin
|| second
> defaults
.secondMax
)) {
929 hour
= defaults
.hourMin
;
930 minute
= defaults
.minuteMin
;
931 second
= defaults
.secondMin
;
935 tp_inst
.minute
= minute
;
936 tp_inst
.second
= second
;
938 if (tp_inst
.hour_slider
) tp_inst
.hour_slider
.slider('value', hour
);
939 if (tp_inst
.minute_slider
) tp_inst
.minute_slider
.slider('value', minute
);
940 if (tp_inst
.second_slider
) tp_inst
.second_slider
.slider('value', second
);
942 tp_inst
._onTimeChange();
943 tp_inst
._updateDateTime(inst
);
947 //#######################################################################################
948 // Create new public method to set only time, callable as $().datepicker('setTime', date)
949 //#######################################################################################
950 $.datepicker
._setTimeDatepicker = function(target
, date
, withDate
) {
951 var inst
= this._getInst(target
),
952 tp_inst
= this._get(inst
, 'timepicker');
955 this._setDateFromField(inst
);
958 if (typeof date
== "string") {
959 tp_inst
._parseTime(date
, withDate
);
960 tp_date
= new Date();
961 tp_date
.setHours(tp_inst
.hour
, tp_inst
.minute
, tp_inst
.second
);
963 else tp_date
= new Date(date
.getTime());
964 if (tp_date
.toString() == 'Invalid Date') tp_date
= undefined;
965 this._setTime(inst
, tp_date
);
971 //#######################################################################################
972 // override setDate() to allow setting time too within Date object
973 //#######################################################################################
974 $.datepicker
._base_setDateDatepicker
= $.datepicker
._setDateDatepicker
;
975 $.datepicker
._setDateDatepicker = function(target
, date
) {
976 var inst
= this._getInst(target
),
977 tp_date
= (date
instanceof Date
) ? new Date(date
.getTime()) : date
;
979 this._updateDatepicker(inst
);
980 this._base_setDateDatepicker
.apply(this, arguments
);
981 this._setTimeDatepicker(target
, tp_date
, true);
984 //#######################################################################################
985 // override getDate() to allow getting time too within Date object
986 //#######################################################################################
987 $.datepicker
._base_getDateDatepicker
= $.datepicker
._getDateDatepicker
;
988 $.datepicker
._getDateDatepicker = function(target
, noDefault
) {
989 var inst
= this._getInst(target
),
990 tp_inst
= this._get(inst
, 'timepicker');
993 this._setDateFromField(inst
, noDefault
);
994 var date
= this._getDate(inst
);
995 if (date
&& tp_inst
._parseTime($(target
).val(), tp_inst
.timeOnly
)) date
.setHours(tp_inst
.hour
, tp_inst
.minute
, tp_inst
.second
);
998 return this._base_getDateDatepicker(target
, noDefault
);
1001 //#######################################################################################
1002 // override parseDate() because UI 1.8.14 throws an error about "Extra characters"
1003 // An option in datapicker to ignore extra format characters would be nicer.
1004 //#######################################################################################
1005 $.datepicker
._base_parseDate
= $.datepicker
.parseDate
;
1006 $.datepicker
.parseDate = function(format
, value
, settings
) {
1009 date
= this._base_parseDate(format
, value
, settings
);
1011 // Hack! The error message ends with a colon, a space, and
1012 // the "extra" characters. We rely on that instead of
1013 // attempting to perfectly reproduce the parsing algorithm.
1014 date
= this._base_parseDate(format
, value
.substring(0,value
.length
-(err
.length
-err
.indexOf(':')-2)), settings
);
1019 //#######################################################################################
1020 // override options setter to add time to maxDate(Time) and minDate(Time)
1021 //#######################################################################################
1022 $.datepicker
._base_optionDatepicker
= $.datepicker
._optionDatepicker
;
1023 $.datepicker
._optionDatepicker = function(target
, name
, value
) {
1024 this._base_optionDatepicker(target
, name
, value
);
1025 var inst
= this._getInst(target
),
1026 tp_inst
= this._get(inst
, 'timepicker');
1028 //Set minimum and maximum date values if we have timepicker
1029 if(name
==='minDate') {
1030 if(tp_inst
._defaults
.minDate
!== undefined && tp_inst
._defaults
.minDate
instanceof Date
)
1031 tp_inst
._defaults
.minDateTime
= new Date(value
);
1032 if(tp_inst
._defaults
.minDateTime
!== undefined && tp_inst
._defaults
.minDateTime
instanceof Date
)
1033 tp_inst
._defaults
.minDate
= new Date(tp_inst
._defaults
.minDateTime
.getTime());
1034 tp_inst
._limitMinMaxDateTime(inst
,true);
1036 if(name
==='maxDate') {
1037 if(tp_inst
._defaults
.maxDate
!== undefined && tp_inst
._defaults
.maxDate
instanceof Date
)
1038 tp_inst
._defaults
.maxDateTime
= new Date(value
);
1039 if(tp_inst
._defaults
.maxDateTime
!== undefined && tp_inst
._defaults
.maxDateTime
instanceof Date
)
1040 tp_inst
._defaults
.maxDate
= new Date(tp_inst
._defaults
.maxDateTime
.getTime());
1041 tp_inst
._limitMinMaxDateTime(inst
,true);
1046 //#######################################################################################
1047 // jQuery extend now ignores nulls!
1048 //#######################################################################################
1049 function extendRemove(target
, props
) {
1050 $.extend(target
, props
);
1051 for (var name
in props
)
1052 if (props
[name
] === null || props
[name
] === undefined)
1053 target
[name
] = props
[name
];
1057 $.timepicker
= new Timepicker(); // singleton instance
1058 $.timepicker
.version
= "0.9.6";