jquery-timepicker-rails 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }));