jquery-timepicker-rails 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  module Jquery
2
2
  module Timepicker
3
3
  module Rails
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
6
6
  end
7
7
  end
@@ -1,675 +1,687 @@
1
- /************************
2
- jquery-timepicker
3
- http://jonthornton.github.com/jquery-timepicker/
4
-
5
- requires jQuery 1.7+
6
- ************************/
7
-
8
-
9
- (function (factory) {
10
- if (typeof define === 'function' && define.amd) {
11
- // AMD. Register as an anonymous module.
12
- define(['jquery'], factory);
13
- } else {
14
- // Browser globals
15
- factory(jQuery);
16
- }
17
- }(function ($) {
18
- var _baseDate = _generateBaseDate();
19
- var _ONE_DAY = 86400;
20
- var _closeEvent = 'ontouchstart' in document ? 'touchstart' : 'mousedown';
21
- var _defaults = {
22
- className: null,
23
- minTime: null,
24
- maxTime: null,
25
- durationTime: null,
26
- step: 30,
27
- showDuration: false,
28
- timeFormat: 'g:ia',
29
- scrollDefaultNow: false,
30
- scrollDefaultTime: false,
31
- selectOnBlur: false,
32
- forceRoundTime: false,
33
- appendTo: 'body'
34
- };
35
- var _lang = {
36
- decimal: '.',
37
- mins: 'mins',
38
- hr: 'hr',
39
- hrs: 'hrs'
40
- };
41
- var globalInit = false;
42
-
43
- var methods =
44
- {
45
- init: function(options)
46
- {
47
- return this.each(function()
48
- {
49
- var self = $(this);
50
-
51
- // convert dropdowns to text input
52
- if (self[0].tagName == 'SELECT') {
53
- var input = $('<input />');
54
- var attrs = { 'type': 'text', 'value': self.val() };
55
- var raw_attrs = self[0].attributes;
56
-
57
- for (var i=0; i < raw_attrs.length; i++) {
58
- attrs[raw_attrs[i].nodeName] = raw_attrs[i].nodeValue;
59
- }
60
-
61
- input.attr(attrs);
62
- self.replaceWith(input);
63
- self = input;
64
- }
65
-
66
- var settings = $.extend({}, _defaults);
67
-
68
- if (options) {
69
- settings = $.extend(settings, options);
70
- }
71
-
72
- if (settings.minTime) {
73
- settings.minTime = _time2int(settings.minTime);
74
- }
75
-
76
- if (settings.maxTime) {
77
- settings.maxTime = _time2int(settings.maxTime);
78
- }
79
-
80
- if (settings.durationTime) {
81
- settings.durationTime = _time2int(settings.durationTime);
82
- }
83
-
84
- if (settings.lang) {
85
- _lang = $.extend(_lang, settings.lang);
86
- }
87
-
88
- self.data('timepicker-settings', settings);
89
- self.attr('autocomplete', 'off');
90
- self.on('click.timepicker focus.timepicker', methods.show);
91
- self.on('blur.timepicker', _formatValue);
92
- self.on('keydown.timepicker', _keyhandler);
93
- self.addClass('ui-timepicker-input');
94
-
95
- _formatValue.call(self.get(0));
96
-
97
- if (!globalInit) {
98
- // close the dropdown when container loses focus
99
- $('body').on(_closeEvent, function(e) {
100
- var target = $(e.target);
101
- var input = target.closest('.ui-timepicker-input');
102
- if (input.length === 0 && target.closest('.ui-timepicker-list').length === 0) {
103
- methods.hide();
104
- }
105
- });
106
- globalInit = true;
107
- }
108
- });
109
- },
110
-
111
- show: function(e)
112
- {
113
- var self = $(this);
114
-
115
- if ('ontouchstart' in document) {
116
- // block the keyboard on mobile devices
117
- self.blur();
118
- }
119
-
120
- var list = self.data('timepicker-list');
121
-
122
- // check if input is readonly
123
- if (self.attr('readonly')) {
124
- return;
125
- }
126
-
127
- // check if list needs to be rendered
128
- if (!list || list.length === 0) {
129
- _render(self);
130
- list = self.data('timepicker-list');
131
- }
132
-
133
- // check if a flag was set to close this picker
134
- if (self.hasClass('ui-timepicker-hideme')) {
135
- self.removeClass('ui-timepicker-hideme');
136
- list.hide();
137
- return;
138
- }
139
-
140
- if (list.is(':visible')) {
141
- return;
142
- }
143
-
144
- // make sure other pickers are hidden
145
- methods.hide();
146
-
147
- if ((self.offset().top + self.outerHeight(true) + list.outerHeight()) > $(window).height() + $(window).scrollTop()) {
148
- // position the dropdown on top
149
- list.css({ 'left':(self.offset().left), 'top': self.offset().top - list.outerHeight() });
150
- } else {
151
- // put it under the input
152
- list.css({ 'left':(self.offset().left), 'top': self.offset().top + self.outerHeight() });
153
- }
154
-
155
- list.show();
156
-
157
- var settings = self.data('timepicker-settings');
158
- // position scrolling
159
- var selected = list.find('.ui-timepicker-selected');
160
-
161
- if (!selected.length) {
162
- if (self.val()) {
163
- selected = _findRow(self, list, _time2int(self.val()));
164
- } else if (settings.scrollDefaultNow) {
165
- selected = _findRow(self, list, _time2int(new Date()));
166
- } else if (settings.scrollDefaultTime !== false) {
167
- selected = _findRow(self, list, _time2int(settings.scrollDefaultTime));
168
- }
169
- }
170
-
171
- if (selected && selected.length) {
172
- var topOffset = list.scrollTop() + selected.position().top - selected.outerHeight();
173
- list.scrollTop(topOffset);
174
- } else {
175
- list.scrollTop(0);
176
- }
177
-
178
- self.trigger('showTimepicker');
179
- },
180
-
181
- hide: function(e)
182
- {
183
- $('.ui-timepicker-list:visible').each(function() {
184
- var list = $(this);
185
- var self = list.data('timepicker-input');
186
- var settings = self.data('timepicker-settings');
187
-
188
- if (settings && settings.selectOnBlur) {
189
- _selectValue(self);
190
- }
191
-
192
- list.hide();
193
- self.trigger('hideTimepicker');
194
- });
195
- },
196
-
197
- option: function(key, value)
198
- {
199
- var self = $(this);
200
- var settings = self.data('timepicker-settings');
201
- var list = self.data('timepicker-list');
202
-
203
- if (typeof key == 'object') {
204
- settings = $.extend(settings, key);
205
-
206
- } else if (typeof key == 'string' && typeof value != 'undefined') {
207
- settings[key] = value;
208
-
209
- } else if (typeof key == 'string') {
210
- return settings[key];
211
- }
212
-
213
- if (settings.minTime) {
214
- settings.minTime = _time2int(settings.minTime);
215
- }
216
-
217
- if (settings.maxTime) {
218
- settings.maxTime = _time2int(settings.maxTime);
219
- }
220
-
221
- if (settings.durationTime) {
222
- settings.durationTime = _time2int(settings.durationTime);
223
- }
224
-
225
- self.data('timepicker-settings', settings);
226
-
227
- if (list) {
228
- list.remove();
229
- self.data('timepicker-list', false);
230
- }
231
-
232
- },
233
-
234
- getSecondsFromMidnight: function()
235
- {
236
- return _time2int($(this).val());
237
- },
238
-
239
- getTime: function()
240
- {
241
- return new Date(_baseDate.valueOf() + (_time2int($(this).val())*1000));
242
- },
243
-
244
- setTime: function(value)
245
- {
246
- var self = $(this);
247
- var prettyTime = _int2time(_time2int(value), self.data('timepicker-settings').timeFormat);
248
- self.val(prettyTime);
249
- },
250
-
251
- remove: function()
252
- {
253
- var self = $(this);
254
-
255
- // check if this element is a timepicker
256
- if (!self.hasClass('ui-timepicker-input')) {
257
- return;
258
- }
259
-
260
- self.removeAttr('autocomplete', 'off');
261
- self.removeClass('ui-timepicker-input');
262
- self.removeData('timepicker-settings');
263
- self.off('.timepicker');
264
-
265
- // timepicker-list won't be present unless the user has interacted with this timepicker
266
- if (self.data('timepicker-list')) {
267
- self.data('timepicker-list').remove();
268
- }
269
-
270
- self.removeData('timepicker-list');
271
- }
272
- };
273
-
274
- // private methods
275
-
276
- function _render(self)
277
- {
278
- var settings = self.data('timepicker-settings');
279
- var list = self.data('timepicker-list');
280
-
281
- if (list && list.length) {
282
- list.remove();
283
- self.data('timepicker-list', false);
284
- }
285
-
286
- list = $('<ul />');
287
- list.attr('tabindex', -1);
288
- list.addClass('ui-timepicker-list');
289
- if (settings.className) {
290
- list.addClass(settings.className);
291
- }
292
-
293
- list.css({'display':'none', 'position': 'absolute' });
294
-
295
- if ((settings.minTime !== null || settings.durationTime !== null) && settings.showDuration) {
296
- list.addClass('ui-timepicker-with-duration');
297
- }
298
-
299
- var durStart = (settings.durationTime !== null) ? settings.durationTime : settings.minTime;
300
- var start = (settings.minTime !== null) ? settings.minTime : 0;
301
- var end = (settings.maxTime !== null) ? settings.maxTime : (start + _ONE_DAY - 1);
302
-
303
- if (end <= start) {
304
- // make sure the end time is greater than start time, otherwise there will be no list to show
305
- end += _ONE_DAY;
306
- }
307
-
308
- for (var i=start; i <= end; i += settings.step*60) {
309
- var timeInt = i%_ONE_DAY;
310
- var row = $('<li />');
311
- row.data('time', timeInt);
312
- row.text(_int2time(timeInt, settings.timeFormat));
313
-
314
- if ((settings.minTime !== null || settings.durationTime !== null) && settings.showDuration) {
315
- var duration = $('<span />');
316
- duration.addClass('ui-timepicker-duration');
317
- duration.text(' ('+_int2duration(i - durStart)+')');
318
- row.append(duration);
319
- }
320
-
321
- list.append(row);
322
- }
323
-
324
- list.data('timepicker-input', self);
325
- self.data('timepicker-list', list);
326
-
327
- var appendTo = settings.appendTo;
328
- if (typeof appendTo === 'string') {
329
- appendTo = $(appendTo);
330
- } else if (typeof appendTo === 'function') {
331
- appendTo = appendTo(self);
332
- }
333
- appendTo.append(list);
334
- _setSelected(self, list);
335
-
336
- list.on('click', 'li', function(e) {
337
- self.addClass('ui-timepicker-hideme');
338
- self[0].focus();
339
-
340
- // make sure only the clicked row is selected
341
- list.find('li').removeClass('ui-timepicker-selected');
342
- $(this).addClass('ui-timepicker-selected');
343
-
344
- _selectValue(self);
345
- list.hide();
346
- });
347
- }
348
-
349
- function _generateBaseDate()
350
- {
351
- var _baseDate = new Date();
352
- var _currentTimezoneOffset = _baseDate.getTimezoneOffset()*60000;
353
- _baseDate.setHours(0); _baseDate.setMinutes(0); _baseDate.setSeconds(0);
354
- var _baseDateTimezoneOffset = _baseDate.getTimezoneOffset()*60000;
355
-
356
- return new Date(_baseDate.valueOf() - _baseDateTimezoneOffset + _currentTimezoneOffset);
357
- }
358
-
359
- function _findRow(self, list, value)
360
- {
361
- if (!value && value !== 0) {
362
- return false;
363
- }
364
-
365
- var settings = self.data('timepicker-settings');
366
- var out = false;
367
- var halfStep = settings.step*30;
368
-
369
- // loop through the menu items
370
- list.find('li').each(function(i, obj) {
371
- var jObj = $(obj);
372
-
373
- var offset = jObj.data('time') - value;
374
-
375
- // check if the value is less than half a step from each row
376
- if (Math.abs(offset) < halfStep || offset == halfStep) {
377
- out = jObj;
378
- return false;
379
- }
380
- });
381
-
382
- return out;
383
- }
384
-
385
- function _setSelected(self, list)
386
- {
387
- var timeValue = _time2int(self.val());
388
-
389
- var selected = _findRow(self, list, timeValue);
390
- if (selected) selected.addClass('ui-timepicker-selected');
391
- }
392
-
393
-
394
- function _formatValue()
395
- {
396
- if (this.value === '') {
397
- return;
398
- }
399
-
400
- var self = $(this);
401
- var seconds = _time2int(this.value);
402
-
403
- if (seconds === null) {
404
- self.trigger('timeFormatError');
405
- return;
406
- }
407
-
408
- var settings = self.data('timepicker-settings');
409
-
410
- if (settings.forceRoundTime) {
411
- var offset = seconds % (settings.step*60); // step is in minutes
412
-
413
- if (offset >= settings.step*30) {
414
- // if offset is larger than a half step, round up
415
- seconds += (settings.step*60) - offset;
416
- } else {
417
- // round down
418
- seconds -= offset;
419
- }
420
- }
421
-
422
- var prettyTime = _int2time(seconds, settings.timeFormat);
423
- self.val(prettyTime);
424
- }
425
-
426
- function _keyhandler(e)
427
- {
428
- var self = $(this);
429
- var list = self.data('timepicker-list');
430
-
431
- if (!list.is(':visible')) {
432
- if (e.keyCode == 40) {
433
- self.focus();
434
- } else {
435
- return true;
436
- }
437
- }
438
-
439
- switch (e.keyCode) {
440
-
441
- case 13: // return
442
- _selectValue(self);
443
- methods.hide.apply(this);
444
- e.preventDefault();
445
- return false;
446
-
447
- case 38: // up
448
- var selected = list.find('.ui-timepicker-selected');
449
-
450
- if (!selected.length) {
451
- list.children().each(function(i, obj) {
452
- if ($(obj).position().top > 0) {
453
- selected = $(obj);
454
- return false;
455
- }
456
- });
457
- selected.addClass('ui-timepicker-selected');
458
-
459
- } else if (!selected.is(':first-child')) {
460
- selected.removeClass('ui-timepicker-selected');
461
- selected.prev().addClass('ui-timepicker-selected');
462
-
463
- if (selected.prev().position().top < selected.outerHeight()) {
464
- list.scrollTop(list.scrollTop() - selected.outerHeight());
465
- }
466
- }
467
-
468
- break;
469
-
470
- case 40: // down
471
- selected = list.find('.ui-timepicker-selected');
472
-
473
- if (selected.length === 0) {
474
- list.children().each(function(i, obj) {
475
- if ($(obj).position().top > 0) {
476
- selected = $(obj);
477
- return false;
478
- }
479
- });
480
-
481
- selected.addClass('ui-timepicker-selected');
482
- } else if (!selected.is(':last-child')) {
483
- selected.removeClass('ui-timepicker-selected');
484
- selected.next().addClass('ui-timepicker-selected');
485
-
486
- if (selected.next().position().top + 2*selected.outerHeight() > list.outerHeight()) {
487
- list.scrollTop(list.scrollTop() + selected.outerHeight());
488
- }
489
- }
490
-
491
- break;
492
-
493
- case 27: // escape
494
- list.find('li').removeClass('ui-timepicker-selected');
495
- list.hide();
496
- break;
497
-
498
- case 9: //tab
499
- methods.hide();
500
- break;
501
-
502
- case 16:
503
- case 17:
504
- case 18:
505
- case 19:
506
- case 20:
507
- case 33:
508
- case 34:
509
- case 35:
510
- case 36:
511
- case 37:
512
- case 39:
513
- case 45:
514
- return;
515
-
516
- default:
517
- list.find('li').removeClass('ui-timepicker-selected');
518
- return;
519
- }
520
- }
521
-
522
- function _selectValue(self)
523
- {
524
- var settings = self.data('timepicker-settings');
525
- var list = self.data('timepicker-list');
526
- var timeValue = null;
527
-
528
- var cursor = list.find('.ui-timepicker-selected');
529
-
530
- if (cursor.length) {
531
- // selected value found
532
- timeValue = cursor.data('time');
533
-
534
- } else if (self.val()) {
535
-
536
- // no selected value; fall back on input value
537
- timeValue = _time2int(self.val());
538
-
539
- _setSelected(self, list);
540
- }
541
-
542
- if (timeValue !== null) {
543
- var timeString = _int2time(timeValue, settings.timeFormat);
544
- self.attr('value', timeString);
545
- }
546
-
547
- self.trigger('change').trigger('changeTime');
548
- }
549
-
550
- function _int2duration(seconds)
551
- {
552
- var minutes = Math.round(seconds/60);
553
- var duration;
554
-
555
- if (Math.abs(minutes) < 60) {
556
- duration = [minutes, _lang.mins];
557
- } else if (minutes == 60) {
558
- duration = ['1', _lang.hr];
559
- } else {
560
- var hours = (minutes/60).toFixed(1);
561
- if (_lang.decimal != '.') hours = hours.replace('.', _lang.decimal);
562
- duration = [hours, _lang.hrs];
563
- }
564
-
565
- return duration.join(' ');
566
- }
567
-
568
- function _int2time(seconds, format)
569
- {
570
- if (seconds === null) {
571
- return;
572
- }
573
-
574
- var time = new Date(_baseDate.valueOf() + (seconds*1000));
575
- var output = '';
576
- var hour, code;
577
-
578
- for (var i=0; i<format.length; i++) {
579
-
580
- code = format.charAt(i);
581
- switch (code) {
582
-
583
- case 'a':
584
- output += (time.getHours() > 11) ? 'pm' : 'am';
585
- break;
586
-
587
- case 'A':
588
- output += (time.getHours() > 11) ? 'PM' : 'AM';
589
- break;
590
-
591
- case 'g':
592
- hour = time.getHours() % 12;
593
- output += (hour === 0) ? '12' : hour;
594
- break;
595
-
596
- case 'G':
597
- output += time.getHours();
598
- break;
599
-
600
- case 'h':
601
- hour = time.getHours() % 12;
602
-
603
- if (hour !== 0 && hour < 10) {
604
- hour = '0'+hour;
605
- }
606
-
607
- output += (hour === 0) ? '12' : hour;
608
- break;
609
-
610
- case 'H':
611
- hour = time.getHours();
612
- output += (hour > 9) ? hour : '0'+hour;
613
- break;
614
-
615
- case 'i':
616
- var minutes = time.getMinutes();
617
- output += (minutes > 9) ? minutes : '0'+minutes;
618
- break;
619
-
620
- case 's':
621
- seconds = time.getSeconds();
622
- output += (seconds > 9) ? seconds : '0'+seconds;
623
- break;
624
-
625
- default:
626
- output += code;
627
- }
628
- }
629
-
630
- return output;
631
- }
632
-
633
- function _time2int(timeString)
634
- {
635
- if (timeString === '') return null;
636
- if (timeString+0 == timeString) return timeString;
637
-
638
- if (typeof(timeString) == 'object') {
639
- timeString = timeString.getHours()+':'+timeString.getMinutes()+':'+timeString.getSeconds();
640
- }
641
-
642
- var d = new Date(0);
643
- var time = timeString.toLowerCase().match(/(\d{1,2})(?::(\d{1,2}))?(?::(\d{2}))?\s*([pa]?)/);
644
-
645
- if (!time) {
646
- return null;
647
- }
648
-
649
- var hour = parseInt(time[1]*1, 10);
650
- var hours;
651
-
652
- if (time[4]) {
653
- if (hour == 12) {
654
- hours = (time[4] == 'p') ? 12 : 0;
655
- } else {
656
- hours = (hour + (time[4] == 'p' ? 12 : 0));
657
- }
658
-
659
- } else {
660
- hours = hour;
661
- }
662
-
663
- var minutes = ( time[2]*1 || 0 );
664
- var seconds = ( time[3]*1 || 0 );
665
- return hours*3600 + minutes*60 + seconds;
666
- }
667
-
668
- // Plugin entry
669
- $.fn.timepicker = function(method)
670
- {
671
- if(methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); }
672
- else if(typeof method === "object" || !method) { return methods.init.apply(this, arguments); }
673
- else { $.error("Method "+ method + " does not exist on jQuery.timepicker"); }
674
- };
675
- }));
1
+ /************************
2
+ jquery-timepicker
3
+ http://jonthornton.github.com/jquery-timepicker/
4
+
5
+ requires jQuery 1.7+
6
+ ************************/
7
+
8
+
9
+ (function (factory) {
10
+ if (typeof define === 'function' && define.amd) {
11
+ // AMD. Register as an anonymous module.
12
+ define(['jquery'], factory);
13
+ } else {
14
+ // Browser globals
15
+ factory(jQuery);
16
+ }
17
+ }(function ($) {
18
+ var _baseDate = _generateBaseDate();
19
+ var _ONE_DAY = 86400;
20
+ var _defaults = {
21
+ className: null,
22
+ minTime: null,
23
+ maxTime: null,
24
+ durationTime: null,
25
+ step: 30,
26
+ showDuration: false,
27
+ timeFormat: 'g:ia',
28
+ scrollDefaultNow: false,
29
+ scrollDefaultTime: false,
30
+ selectOnBlur: false,
31
+ forceRoundTime: false,
32
+ appendTo: 'body'
33
+ };
34
+ var _lang = {
35
+ decimal: '.',
36
+ mins: 'mins',
37
+ hr: 'hr',
38
+ hrs: 'hrs'
39
+ };
40
+
41
+ var methods =
42
+ {
43
+ init: function(options)
44
+ {
45
+ return this.each(function()
46
+ {
47
+ var self = $(this);
48
+
49
+ // convert dropdowns to text input
50
+ if (self[0].tagName == 'SELECT') {
51
+ var attrs = { 'type': 'text', 'value': self.val() };
52
+ var raw_attrs = self[0].attributes;
53
+
54
+ for (var i=0; i < raw_attrs.length; i++) {
55
+ attrs[raw_attrs[i].nodeName] = raw_attrs[i].nodeValue;
56
+ }
57
+
58
+ var input = $('<input />', attrs);
59
+ self.replaceWith(input);
60
+ self = input;
61
+ }
62
+
63
+ var settings = $.extend({}, _defaults);
64
+
65
+ if (options) {
66
+ settings = $.extend(settings, options);
67
+ }
68
+
69
+ if (settings.minTime) {
70
+ settings.minTime = _time2int(settings.minTime);
71
+ }
72
+
73
+ if (settings.maxTime) {
74
+ settings.maxTime = _time2int(settings.maxTime);
75
+ }
76
+
77
+ if (settings.durationTime) {
78
+ settings.durationTime = _time2int(settings.durationTime);
79
+ }
80
+
81
+ if (settings.lang) {
82
+ _lang = $.extend(_lang, settings.lang);
83
+ }
84
+
85
+ self.data('timepicker-settings', settings);
86
+ self.prop('autocomplete', 'off');
87
+ self.on('click.timepicker focus.timepicker', methods.show);
88
+ self.on('blur.timepicker', _formatValue);
89
+ self.on('keydown.timepicker', _keyhandler);
90
+ self.addClass('ui-timepicker-input');
91
+
92
+ _formatValue.call(self.get(0));
93
+ });
94
+ },
95
+
96
+ show: function(e)
97
+ {
98
+ var self = $(this);
99
+
100
+ if ('ontouchstart' in document) {
101
+ // block the keyboard on mobile devices
102
+ self.blur();
103
+ }
104
+
105
+ var list = self.data('timepicker-list');
106
+
107
+ // check if input is readonly
108
+ if (self.prop('readonly')) {
109
+ return;
110
+ }
111
+
112
+ // check if list needs to be rendered
113
+ if (!list || list.length === 0) {
114
+ _render(self);
115
+ list = self.data('timepicker-list');
116
+ }
117
+
118
+ // check if a flag was set to close this picker
119
+ if (self.hasClass('ui-timepicker-hideme')) {
120
+ self.removeClass('ui-timepicker-hideme');
121
+ list.hide();
122
+ return;
123
+ }
124
+
125
+ if (list.is(':visible')) {
126
+ return;
127
+ }
128
+
129
+ // make sure other pickers are hidden
130
+ methods.hide();
131
+
132
+ if ((self.offset().top + self.outerHeight(true) + list.outerHeight()) > $(window).height() + $(window).scrollTop()) {
133
+ // position the dropdown on top
134
+ list.css({ 'left':(self.offset().left), 'top': self.offset().top - list.outerHeight() });
135
+ } else {
136
+ // put it under the input
137
+ list.css({ 'left':(self.offset().left), 'top': self.offset().top + self.outerHeight() });
138
+ }
139
+
140
+ list.show();
141
+
142
+ var settings = self.data('timepicker-settings');
143
+ // position scrolling
144
+ var selected = list.find('.ui-timepicker-selected');
145
+
146
+ if (!selected.length) {
147
+ if (self.val()) {
148
+ selected = _findRow(self, list, _time2int(self.val()));
149
+ } else if (settings.scrollDefaultNow) {
150
+ selected = _findRow(self, list, _time2int(new Date()));
151
+ } else if (settings.scrollDefaultTime !== false) {
152
+ selected = _findRow(self, list, _time2int(settings.scrollDefaultTime));
153
+ }
154
+ }
155
+
156
+ if (selected && selected.length) {
157
+ var topOffset = list.scrollTop() + selected.position().top - selected.outerHeight();
158
+ list.scrollTop(topOffset);
159
+ } else {
160
+ list.scrollTop(0);
161
+ }
162
+
163
+ _attachCloseHandler();
164
+
165
+ self.trigger('showTimepicker');
166
+ },
167
+
168
+ hide: function(e)
169
+ {
170
+ $('.ui-timepicker-list:visible').each(function() {
171
+ var list = $(this);
172
+ var self = list.data('timepicker-input');
173
+ var settings = self.data('timepicker-settings');
174
+
175
+ if (settings && settings.selectOnBlur) {
176
+ _selectValue(self);
177
+ }
178
+
179
+ list.hide();
180
+ self.trigger('hideTimepicker');
181
+ });
182
+ },
183
+
184
+ option: function(key, value)
185
+ {
186
+ var self = $(this);
187
+ var settings = self.data('timepicker-settings');
188
+ var list = self.data('timepicker-list');
189
+
190
+ if (typeof key == 'object') {
191
+ settings = $.extend(settings, key);
192
+
193
+ } else if (typeof key == 'string' && typeof value != 'undefined') {
194
+ settings[key] = value;
195
+
196
+ } else if (typeof key == 'string') {
197
+ return settings[key];
198
+ }
199
+
200
+ if (settings.minTime) {
201
+ settings.minTime = _time2int(settings.minTime);
202
+ }
203
+
204
+ if (settings.maxTime) {
205
+ settings.maxTime = _time2int(settings.maxTime);
206
+ }
207
+
208
+ if (settings.durationTime) {
209
+ settings.durationTime = _time2int(settings.durationTime);
210
+ }
211
+
212
+ self.data('timepicker-settings', settings);
213
+
214
+ if (list) {
215
+ list.remove();
216
+ self.data('timepicker-list', false);
217
+ }
218
+
219
+ },
220
+
221
+ getSecondsFromMidnight: function()
222
+ {
223
+ return _time2int($(this).val());
224
+ },
225
+
226
+ getTime: function()
227
+ {
228
+ return new Date(_baseDate.valueOf() + (_time2int($(this).val())*1000));
229
+ },
230
+
231
+ setTime: function(value)
232
+ {
233
+ var self = $(this);
234
+ var prettyTime = _int2time(_time2int(value), self.data('timepicker-settings').timeFormat);
235
+ self.val(prettyTime);
236
+ },
237
+
238
+ remove: function()
239
+ {
240
+ var self = $(this);
241
+
242
+ // check if this element is a timepicker
243
+ if (!self.hasClass('ui-timepicker-input')) {
244
+ return;
245
+ }
246
+
247
+ self.removeAttr('autocomplete', 'off');
248
+ self.removeClass('ui-timepicker-input');
249
+ self.removeData('timepicker-settings');
250
+ self.off('.timepicker');
251
+
252
+ // timepicker-list won't be present unless the user has interacted with this timepicker
253
+ if (self.data('timepicker-list')) {
254
+ self.data('timepicker-list').remove();
255
+ }
256
+
257
+ self.removeData('timepicker-list');
258
+ }
259
+ };
260
+
261
+ // private methods
262
+
263
+ function _render(self)
264
+ {
265
+ var settings = self.data('timepicker-settings');
266
+ var list = self.data('timepicker-list');
267
+
268
+ if (list && list.length) {
269
+ list.remove();
270
+ self.data('timepicker-list', false);
271
+ }
272
+
273
+ list = $('<ul />', {
274
+ 'tabindex': -1,
275
+ 'class': 'ui-timepicker-list'
276
+ });
277
+
278
+ if (settings.className) {
279
+ list.addClass(settings.className);
280
+ }
281
+
282
+ list.css({'display':'none', 'position': 'absolute' });
283
+
284
+ if ((settings.minTime !== null || settings.durationTime !== null) && settings.showDuration) {
285
+ list.addClass('ui-timepicker-with-duration');
286
+ }
287
+
288
+ var durStart = (settings.durationTime !== null) ? settings.durationTime : settings.minTime;
289
+ var start = (settings.minTime !== null) ? settings.minTime : 0;
290
+ var end = (settings.maxTime !== null) ? settings.maxTime : (start + _ONE_DAY - 1);
291
+
292
+ if (end <= start) {
293
+ // make sure the end time is greater than start time, otherwise there will be no list to show
294
+ end += _ONE_DAY;
295
+ }
296
+
297
+ for (var i=start; i <= end; i += settings.step*60) {
298
+ var timeInt = i%_ONE_DAY;
299
+ var row = $('<li />');
300
+ row.data('time', timeInt);
301
+ row.text(_int2time(timeInt, settings.timeFormat));
302
+
303
+ if ((settings.minTime !== null || settings.durationTime !== null) && settings.showDuration) {
304
+ var duration = $('<span />');
305
+ duration.addClass('ui-timepicker-duration');
306
+ duration.text(' ('+_int2duration(i - durStart)+')');
307
+ row.append(duration);
308
+ }
309
+
310
+ list.append(row);
311
+ }
312
+
313
+ list.data('timepicker-input', self);
314
+ self.data('timepicker-list', list);
315
+
316
+ var appendTo = settings.appendTo;
317
+ if (typeof appendTo === 'string') {
318
+ appendTo = $(appendTo);
319
+ } else if (typeof appendTo === 'function') {
320
+ appendTo = appendTo(self);
321
+ }
322
+ appendTo.append(list);
323
+ _setSelected(self, list);
324
+
325
+ list.on('click', 'li', function(e) {
326
+ self.addClass('ui-timepicker-hideme');
327
+ self[0].focus();
328
+
329
+ // make sure only the clicked row is selected
330
+ list.find('li').removeClass('ui-timepicker-selected');
331
+ $(this).addClass('ui-timepicker-selected');
332
+
333
+ _selectValue(self);
334
+ list.hide();
335
+ });
336
+ }
337
+
338
+ function _generateBaseDate()
339
+ {
340
+ var _baseDate = new Date();
341
+ var _currentTimezoneOffset = _baseDate.getTimezoneOffset()*60000;
342
+ _baseDate.setHours(0); _baseDate.setMinutes(0); _baseDate.setSeconds(0);
343
+ var _baseDateTimezoneOffset = _baseDate.getTimezoneOffset()*60000;
344
+
345
+ return new Date(_baseDate.valueOf() - _baseDateTimezoneOffset + _currentTimezoneOffset);
346
+ }
347
+
348
+ function _attachCloseHandler()
349
+ {
350
+ if ('ontouchstart' in document) {
351
+ $('body').on('touchstart.ui-timepicker', _closeHandler);
352
+ } else {
353
+ $('body').on('mousedown.ui-timepicker', _closeHandler);
354
+ $(window).on('scroll.ui-timepicker', _closeHandler);
355
+ }
356
+ }
357
+
358
+ // event handler to decide whether to close timepicker
359
+ function _closeHandler(e)
360
+ {
361
+ var target = $(e.target);
362
+ var input = target.closest('.ui-timepicker-input');
363
+ if (input.length === 0 && target.closest('.ui-timepicker-list').length === 0) {
364
+ methods.hide();
365
+ }
366
+
367
+ $('body').unbind('.ui-timepicker');
368
+ $(window).unbind('.ui-timepicker');
369
+ }
370
+
371
+ function _findRow(self, list, value)
372
+ {
373
+ if (!value && value !== 0) {
374
+ return false;
375
+ }
376
+
377
+ var settings = self.data('timepicker-settings');
378
+ var out = false;
379
+ var halfStep = settings.step*30;
380
+
381
+ // loop through the menu items
382
+ list.find('li').each(function(i, obj) {
383
+ var jObj = $(obj);
384
+
385
+ var offset = jObj.data('time') - value;
386
+
387
+ // check if the value is less than half a step from each row
388
+ if (Math.abs(offset) < halfStep || offset == halfStep) {
389
+ out = jObj;
390
+ return false;
391
+ }
392
+ });
393
+
394
+ return out;
395
+ }
396
+
397
+ function _setSelected(self, list)
398
+ {
399
+ var timeValue = _time2int(self.val());
400
+
401
+ var selected = _findRow(self, list, timeValue);
402
+ if (selected) selected.addClass('ui-timepicker-selected');
403
+ }
404
+
405
+
406
+ function _formatValue()
407
+ {
408
+ if (this.value === '') {
409
+ return;
410
+ }
411
+
412
+ var self = $(this);
413
+ var seconds = _time2int(this.value);
414
+
415
+ if (seconds === null) {
416
+ self.trigger('timeFormatError');
417
+ return;
418
+ }
419
+
420
+ var settings = self.data('timepicker-settings');
421
+
422
+ if (settings.forceRoundTime) {
423
+ var offset = seconds % (settings.step*60); // step is in minutes
424
+
425
+ if (offset >= settings.step*30) {
426
+ // if offset is larger than a half step, round up
427
+ seconds += (settings.step*60) - offset;
428
+ } else {
429
+ // round down
430
+ seconds -= offset;
431
+ }
432
+ }
433
+
434
+ var prettyTime = _int2time(seconds, settings.timeFormat);
435
+ self.val(prettyTime);
436
+ }
437
+
438
+ function _keyhandler(e)
439
+ {
440
+ var self = $(this);
441
+ var list = self.data('timepicker-list');
442
+
443
+ if (!list.is(':visible')) {
444
+ if (e.keyCode == 40) {
445
+ self.focus();
446
+ } else {
447
+ return true;
448
+ }
449
+ }
450
+
451
+ switch (e.keyCode) {
452
+
453
+ case 13: // return
454
+ _selectValue(self);
455
+ methods.hide.apply(this);
456
+ e.preventDefault();
457
+ return false;
458
+
459
+ case 38: // up
460
+ var selected = list.find('.ui-timepicker-selected');
461
+
462
+ if (!selected.length) {
463
+ list.children().each(function(i, obj) {
464
+ if ($(obj).position().top > 0) {
465
+ selected = $(obj);
466
+ return false;
467
+ }
468
+ });
469
+ selected.addClass('ui-timepicker-selected');
470
+
471
+ } else if (!selected.is(':first-child')) {
472
+ selected.removeClass('ui-timepicker-selected');
473
+ selected.prev().addClass('ui-timepicker-selected');
474
+
475
+ if (selected.prev().position().top < selected.outerHeight()) {
476
+ list.scrollTop(list.scrollTop() - selected.outerHeight());
477
+ }
478
+ }
479
+
480
+ break;
481
+
482
+ case 40: // down
483
+ selected = list.find('.ui-timepicker-selected');
484
+
485
+ if (selected.length === 0) {
486
+ list.children().each(function(i, obj) {
487
+ if ($(obj).position().top > 0) {
488
+ selected = $(obj);
489
+ return false;
490
+ }
491
+ });
492
+
493
+ selected.addClass('ui-timepicker-selected');
494
+ } else if (!selected.is(':last-child')) {
495
+ selected.removeClass('ui-timepicker-selected');
496
+ selected.next().addClass('ui-timepicker-selected');
497
+
498
+ if (selected.next().position().top + 2*selected.outerHeight() > list.outerHeight()) {
499
+ list.scrollTop(list.scrollTop() + selected.outerHeight());
500
+ }
501
+ }
502
+
503
+ break;
504
+
505
+ case 27: // escape
506
+ list.find('li').removeClass('ui-timepicker-selected');
507
+ list.hide();
508
+ break;
509
+
510
+ case 9: //tab
511
+ methods.hide();
512
+ break;
513
+
514
+ case 16:
515
+ case 17:
516
+ case 18:
517
+ case 19:
518
+ case 20:
519
+ case 33:
520
+ case 34:
521
+ case 35:
522
+ case 36:
523
+ case 37:
524
+ case 39:
525
+ case 45:
526
+ return;
527
+
528
+ default:
529
+ list.find('li').removeClass('ui-timepicker-selected');
530
+ return;
531
+ }
532
+ }
533
+
534
+ function _selectValue(self)
535
+ {
536
+ var settings = self.data('timepicker-settings');
537
+ var list = self.data('timepicker-list');
538
+ var timeValue = null;
539
+
540
+ var cursor = list.find('.ui-timepicker-selected');
541
+
542
+ if (cursor.length) {
543
+ // selected value found
544
+ timeValue = cursor.data('time');
545
+
546
+ } else if (self.val()) {
547
+
548
+ // no selected value; fall back on input value
549
+ timeValue = _time2int(self.val());
550
+
551
+ _setSelected(self, list);
552
+ }
553
+
554
+ if (timeValue !== null) {
555
+ var timeString = _int2time(timeValue, settings.timeFormat);
556
+ self.val(timeString);
557
+ }
558
+
559
+ self.trigger('change').trigger('changeTime');
560
+ }
561
+
562
+ function _int2duration(seconds)
563
+ {
564
+ var minutes = Math.round(seconds/60);
565
+ var duration;
566
+
567
+ if (Math.abs(minutes) < 60) {
568
+ duration = [minutes, _lang.mins];
569
+ } else if (minutes == 60) {
570
+ duration = ['1', _lang.hr];
571
+ } else {
572
+ var hours = (minutes/60).toFixed(1);
573
+ if (_lang.decimal != '.') hours = hours.replace('.', _lang.decimal);
574
+ duration = [hours, _lang.hrs];
575
+ }
576
+
577
+ return duration.join(' ');
578
+ }
579
+
580
+ function _int2time(seconds, format)
581
+ {
582
+ if (seconds === null) {
583
+ return;
584
+ }
585
+
586
+ var time = new Date(_baseDate.valueOf() + (seconds*1000));
587
+ var output = '';
588
+ var hour, code;
589
+
590
+ for (var i=0; i<format.length; i++) {
591
+
592
+ code = format.charAt(i);
593
+ switch (code) {
594
+
595
+ case 'a':
596
+ output += (time.getHours() > 11) ? 'pm' : 'am';
597
+ break;
598
+
599
+ case 'A':
600
+ output += (time.getHours() > 11) ? 'PM' : 'AM';
601
+ break;
602
+
603
+ case 'g':
604
+ hour = time.getHours() % 12;
605
+ output += (hour === 0) ? '12' : hour;
606
+ break;
607
+
608
+ case 'G':
609
+ output += time.getHours();
610
+ break;
611
+
612
+ case 'h':
613
+ hour = time.getHours() % 12;
614
+
615
+ if (hour !== 0 && hour < 10) {
616
+ hour = '0'+hour;
617
+ }
618
+
619
+ output += (hour === 0) ? '12' : hour;
620
+ break;
621
+
622
+ case 'H':
623
+ hour = time.getHours();
624
+ output += (hour > 9) ? hour : '0'+hour;
625
+ break;
626
+
627
+ case 'i':
628
+ var minutes = time.getMinutes();
629
+ output += (minutes > 9) ? minutes : '0'+minutes;
630
+ break;
631
+
632
+ case 's':
633
+ seconds = time.getSeconds();
634
+ output += (seconds > 9) ? seconds : '0'+seconds;
635
+ break;
636
+
637
+ default:
638
+ output += code;
639
+ }
640
+ }
641
+
642
+ return output;
643
+ }
644
+
645
+ function _time2int(timeString)
646
+ {
647
+ if (timeString === '') return null;
648
+ if (timeString+0 == timeString) return timeString;
649
+
650
+ if (typeof(timeString) == 'object') {
651
+ timeString = timeString.getHours()+':'+timeString.getMinutes()+':'+timeString.getSeconds();
652
+ }
653
+
654
+ var d = new Date(0);
655
+ var time = timeString.toLowerCase().match(/(\d{1,2})(?::(\d{1,2}))?(?::(\d{2}))?\s*([pa]?)/);
656
+
657
+ if (!time) {
658
+ return null;
659
+ }
660
+
661
+ var hour = parseInt(time[1]*1, 10);
662
+ var hours;
663
+
664
+ if (time[4]) {
665
+ if (hour == 12) {
666
+ hours = (time[4] == 'p') ? 12 : 0;
667
+ } else {
668
+ hours = (hour + (time[4] == 'p' ? 12 : 0));
669
+ }
670
+
671
+ } else {
672
+ hours = hour;
673
+ }
674
+
675
+ var minutes = ( time[2]*1 || 0 );
676
+ var seconds = ( time[3]*1 || 0 );
677
+ return hours*3600 + minutes*60 + seconds;
678
+ }
679
+
680
+ // Plugin entry
681
+ $.fn.timepicker = function(method)
682
+ {
683
+ if(methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); }
684
+ else if(typeof method === "object" || !method) { return methods.init.apply(this, arguments); }
685
+ else { $.error("Method "+ method + " does not exist on jQuery.timepicker"); }
686
+ };
687
+ }));