bootstrap-timepicker-rails 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,377 +1,883 @@
1
- /* =========================================================
2
- * bootstrap-timepicker.js
3
- * http://www.github.com/jdewit/bootstrap-timepicker
4
- * =========================================================
5
- * Copyright 2012
1
+ /*!
2
+ * Timepicker Component for Twitter Bootstrap
6
3
  *
7
- * Created By:
8
- * Joris de Wit @joris_dewit
9
- * Gilbert @mindeavor
4
+ * Copyright 2013 Joris de Wit
10
5
  *
11
- * Licensed under the Apache License, Version 2.0 (the "License");
12
- * you may not use this file except in compliance with the License.
13
- * You may obtain a copy of the License at
6
+ * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
14
7
  *
15
- * http://www.apache.org/licenses/LICENSE-2.0
16
- *
17
- * Unless required by applicable law or agreed to in writing, software
18
- * distributed under the License is distributed on an "AS IS" BASIS,
19
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
- * See the License for the specific language governing permissions and
21
- * limitations under the License.
22
- * ========================================================= */
23
-
24
- !function($) {
25
-
26
- "use strict"; // jshint ;_;
27
-
28
- /* TIMEPICKER PUBLIC CLASS DEFINITION
29
- * ================================== */
30
- var Timepicker = function(element, options) {
31
- this.$element = $(element);
32
- this.options = $.extend({}, $.fn.timepicker.defaults, options, this.$element.data());
33
- this.minuteStep = this.options.minuteStep || this.minuteStep;
34
- this.showMeridian = this.options.showMeridian || this.showMeridian;
35
- this.disableFocus = this.options.disableFocus || this.disableFocus;
36
- this.template = this.options.template || this.template;
37
- this.defaultTime = this.options.defaultTime || this.defaultTime;
38
- this.open = false;
39
- this.init();
40
- };
41
-
42
- Timepicker.prototype = {
43
-
44
- constructor: Timepicker
45
-
46
- , init: function () {
47
-
48
- this.$element
49
- .on('click', $.proxy(this.show, this))
50
- .on('keyup', $.proxy(this.updateFromElementVal, this))
51
- ;
52
-
53
- this.$widget = $(this.getTemplate()).appendTo('body');
54
-
55
- this.$widget.on('click', $.proxy(this.click, this));
56
-
57
- this.setDefaultTime(this.defaultTime);
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ ;(function($, window, document, undefined) {
12
+
13
+ 'use strict'; // jshint ;_;
14
+
15
+ // TIMEPICKER PUBLIC CLASS DEFINITION
16
+ var Timepicker = function(element, options) {
17
+ this.widget = '';
18
+ this.$element = $(element);
19
+ this.defaultTime = options.defaultTime;
20
+ this.disableFocus = options.disableFocus;
21
+ this.isOpen = options.isOpen;
22
+ this.minuteStep = options.minuteStep;
23
+ this.modalBackdrop = options.modalBackdrop;
24
+ this.secondStep = options.secondStep;
25
+ this.showInputs = options.showInputs;
26
+ this.showMeridian = options.showMeridian;
27
+ this.showSeconds = options.showSeconds;
28
+ this.template = options.template;
29
+ this.appendWidgetTo = options.appendWidgetTo;
30
+
31
+ this._init();
32
+ };
33
+
34
+ Timepicker.prototype = {
35
+
36
+ constructor: Timepicker,
37
+
38
+ _init: function() {
39
+ var self = this;
40
+
41
+ if (this.$element.parent().hasClass('input-append') || this.$element.parent().hasClass('input-prepend')) {
42
+ this.$element.parent('.input-append, .input-prepend').find('.add-on').on({
43
+ 'click.timepicker': $.proxy(this.showWidget, this)
44
+ });
45
+ this.$element.on({
46
+ 'focus.timepicker': $.proxy(this.highlightUnit, this),
47
+ 'click.timepicker': $.proxy(this.highlightUnit, this),
48
+ 'keydown.timepicker': $.proxy(this.elementKeydown, this),
49
+ 'blur.timepicker': $.proxy(this.blurElement, this)
50
+ });
51
+ } else {
52
+ if (this.template) {
53
+ this.$element.on({
54
+ 'focus.timepicker': $.proxy(this.showWidget, this),
55
+ 'click.timepicker': $.proxy(this.showWidget, this),
56
+ 'blur.timepicker': $.proxy(this.blurElement, this)
57
+ });
58
+ } else {
59
+ this.$element.on({
60
+ 'focus.timepicker': $.proxy(this.highlightUnit, this),
61
+ 'click.timepicker': $.proxy(this.highlightUnit, this),
62
+ 'keydown.timepicker': $.proxy(this.elementKeydown, this),
63
+ 'blur.timepicker': $.proxy(this.blurElement, this)
64
+ });
58
65
  }
59
-
60
- , show: function(e) {
61
- e.stopPropagation();
62
- e.preventDefault();
63
-
64
- this.$element.trigger('show');
65
-
66
- $('html').on('click.timepicker.data-api', $.proxy(this.hide, this));
67
-
68
- if (true === this.disableFocus) {
69
- this.$element.blur();
70
- }
71
-
72
- var pos = $.extend({}, this.$element.offset(), {
73
- height: this.$element[0].offsetHeight
66
+ }
67
+
68
+ if (this.template !== false) {
69
+ this.$widget = $(this.getTemplate()).prependTo(this.$element.parents(this.appendWidgetTo)).on('click', $.proxy(this.widgetClick, this));
70
+ } else {
71
+ this.$widget = false;
72
+ }
73
+
74
+ if (this.showInputs && this.$widget !== false) {
75
+ this.$widget.find('input').each(function() {
76
+ $(this).on({
77
+ 'click.timepicker': function() { $(this).select(); },
78
+ 'keydown.timepicker': $.proxy(self.widgetKeydown, self)
74
79
  });
75
-
76
- if (this.options.template === 'modal') {
77
- // this.$widget.css({
78
- // top: pos.top + pos.height
79
- // })
80
-
81
- this.$widget.modal('show');
82
- } else {
83
- this.$widget.css({
84
- top: pos.top + pos.height
85
- , left: pos.left
86
- })
87
-
88
- if (!this.open) {
89
- this.$widget.addClass('open');
90
- }
91
- }
92
-
93
- this.open = true;
94
- this.$element.trigger('shown');
95
-
96
- return this;
80
+ });
81
+ }
82
+
83
+ this.setDefaultTime(this.defaultTime);
84
+ },
85
+
86
+ blurElement: function() {
87
+ this.highlightedUnit = undefined;
88
+ this.updateFromElementVal();
89
+ },
90
+
91
+ decrementHour: function() {
92
+ if (this.showMeridian) {
93
+ if (this.hour === 1) {
94
+ this.hour = 12;
95
+ } else if (this.hour === 12) {
96
+ this.hour--;
97
+
98
+ return this.toggleMeridian();
99
+ } else if (this.hour === 0) {
100
+ this.hour = 11;
101
+
102
+ return this.toggleMeridian();
103
+ } else {
104
+ this.hour--;
97
105
  }
98
-
99
- , hide: function(){
100
- this.$element.trigger('hide');
101
-
102
- $('html').off('click.timepicker.data-api', $.proxy(this.hide, this));
103
-
104
- if (this.options.template === 'modal') {
105
- this.$widget.modal('hide');
106
- } else {
107
- this.$widget.removeClass('open');
108
- }
109
- this.open = false;
110
- this.$element.trigger('hidden');
111
-
112
- return this;
106
+ } else {
107
+ if (this.hour === 0) {
108
+ this.hour = 23;
109
+ } else {
110
+ this.hour--;
113
111
  }
114
-
115
- , setValues: function(time) {
116
- var meridian, match = time.match(/(AM|PM)/i);
117
- if (match) {
118
- meridian = match[1];
119
- }
120
- time = $.trim(time.replace(/(PM|AM)/i, ''));
121
- var timeArray = time.split(':');
122
-
123
- this.meridian = meridian;
124
- this.hour = parseInt(timeArray[0], 10);
125
- this.minute = parseInt(timeArray[1], 10);
112
+ }
113
+ this.update();
114
+ },
115
+
116
+ decrementMinute: function(step) {
117
+ var newVal;
118
+
119
+ if (step) {
120
+ newVal = this.minute - step;
121
+ } else {
122
+ newVal = this.minute - this.minuteStep;
123
+ }
124
+
125
+ if (newVal < 0) {
126
+ this.decrementHour();
127
+ this.minute = newVal + 60;
128
+ } else {
129
+ this.minute = newVal;
130
+ }
131
+ this.update();
132
+ },
133
+
134
+ decrementSecond: function() {
135
+ var newVal = this.second - this.secondStep;
136
+
137
+ if (newVal < 0) {
138
+ this.decrementMinute(true);
139
+ this.second = newVal + 60;
140
+ } else {
141
+ this.second = newVal;
142
+ }
143
+ this.update();
144
+ },
145
+
146
+ elementKeydown: function(e) {
147
+ switch (e.keyCode) {
148
+ case 9: //tab
149
+ this.updateFromElementVal();
150
+
151
+ switch (this.highlightedUnit) {
152
+ case 'hour':
153
+ e.preventDefault();
154
+ this.highlightNextUnit();
155
+ break;
156
+ case 'minute':
157
+ if (this.showMeridian || this.showSeconds) {
158
+ e.preventDefault();
159
+ this.highlightNextUnit();
160
+ }
161
+ break;
162
+ case 'second':
163
+ if (this.showMeridian) {
164
+ e.preventDefault();
165
+ this.highlightNextUnit();
166
+ }
167
+ break;
168
+ }
169
+ break;
170
+ case 27: // escape
171
+ this.updateFromElementVal();
172
+ break;
173
+ case 37: // left arrow
174
+ e.preventDefault();
175
+ this.highlightPrevUnit();
176
+ this.updateFromElementVal();
177
+ break;
178
+ case 38: // up arrow
179
+ e.preventDefault();
180
+ switch (this.highlightedUnit) {
181
+ case 'hour':
182
+ this.incrementHour();
183
+ this.highlightHour();
184
+ break;
185
+ case 'minute':
186
+ this.incrementMinute();
187
+ this.highlightMinute();
188
+ break;
189
+ case 'second':
190
+ this.incrementSecond();
191
+ this.highlightSecond();
192
+ break;
193
+ case 'meridian':
194
+ this.toggleMeridian();
195
+ this.highlightMeridian();
196
+ break;
197
+ }
198
+ break;
199
+ case 39: // right arrow
200
+ e.preventDefault();
201
+ this.updateFromElementVal();
202
+ this.highlightNextUnit();
203
+ break;
204
+ case 40: // down arrow
205
+ e.preventDefault();
206
+ switch (this.highlightedUnit) {
207
+ case 'hour':
208
+ this.decrementHour();
209
+ this.highlightHour();
210
+ break;
211
+ case 'minute':
212
+ this.decrementMinute();
213
+ this.highlightMinute();
214
+ break;
215
+ case 'second':
216
+ this.decrementSecond();
217
+ this.highlightSecond();
218
+ break;
219
+ case 'meridian':
220
+ this.toggleMeridian();
221
+ this.highlightMeridian();
222
+ break;
223
+ }
224
+ break;
225
+ }
226
+ },
227
+
228
+ formatTime: function(hour, minute, second, meridian) {
229
+ hour = hour < 10 ? '0' + hour : hour;
230
+ minute = minute < 10 ? '0' + minute : minute;
231
+ second = second < 10 ? '0' + second : second;
232
+
233
+ return hour + ':' + minute + (this.showSeconds ? ':' + second : '') + (this.showMeridian ? ' ' + meridian : '');
234
+ },
235
+
236
+ getCursorPosition: function() {
237
+ var input = this.$element.get(0);
238
+
239
+ if ('selectionStart' in input) {// Standard-compliant browsers
240
+
241
+ return input.selectionStart;
242
+ } else if (document.selection) {// IE fix
243
+ input.focus();
244
+ var sel = document.selection.createRange(),
245
+ selLen = document.selection.createRange().text.length;
246
+
247
+ sel.moveStart('character', - input.value.length);
248
+
249
+ return sel.text.length - selLen;
250
+ }
251
+ },
252
+
253
+ getTemplate: function() {
254
+ var template,
255
+ hourTemplate,
256
+ minuteTemplate,
257
+ secondTemplate,
258
+ meridianTemplate,
259
+ templateContent;
260
+
261
+ if (this.showInputs) {
262
+ hourTemplate = '<input type="text" name="hour" class="bootstrap-timepicker-hour" maxlength="2"/>';
263
+ minuteTemplate = '<input type="text" name="minute" class="bootstrap-timepicker-minute" maxlength="2"/>';
264
+ secondTemplate = '<input type="text" name="second" class="bootstrap-timepicker-second" maxlength="2"/>';
265
+ meridianTemplate = '<input type="text" name="meridian" class="bootstrap-timepicker-meridian" maxlength="2"/>';
266
+ } else {
267
+ hourTemplate = '<span class="bootstrap-timepicker-hour"></span>';
268
+ minuteTemplate = '<span class="bootstrap-timepicker-minute"></span>';
269
+ secondTemplate = '<span class="bootstrap-timepicker-second"></span>';
270
+ meridianTemplate = '<span class="bootstrap-timepicker-meridian"></span>';
271
+ }
272
+
273
+ templateContent = '<table>'+
274
+ '<tr>'+
275
+ '<td><a href="#" data-action="incrementHour"><i class="icon-chevron-up"></i></a></td>'+
276
+ '<td class="separator">&nbsp;</td>'+
277
+ '<td><a href="#" data-action="incrementMinute"><i class="icon-chevron-up"></i></a></td>'+
278
+ (this.showSeconds ?
279
+ '<td class="separator">&nbsp;</td>'+
280
+ '<td><a href="#" data-action="incrementSecond"><i class="icon-chevron-up"></i></a></td>'
281
+ : '') +
282
+ (this.showMeridian ?
283
+ '<td class="separator">&nbsp;</td>'+
284
+ '<td class="meridian-column"><a href="#" data-action="toggleMeridian"><i class="icon-chevron-up"></i></a></td>'
285
+ : '') +
286
+ '</tr>'+
287
+ '<tr>'+
288
+ '<td>'+ hourTemplate +'</td> '+
289
+ '<td class="separator">:</td>'+
290
+ '<td>'+ minuteTemplate +'</td> '+
291
+ (this.showSeconds ?
292
+ '<td class="separator">:</td>'+
293
+ '<td>'+ secondTemplate +'</td>'
294
+ : '') +
295
+ (this.showMeridian ?
296
+ '<td class="separator">&nbsp;</td>'+
297
+ '<td>'+ meridianTemplate +'</td>'
298
+ : '') +
299
+ '</tr>'+
300
+ '<tr>'+
301
+ '<td><a href="#" data-action="decrementHour"><i class="icon-chevron-down"></i></a></td>'+
302
+ '<td class="separator"></td>'+
303
+ '<td><a href="#" data-action="decrementMinute"><i class="icon-chevron-down"></i></a></td>'+
304
+ (this.showSeconds ?
305
+ '<td class="separator">&nbsp;</td>'+
306
+ '<td><a href="#" data-action="decrementSecond"><i class="icon-chevron-down"></i></a></td>'
307
+ : '') +
308
+ (this.showMeridian ?
309
+ '<td class="separator">&nbsp;</td>'+
310
+ '<td><a href="#" data-action="toggleMeridian"><i class="icon-chevron-down"></i></a></td>'
311
+ : '') +
312
+ '</tr>'+
313
+ '</table>';
314
+
315
+ switch(this.template) {
316
+ case 'modal':
317
+ template = '<div class="bootstrap-timepicker-widget modal hide fade in" data-backdrop="'+ (this.modalBackdrop ? 'true' : 'false') +'">'+
318
+ '<div class="modal-header">'+
319
+ '<a href="#" class="close" data-dismiss="modal">×</a>'+
320
+ '<h3>Pick a Time</h3>'+
321
+ '</div>'+
322
+ '<div class="modal-content">'+
323
+ templateContent +
324
+ '</div>'+
325
+ '<div class="modal-footer">'+
326
+ '<a href="#" class="btn btn-primary" data-dismiss="modal">OK</a>'+
327
+ '</div>'+
328
+ '</div>';
329
+ break;
330
+ case 'dropdown':
331
+ template = '<div class="bootstrap-timepicker-widget dropdown-menu">'+ templateContent +'</div>';
332
+ break;
333
+ }
334
+
335
+ return template;
336
+ },
337
+
338
+ getTime: function() {
339
+ return this.formatTime(this.hour, this.minute, this.second, this.meridian);
340
+ },
341
+
342
+ hideWidget: function() {
343
+ if (this.isOpen === false) {
344
+ return;
345
+ }
346
+
347
+ if (this.showInputs) {
348
+ this.updateFromWidgetInputs();
349
+ }
350
+
351
+ this.$element.trigger({
352
+ 'type': 'hide.timepicker',
353
+ 'time': {
354
+ 'value': this.getTime(),
355
+ 'hours': this.hour,
356
+ 'minutes': this.minute,
357
+ 'seconds': this.second,
358
+ 'meridian': this.meridian
359
+ }
360
+ });
361
+
362
+ if (this.template === 'modal') {
363
+ this.$widget.modal('hide');
364
+ } else {
365
+ this.$widget.removeClass('open');
366
+ }
367
+
368
+ $(document).off('mousedown.timepicker');
369
+
370
+ this.isOpen = false;
371
+ },
372
+
373
+ highlightUnit: function() {
374
+ this.position = this.getCursorPosition();
375
+ if (this.position >= 0 && this.position <= 2) {
376
+ this.highlightHour();
377
+ } else if (this.position >= 3 && this.position <= 5) {
378
+ this.highlightMinute();
379
+ } else if (this.position >= 6 && this.position <= 8) {
380
+ if (this.showSeconds) {
381
+ this.highlightSecond();
382
+ } else {
383
+ this.highlightMeridian();
126
384
  }
127
-
128
- , setDefaultTime: function(defaultTime){
129
- if (defaultTime) {
130
- if (defaultTime === 'current') {
131
- var dTime = new Date();
132
- var hours = dTime.getHours();
133
- var minutes = Math.floor(dTime.getMinutes() / this.minuteStep) * this.minuteStep;
134
-
135
- var meridian = "AM";
136
- if (hours === 0) {
137
- hours = 12;
138
- } else if (hours > 12) {
139
- hours = hours - 12;
140
- meridian = "PM";
141
- } else {
142
- meridian = "AM";
143
- }
144
-
145
- this.hour = hours;
146
- this.minute = minutes;
147
- this.meridian = meridian;
148
- } else {
149
- this.setValues(defaultTime);
150
- }
151
- this.update();
385
+ } else if (this.position >= 9 && this.position <= 11) {
386
+ this.highlightMeridian();
387
+ }
388
+ },
389
+
390
+ highlightNextUnit: function() {
391
+ switch (this.highlightedUnit) {
392
+ case 'hour':
393
+ this.highlightMinute();
394
+ break;
395
+ case 'minute':
396
+ if (this.showSeconds) {
397
+ this.highlightSecond();
398
+ } else if (this.showMeridian){
399
+ this.highlightMeridian();
400
+ } else {
401
+ this.highlightHour();
402
+ }
403
+ break;
404
+ case 'second':
405
+ if (this.showMeridian) {
406
+ this.highlightMeridian();
407
+ } else {
408
+ this.highlightHour();
409
+ }
410
+ break;
411
+ case 'meridian':
412
+ this.highlightHour();
413
+ break;
414
+ }
415
+ },
416
+
417
+ highlightPrevUnit: function() {
418
+ switch (this.highlightedUnit) {
419
+ case 'hour':
420
+ this.highlightMeridian();
421
+ break;
422
+ case 'minute':
423
+ this.highlightHour();
424
+ break;
425
+ case 'second':
426
+ this.highlightMinute();
427
+ break;
428
+ case 'meridian':
429
+ if (this.showSeconds) {
430
+ this.highlightSecond();
431
+ } else {
432
+ this.highlightMinute();
433
+ }
434
+ break;
435
+ }
436
+ },
437
+
438
+ highlightHour: function() {
439
+ var $element = this.$element.get(0);
440
+
441
+ this.highlightedUnit = 'hour';
442
+
443
+ if ($element.setSelectionRange) {
444
+ setTimeout(function() {
445
+ $element.setSelectionRange(0,2);
446
+ }, 0);
447
+ }
448
+ },
449
+
450
+ highlightMinute: function() {
451
+ var $element = this.$element.get(0);
452
+
453
+ this.highlightedUnit = 'minute';
454
+
455
+ if ($element.setSelectionRange) {
456
+ setTimeout(function() {
457
+ $element.setSelectionRange(3,5);
458
+ }, 0);
459
+ }
460
+ },
461
+
462
+ highlightSecond: function() {
463
+ var $element = this.$element.get(0);
464
+
465
+ this.highlightedUnit = 'second';
466
+
467
+ if ($element.setSelectionRange) {
468
+ setTimeout(function() {
469
+ $element.setSelectionRange(6,8);
470
+ }, 0);
471
+ }
472
+ },
473
+
474
+ highlightMeridian: function() {
475
+ var $element = this.$element.get(0);
476
+
477
+ this.highlightedUnit = 'meridian';
478
+
479
+ if ($element.setSelectionRange) {
480
+ if (this.showSeconds) {
481
+ setTimeout(function() {
482
+ $element.setSelectionRange(9,11);
483
+ }, 0);
484
+ } else {
485
+ setTimeout(function() {
486
+ $element.setSelectionRange(6,8);
487
+ }, 0);
488
+ }
489
+ }
490
+ },
491
+
492
+ incrementHour: function() {
493
+ if (this.showMeridian) {
494
+ if (this.hour === 11) {
495
+ this.hour++;
496
+ return this.toggleMeridian();
497
+ } else if (this.hour === 12) {
498
+ this.hour = 0;
499
+ }
500
+ }
501
+ if (this.hour === 23) {
502
+ return this.hour = 0;
503
+ }
504
+ this.hour++;
505
+ this.update();
506
+ },
507
+
508
+ incrementMinute: function(step) {
509
+ var newVal;
510
+
511
+ if (step) {
512
+ newVal = this.minute + step;
513
+ } else {
514
+ newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep);
515
+ }
516
+
517
+ if (newVal > 59) {
518
+ this.incrementHour();
519
+ this.minute = newVal - 60;
520
+ } else {
521
+ this.minute = newVal;
522
+ }
523
+ this.update();
524
+ },
525
+
526
+ incrementSecond: function() {
527
+ var newVal = this.second + this.secondStep - (this.second % this.secondStep);
528
+
529
+ if (newVal > 59) {
530
+ this.incrementMinute(true);
531
+ this.second = newVal - 60;
532
+ } else {
533
+ this.second = newVal;
534
+ }
535
+ this.update();
536
+ },
537
+
538
+ remove: function() {
539
+ $('document').off('.timepicker');
540
+ if (this.$widget) {
541
+ this.$widget.remove();
542
+ }
543
+ delete this.$element.data().timepicker;
544
+ },
545
+
546
+ setDefaultTime: function(defaultTime){
547
+ if (!this.$element.val()) {
548
+ if (defaultTime === 'current') {
549
+ var dTime = new Date(),
550
+ hours = dTime.getHours(),
551
+ minutes = Math.floor(dTime.getMinutes() / this.minuteStep) * this.minuteStep,
552
+ seconds = Math.floor(dTime.getSeconds() / this.secondStep) * this.secondStep,
553
+ meridian = 'AM';
554
+
555
+ if (this.showMeridian) {
556
+ if (hours === 0) {
557
+ hours = 12;
558
+ } else if (hours >= 12) {
559
+ if (hours > 12) {
560
+ hours = hours - 12;
561
+ }
562
+ meridian = 'PM';
563
+ } else {
564
+ meridian = 'AM';
152
565
  }
566
+ }
567
+
568
+ this.hour = hours;
569
+ this.minute = minutes;
570
+ this.second = seconds;
571
+ this.meridian = meridian;
572
+
573
+ this.update();
574
+
575
+ } else if (defaultTime === false) {
576
+ this.hour = 0;
577
+ this.minute = 0;
578
+ this.second = 0;
579
+ this.meridian = 'AM';
580
+ } else {
581
+ this.setTime(defaultTime);
153
582
  }
154
-
155
- , formatTime: function(hour, minute, meridian) {
156
- hour = hour < 10 ? '0' + hour : hour;
157
- minute = minute < 10 ? '0' + minute : minute;
158
-
159
- return hour + ':' + minute + ( this.showMeridian ? ' ' + meridian : '' );
583
+ } else {
584
+ this.updateFromElementVal();
585
+ }
586
+ },
587
+
588
+ setTime: function(time) {
589
+ var arr,
590
+ timeArray;
591
+
592
+ if (this.showMeridian) {
593
+ arr = time.split(' ');
594
+ timeArray = arr[0].split(':');
595
+ this.meridian = arr[1];
596
+ } else {
597
+ timeArray = time.split(':');
598
+ }
599
+
600
+ this.hour = parseInt(timeArray[0], 10);
601
+ this.minute = parseInt(timeArray[1], 10);
602
+ this.second = parseInt(timeArray[2], 10);
603
+
604
+ if (isNaN(this.hour)) {
605
+ this.hour = 0;
606
+ }
607
+ if (isNaN(this.minute)) {
608
+ this.minute = 0;
609
+ }
610
+
611
+ if (this.showMeridian) {
612
+ if (this.hour > 12) {
613
+ this.hour = 12;
614
+ } else if (this.hour < 1) {
615
+ this.hour = 12;
160
616
  }
161
617
 
162
- , getTime: function() {
163
- return this.formatTime(this.hour, this.minute, this.meridian);
618
+ if (this.meridian === 'am' || this.meridian === 'a') {
619
+ this.meridian = 'AM';
620
+ } else if (this.meridian === 'pm' || this.meridian === 'p') {
621
+ this.meridian = 'PM';
164
622
  }
165
623
 
166
- , setTime: function(time) {
167
- this.setValues(time);
168
- this.update();
624
+ if (this.meridian !== 'AM' && this.meridian !== 'PM') {
625
+ this.meridian = 'AM';
169
626
  }
170
-
171
- , updateElement: function() {
172
- var time = this.getTime();
173
-
174
- this.$element.val(time);
627
+ } else {
628
+ if (this.hour >= 24) {
629
+ this.hour = 23;
630
+ } else if (this.hour < 0) {
631
+ this.hour = 0;
175
632
  }
176
-
177
- , updateWidget: function() {
178
- this.$widget
179
- .find('td.bootstrap-timepicker-hour').text(this.hour).end()
180
- .find('td.bootstrap-timepicker-minute').text(this.minute < 10 ? '0' + this.minute : this.minute).end()
181
- .find('td.bootstrap-timepicker-meridian').text(this.meridian);
633
+ }
634
+
635
+ if (this.minute < 0) {
636
+ this.minute = 0;
637
+ } else if (this.minute >= 60) {
638
+ this.minute = 59;
639
+ }
640
+
641
+ if (this.showSeconds) {
642
+ if (isNaN(this.second)) {
643
+ this.second = 0;
644
+ } else if (this.second < 0) {
645
+ this.second = 0;
646
+ } else if (this.second >= 60) {
647
+ this.second = 59;
182
648
  }
649
+ }
183
650
 
184
- , update: function() {
185
- this.updateElement();
186
- this.updateWidget();
187
- }
651
+ this.update();
652
+ },
188
653
 
189
- , updateFromElementVal: function () {
190
- var time = this.$element.val();
191
- if (time) {
192
- this.setValues(time);
193
- this.updateWidget();
194
- }
195
- }
196
-
197
- , click: function(e) {
198
- e.stopPropagation();
199
- e.preventDefault();
200
-
201
- if (true !== this.disableFocus) {
202
- this.$element.focus();
203
- }
204
-
205
- var action = $(e.target).closest('a').data('action');
206
- if (action) {
207
- this[action]();
208
- this.update();
209
- }
654
+ showWidget: function() {
655
+ if (this.isOpen) {
656
+ return;
657
+ }
210
658
 
659
+ var self = this;
660
+ $(document).on('mousedown.timepicker', function (e) {
661
+ // Clicked outside the timepicker, hide it
662
+ if ($(e.target).closest('.bootstrap-timepicker-widget').length === 0) {
663
+ self.hideWidget();
211
664
  }
212
-
213
- , incrementHour: function() {
214
- if ( this.showMeridian ) {
215
- if ( this.hour === 12 ) {
216
- this.hour = 1;
217
- return this.toggleMeridian();
218
- }
219
- }
220
- if ( this.hour === 23 ) {
221
- return this.hour = 0;
222
- }
223
- this.hour = this.hour + 1;
665
+ });
666
+
667
+ this.$element.trigger({
668
+ 'type': 'show.timepicker',
669
+ 'time': {
670
+ 'value': this.getTime(),
671
+ 'hours': this.hour,
672
+ 'minutes': this.minute,
673
+ 'seconds': this.second,
674
+ 'meridian': this.meridian
675
+ }
676
+ });
677
+
678
+ if (this.disableFocus) {
679
+ this.$element.blur();
680
+ }
681
+
682
+ this.updateFromElementVal();
683
+
684
+ if (this.template === 'modal') {
685
+ this.$widget.modal('show').on('hidden', $.proxy(this.hideWidget, this));
686
+ } else {
687
+ if (this.isOpen === false) {
688
+ this.$widget.addClass('open');
224
689
  }
225
-
226
- , decrementHour: function() {
227
- if ( this.showMeridian ) {
228
- if (this.hour === 1) {
229
- this.hour = 12;
230
- return this.toggleMeridian();
231
- }
232
- }
233
- if (this.hour === 0) {
234
- return this.hour = 23;
235
- }
236
- this.hour = this.hour - 1;
690
+ }
691
+
692
+ this.isOpen = true;
693
+ },
694
+
695
+ toggleMeridian: function() {
696
+ this.meridian = this.meridian === 'AM' ? 'PM' : 'AM';
697
+ this.update();
698
+ },
699
+
700
+ update: function() {
701
+ this.$element.trigger({
702
+ 'type': 'changeTime.timepicker',
703
+ 'time': {
704
+ 'value': this.getTime(),
705
+ 'hours': this.hour,
706
+ 'minutes': this.minute,
707
+ 'seconds': this.second,
708
+ 'meridian': this.meridian
709
+ }
710
+ });
711
+
712
+ this.updateElement();
713
+ this.updateWidget();
714
+ },
715
+
716
+ updateElement: function() {
717
+ this.$element.val(this.getTime()).change();
718
+ },
719
+
720
+ updateFromElementVal: function() {
721
+ var val = this.$element.val();
722
+
723
+ if (val) {
724
+ this.setTime(val);
725
+ }
726
+ },
727
+
728
+ updateWidget: function() {
729
+ if (this.$widget === false) {
730
+ return;
731
+ }
732
+
733
+ var hour = this.hour < 10 ? '0' + this.hour : this.hour,
734
+ minute = this.minute < 10 ? '0' + this.minute : this.minute,
735
+ second = this.second < 10 ? '0' + this.second : this.second;
736
+
737
+ if (this.showInputs) {
738
+ this.$widget.find('input.bootstrap-timepicker-hour').val(hour);
739
+ this.$widget.find('input.bootstrap-timepicker-minute').val(minute);
740
+
741
+ if (this.showSeconds) {
742
+ this.$widget.find('input.bootstrap-timepicker-second').val(second);
237
743
  }
238
-
239
- , incrementMinute: function() {
240
- var newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep);
241
- if (newVal > 59) {
242
- this.incrementHour();
243
- this.minute = newVal - 60;
244
- } else {
245
- this.minute = newVal;
246
- }
744
+ if (this.showMeridian) {
745
+ this.$widget.find('input.bootstrap-timepicker-meridian').val(this.meridian);
247
746
  }
747
+ } else {
748
+ this.$widget.find('span.bootstrap-timepicker-hour').text(hour);
749
+ this.$widget.find('span.bootstrap-timepicker-minute').text(minute);
248
750
 
249
- , decrementMinute: function() {
250
- var newVal = this.minute - this.minuteStep;
251
- if (newVal < 0) {
252
- this.decrementHour();
253
- this.minute = newVal + 60;
254
- } else {
255
- this.minute = newVal;
256
- }
751
+ if (this.showSeconds) {
752
+ this.$widget.find('span.bootstrap-timepicker-second').text(second);
257
753
  }
258
-
259
- , toggleMeridian: function() {
260
- this.meridian = this.meridian === 'AM' ? 'PM' : 'AM';
261
-
262
- this.update();
263
- }
264
-
265
- , getTemplate: function() {
266
- if (this.options.templates[this.options.template]) {
267
- return this.options.templates[this.options.template];
268
- }
269
- var template;
270
- switch(this.options.template) {
271
- case 'modal':
272
- template = '<div class="bootstrap-timepicker modal hide fade in" style="top: 30%; margin-top: 0; width: 200px; margin-left: -100px;" data-backdrop="false">'+
273
- '<div class="modal-header">'+
274
- '<a href="#" class="close" data-action="hide">×</a>'+
275
- '<h3>Pick a Time</h3>'+
276
- '</div>'+
277
- '<div class="modal-content">'+
278
- '<table>'+
279
- '<tr>'+
280
- '<td><a href="#" data-action="incrementHour"><i class="icon-chevron-up"></i></a></td>'+
281
- '<td class="separator"></td>'+
282
- '<td><a href="#" data-action="incrementMinute"><i class="icon-chevron-up"></i></a></td>'+
283
- ( this.showMeridian ? '<td><a href="#" data-action="toggleMeridian"><i class="icon-chevron-up"></i></a></td>' : '' ) +
284
- '</tr>'+
285
- '<tr>'+
286
- '<td class="bootstrap-timepicker-hour"></td> '+
287
- '<td class="separator">:</td>'+
288
- '<td class="bootstrap-timepicker-minute"></td> '+
289
- ( this.showMeridian ? '<td class="bootstrap-timepicker-meridian"></td>' : '' ) +
290
- '</tr>'+
291
- '<tr>'+
292
- '<td><a href="#" data-action="decrementHour"><i class="icon-chevron-down"></i></a></td>'+
293
- '<td class="separator"></td>'+
294
- '<td><a href="#" data-action="decrementMinute"><i class="icon-chevron-down"></i></a></td>'+
295
- ( this.showMeridian ? '<td><a href="#" data-action="toggleMeridian"><i class="icon-chevron-down"></i></a></td>' : '' ) +
296
- '</tr>'+
297
- '</table>'+
298
- '</div>'+
299
- '<div class="modal-footer">'+
300
- '<a href="#" class="btn btn-primary" data-action="hide">Ok</a>'+
301
- '</div>'+
302
- '</div>';
303
-
304
- break;
305
- case 'dropdown':
306
- template = '<div class="bootstrap-timepicker dropdown-menu">'+
307
- '<table>'+
308
- '<tr>'+
309
- '<td><a href="#" data-action="incrementHour"><i class="icon-chevron-up"></i></a></td>'+
310
- '<td class="separator"></td>'+
311
- '<td><a href="#" data-action="incrementMinute"><i class="icon-chevron-up"></i></a></td>'+
312
- ( this.showMeridian ? '<td><a href="#" data-action="toggleMeridian"><i class="icon-chevron-up"></i></a></td>' : '' ) +
313
- '</tr>'+
314
- '<tr>'+
315
- '<td class="bootstrap-timepicker-hour"></td> '+
316
- '<td class="separator">:</td>'+
317
- '<td class="bootstrap-timepicker-minute"></td> '+
318
- ( this.showMeridian ? '<td class="bootstrap-timepicker-meridian"></td>' : '' ) +
319
- '</tr>'+
320
- '<tr>'+
321
- '<td><a href="#" data-action="decrementHour"><i class="icon-chevron-down"></i></a></td>'+
322
- '<td class="separator"></td>'+
323
- '<td><a href="#" data-action="decrementMinute"><i class="icon-chevron-down"></i></a></td>'+
324
- ( this.showMeridian ? '<td><a href="#" data-action="toggleMeridian"><i class="icon-chevron-down"></i></a></td>' : '' ) +
325
- '</tr>'+
326
- '</table>'+
327
- '</div>';
328
- break;
329
-
330
- }
331
- return template;
754
+ if (this.showMeridian) {
755
+ this.$widget.find('span.bootstrap-timepicker-meridian').text(this.meridian);
332
756
  }
333
- };
334
-
335
-
336
- /* TIMEPICKER PLUGIN DEFINITION
337
- * =========================== */
338
-
339
- $.fn.timepicker = function (option) {
340
- return this.each(function () {
341
- var $this = $(this)
342
- , data = $this.data('timepicker')
343
- , options = typeof option == 'object' && option;
344
- if (!data) {
345
- $this.data('timepicker', (data = new Timepicker(this, options)));
757
+ }
758
+ },
759
+
760
+ updateFromWidgetInputs: function() {
761
+ if (this.$widget === false) {
762
+ return;
763
+ }
764
+ var time = $('input.bootstrap-timepicker-hour', this.$widget).val() + ':' +
765
+ $('input.bootstrap-timepicker-minute', this.$widget).val() +
766
+ (this.showSeconds ? ':' + $('input.bootstrap-timepicker-second', this.$widget).val() : '') +
767
+ (this.showMeridian ? ' ' + $('input.bootstrap-timepicker-meridian', this.$widget).val() : '');
768
+
769
+ this.setTime(time);
770
+ },
771
+
772
+ widgetClick: function(e) {
773
+ e.stopPropagation();
774
+ e.preventDefault();
775
+
776
+ var action = $(e.target).closest('a').data('action');
777
+ if (action) {
778
+ this[action]();
779
+ }
780
+ },
781
+
782
+ widgetKeydown: function(e) {
783
+ var $input = $(e.target).closest('input'),
784
+ name = $input.attr('name');
785
+
786
+ switch (e.keyCode) {
787
+ case 9: //tab
788
+ if (this.showMeridian) {
789
+ if (name === 'meridian') {
790
+ return this.hideWidget();
346
791
  }
347
- if (typeof option == 'string') {
348
- data[option]();
792
+ } else {
793
+ if (this.showSeconds) {
794
+ if (name === 'second') {
795
+ return this.hideWidget();
796
+ }
797
+ } else {
798
+ if (name === 'minute') {
799
+ return this.hideWidget();
800
+ }
349
801
  }
350
- })
351
- }
352
-
353
- $.fn.timepicker.defaults = {
354
- minuteStep: 15
355
- , disableFocus: false
356
- , defaultTime: 'current'
357
- , showMeridian: true
358
- , template: 'dropdown'
359
- , templates: {} // set custom templates
802
+ }
803
+
804
+ this.updateFromWidgetInputs();
805
+ break;
806
+ case 27: // escape
807
+ this.hideWidget();
808
+ break;
809
+ case 38: // up arrow
810
+ e.preventDefault();
811
+ switch (name) {
812
+ case 'hour':
813
+ this.incrementHour();
814
+ break;
815
+ case 'minute':
816
+ this.incrementMinute();
817
+ break;
818
+ case 'second':
819
+ this.incrementSecond();
820
+ break;
821
+ case 'meridian':
822
+ this.toggleMeridian();
823
+ break;
824
+ }
825
+ break;
826
+ case 40: // down arrow
827
+ e.preventDefault();
828
+ switch (name) {
829
+ case 'hour':
830
+ this.decrementHour();
831
+ break;
832
+ case 'minute':
833
+ this.decrementMinute();
834
+ break;
835
+ case 'second':
836
+ this.decrementSecond();
837
+ break;
838
+ case 'meridian':
839
+ this.toggleMeridian();
840
+ break;
841
+ }
842
+ break;
843
+ }
360
844
  }
361
-
362
- $.fn.timepicker.Constructor = Timepicker
363
-
364
- /* TIMEPICKER DATA-API
365
- * ================== */
366
-
367
- $(function () {
368
- $('body').on('focus.timepicker.data-api', '[data-provide="timepicker"]', function (e) {
369
- var $this = $(this);
370
- if ($this.data('timepicker')) {
371
- return;
372
- }
373
- e.preventDefault();
374
- $this.timepicker($this.data());
375
- })
376
- })
377
- }(window.jQuery);
845
+ };
846
+
847
+
848
+ //TIMEPICKER PLUGIN DEFINITION
849
+ $.fn.timepicker = function(option) {
850
+ var args = Array.apply(null, arguments);
851
+ args.shift();
852
+ return this.each(function() {
853
+ var $this = $(this),
854
+ data = $this.data('timepicker'),
855
+ options = typeof option === 'object' && option;
856
+
857
+ if (!data) {
858
+ $this.data('timepicker', (data = new Timepicker(this, $.extend({}, $.fn.timepicker.defaults, options, $(this).data()))));
859
+ }
860
+
861
+ if (typeof option === 'string') {
862
+ data[option].apply(data, args);
863
+ }
864
+ });
865
+ };
866
+
867
+ $.fn.timepicker.defaults = {
868
+ defaultTime: 'current',
869
+ disableFocus: false,
870
+ isOpen: false,
871
+ minuteStep: 15,
872
+ modalBackdrop: false,
873
+ secondStep: 15,
874
+ showSeconds: false,
875
+ showInputs: true,
876
+ showMeridian: true,
877
+ template: 'dropdown',
878
+ appendWidgetTo: '.bootstrap-timepicker'
879
+ };
880
+
881
+ $.fn.timepicker.Constructor = Timepicker;
882
+
883
+ })(jQuery, window, document);