Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 1
/*!
2
 * Timepicker Component for Twitter Bootstrap
3
 *
4
 * Copyright 2013 Joris de Wit
5
 *
6
 * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
7
 *
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
  'use strict';
13
 
14
  // TIMEPICKER PUBLIC CLASS DEFINITION
15
  var Timepicker = function(element, options) {
16
    this.widget = '';
17
    this.$element = $(element);
18
    this.defaultTime = options.defaultTime;
19
    this.disableFocus = options.disableFocus;
20
    this.disableMousewheel = options.disableMousewheel;
21
    this.isOpen = options.isOpen;
22
    this.minuteStep = options.minuteStep;
23
    this.modalBackdrop = options.modalBackdrop;
24
    this.orientation = options.orientation;
25
    this.secondStep = options.secondStep;
26
    this.showInputs = options.showInputs;
27
    this.showMeridian = options.showMeridian;
28
    this.showSeconds = options.showSeconds;
29
    this.template = options.template;
30
    this.appendWidgetTo = options.appendWidgetTo;
31
    this.showWidgetOnAddonClick = options.showWidgetOnAddonClick;
32
 
33
    this._init();
34
  };
35
 
36
  Timepicker.prototype = {
37
 
38
    constructor: Timepicker,
39
    _init: function() {
40
      var self = this;
41
 
42
      if (this.showWidgetOnAddonClick && (this.$element.parent().hasClass('input-append') || this.$element.parent().hasClass('input-prepend'))) {
43
        this.$element.parent('.input-append, .input-prepend').find('.add-on').on({
44
          'click.timepicker': $.proxy(this.showWidget, this)
45
        });
46
        this.$element.on({
47
          'focus.timepicker': $.proxy(this.highlightUnit, this),
48
          'click.timepicker': $.proxy(this.highlightUnit, this),
49
          'keydown.timepicker': $.proxy(this.elementKeydown, this),
50
          'blur.timepicker': $.proxy(this.blurElement, this),
51
          'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
52
        });
53
      } else {
54
        if (this.template) {
55
          this.$element.on({
56
            'focus.timepicker': $.proxy(this.showWidget, this),
57
            'click.timepicker': $.proxy(this.showWidget, this),
58
            'blur.timepicker': $.proxy(this.blurElement, this),
59
            'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
60
          });
61
        } else {
62
          this.$element.on({
63
            'focus.timepicker': $.proxy(this.highlightUnit, this),
64
            'click.timepicker': $.proxy(this.highlightUnit, this),
65
            'keydown.timepicker': $.proxy(this.elementKeydown, this),
66
            'blur.timepicker': $.proxy(this.blurElement, this),
67
            'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
68
          });
69
        }
70
      }
71
 
72
      if (this.template !== false) {
73
        this.$widget = $(this.getTemplate()).on('click', $.proxy(this.widgetClick, this));
74
      } else {
75
        this.$widget = false;
76
      }
77
 
78
      if (this.showInputs && this.$widget !== false) {
79
        this.$widget.find('input').each(function() {
80
          $(this).on({
81
            'click.timepicker': function() { $(this).select(); },
82
            'keydown.timepicker': $.proxy(self.widgetKeydown, self),
83
            'keyup.timepicker': $.proxy(self.widgetKeyup, self)
84
          });
85
        });
86
      }
87
 
88
      this.setDefaultTime(this.defaultTime);
89
    },
90
 
91
    blurElement: function() {
92
      this.highlightedUnit = null;
93
      this.updateFromElementVal();
94
    },
95
 
96
    clear: function() {
97
      this.hour = '';
98
      this.minute = '';
99
      this.second = '';
100
      this.meridian = '';
101
 
102
      this.$element.val('');
103
    },
104
 
105
    decrementHour: function() {
106
      if (this.showMeridian) {
107
        if (this.hour === 1) {
108
          this.hour = 12;
109
        } else if (this.hour === 12) {
110
          this.hour--;
111
 
112
          return this.toggleMeridian();
113
        } else if (this.hour === 0) {
114
          this.hour = 11;
115
 
116
          return this.toggleMeridian();
117
        } else {
118
          this.hour--;
119
        }
120
      } else {
121
        if (this.hour <= 0) {
122
          this.hour = 23;
123
        } else {
124
          this.hour--;
125
        }
126
      }
127
    },
128
 
129
    decrementMinute: function(step) {
130
      var newVal;
131
 
132
      if (step) {
133
        newVal = this.minute - step;
134
      } else {
135
        newVal = this.minute - this.minuteStep;
136
      }
137
 
138
      if (newVal < 0) {
139
        this.decrementHour();
140
        this.minute = newVal + 60;
141
      } else {
142
        this.minute = newVal;
143
      }
144
    },
145
 
146
    decrementSecond: function() {
147
      var newVal = this.second - this.secondStep;
148
 
149
      if (newVal < 0) {
150
        this.decrementMinute(true);
151
        this.second = newVal + 60;
152
      } else {
153
        this.second = newVal;
154
      }
155
    },
156
 
157
    elementKeydown: function(e) {
158
      switch (e.keyCode) {
159
      case 9: //tab
160
      case 27: // escape
161
        this.updateFromElementVal();
162
        break;
163
      case 37: // left arrow
164
        e.preventDefault();
165
        this.highlightPrevUnit();
166
        break;
167
      case 38: // up arrow
168
        e.preventDefault();
169
        switch (this.highlightedUnit) {
170
        case 'hour':
171
          this.incrementHour();
172
          this.highlightHour();
173
          break;
174
        case 'minute':
175
          this.incrementMinute();
176
          this.highlightMinute();
177
          break;
178
        case 'second':
179
          this.incrementSecond();
180
          this.highlightSecond();
181
          break;
182
        case 'meridian':
183
          this.toggleMeridian();
184
          this.highlightMeridian();
185
          break;
186
        }
187
        this.update();
188
        break;
189
      case 39: // right arrow
190
        e.preventDefault();
191
        this.highlightNextUnit();
192
        break;
193
      case 40: // down arrow
194
        e.preventDefault();
195
        switch (this.highlightedUnit) {
196
        case 'hour':
197
          this.decrementHour();
198
          this.highlightHour();
199
          break;
200
        case 'minute':
201
          this.decrementMinute();
202
          this.highlightMinute();
203
          break;
204
        case 'second':
205
          this.decrementSecond();
206
          this.highlightSecond();
207
          break;
208
        case 'meridian':
209
          this.toggleMeridian();
210
          this.highlightMeridian();
211
          break;
212
        }
213
 
214
        this.update();
215
        break;
216
      }
217
    },
218
 
219
    getCursorPosition: function() {
220
      var input = this.$element.get(0);
221
 
222
      if ('selectionStart' in input) {// Standard-compliant browsers
223
 
224
        return input.selectionStart;
225
      } else if (document.selection) {// IE fix
226
        input.focus();
227
        var sel = document.selection.createRange(),
228
          selLen = document.selection.createRange().text.length;
229
 
230
        sel.moveStart('character', - input.value.length);
231
 
232
        return sel.text.length - selLen;
233
      }
234
    },
235
 
236
    getTemplate: function() {
237
      var template,
238
        hourTemplate,
239
        minuteTemplate,
240
        secondTemplate,
241
        meridianTemplate,
242
        templateContent;
243
 
244
      if (this.showInputs) {
245
        hourTemplate = '<input type="text" class="bootstrap-timepicker-hour" maxlength="2"/>';
246
        minuteTemplate = '<input type="text" class="bootstrap-timepicker-minute" maxlength="2"/>';
247
        secondTemplate = '<input type="text" class="bootstrap-timepicker-second" maxlength="2"/>';
248
        meridianTemplate = '<input type="text" class="bootstrap-timepicker-meridian" maxlength="2"/>';
249
      } else {
250
        hourTemplate = '<span class="bootstrap-timepicker-hour"></span>';
251
        minuteTemplate = '<span class="bootstrap-timepicker-minute"></span>';
252
        secondTemplate = '<span class="bootstrap-timepicker-second"></span>';
253
        meridianTemplate = '<span class="bootstrap-timepicker-meridian"></span>';
254
      }
255
 
256
      templateContent = '<table>'+
257
         '<tr>'+
258
           '<td><a href="#" data-action="incrementHour"><i class="fa fa-angle-up"></i></a></td>'+
259
           '<td class="separator">&nbsp;</td>'+
260
           '<td><a href="#" data-action="incrementMinute"><i class="fa fa-angle-up"></i></a></td>'+
261
           (this.showSeconds ?
262
             '<td class="separator">&nbsp;</td>'+
263
             '<td><a href="#" data-action="incrementSecond"><i class="fa fa-angle-up"></i></a></td>'
264
           : '') +
265
           (this.showMeridian ?
266
             '<td class="separator">&nbsp;</td>'+
267
             '<td class="meridian-column"><a href="#" data-action="toggleMeridian"><i class="fa fa-angle-up"></i></a></td>'
268
           : '') +
269
         '</tr>'+
270
         '<tr>'+
271
           '<td>'+ hourTemplate +'</td> '+
272
           '<td class="separator">:</td>'+
273
           '<td>'+ minuteTemplate +'</td> '+
274
           (this.showSeconds ?
275
            '<td class="separator">:</td>'+
276
            '<td>'+ secondTemplate +'</td>'
277
           : '') +
278
           (this.showMeridian ?
279
            '<td class="separator">&nbsp;</td>'+
280
            '<td>'+ meridianTemplate +'</td>'
281
           : '') +
282
         '</tr>'+
283
         '<tr>'+
284
           '<td><a href="#" data-action="decrementHour"><i class="fa fa-angle-down"></i></a></td>'+
285
           '<td class="separator"></td>'+
286
           '<td><a href="#" data-action="decrementMinute"><i class="fa fa-angle-down"></i></a></td>'+
287
           (this.showSeconds ?
288
            '<td class="separator">&nbsp;</td>'+
289
            '<td><a href="#" data-action="decrementSecond"><i class="fa fa-angle-down"></i></a></td>'
290
           : '') +
291
           (this.showMeridian ?
292
            '<td class="separator">&nbsp;</td>'+
293
            '<td><a href="#" data-action="toggleMeridian"><i class="fa fa-angle-down"></i></a></td>'
294
           : '') +
295
         '</tr>'+
296
       '</table>';
297
 
298
      switch(this.template) {
299
      case 'modal':
300
        template = '<div class="bootstrap-timepicker-widget modal hide fade in" data-backdrop="'+ (this.modalBackdrop ? 'true' : 'false') +'">'+
301
          '<div class="modal-header">'+
302
            '<a href="#" class="close" data-dismiss="modal">×</a>'+
303
            '<h3>Pick a Time</h3>'+
304
          '</div>'+
305
          '<div class="modal-content">'+
306
            templateContent +
307
          '</div>'+
308
          '<div class="modal-footer">'+
309
            '<a href="#" class="btn btn-primary" data-dismiss="modal">OK</a>'+
310
          '</div>'+
311
        '</div>';
312
        break;
313
      case 'dropdown':
314
        template = '<div class="bootstrap-timepicker-widget dropdown-menu">'+ templateContent +'</div>';
315
        break;
316
      }
317
 
318
      return template;
319
    },
320
 
321
    getTime: function() {
322
      if (this.hour === '') {
323
        return '';
324
      }
325
 
326
      return this.hour + ':' + (this.minute.toString().length === 1 ? '0' + this.minute : this.minute) + (this.showSeconds ? ':' + (this.second.toString().length === 1 ? '0' + this.second : this.second) : '') + (this.showMeridian ? ' ' + this.meridian : '');
327
    },
328
 
329
    hideWidget: function() {
330
      if (this.isOpen === false) {
331
        return;
332
      }
333
 
334
      this.$element.trigger({
335
        'type': 'hide.timepicker',
336
        'time': {
337
          'value': this.getTime(),
338
          'hours': this.hour,
339
          'minutes': this.minute,
340
          'seconds': this.second,
341
          'meridian': this.meridian
342
        }
343
      });
344
 
345
      if (this.template === 'modal' && this.$widget.modal) {
346
        this.$widget.modal('hide');
347
      } else {
348
        this.$widget.removeClass('open');
349
      }
350
 
351
      $(document).off('mousedown.timepicker, touchend.timepicker');
352
 
353
      this.isOpen = false;
354
      // show/hide approach taken by datepicker
355
      this.$widget.detach();
356
    },
357
 
358
    highlightUnit: function() {
359
      this.position = this.getCursorPosition();
360
      if (this.position >= 0 && this.position <= 2) {
361
        this.highlightHour();
362
      } else if (this.position >= 3 && this.position <= 5) {
363
        this.highlightMinute();
364
      } else if (this.position >= 6 && this.position <= 8) {
365
        if (this.showSeconds) {
366
          this.highlightSecond();
367
        } else {
368
          this.highlightMeridian();
369
        }
370
      } else if (this.position >= 9 && this.position <= 11) {
371
        this.highlightMeridian();
372
      }
373
    },
374
 
375
    highlightNextUnit: function() {
376
      switch (this.highlightedUnit) {
377
      case 'hour':
378
        this.highlightMinute();
379
        break;
380
      case 'minute':
381
        if (this.showSeconds) {
382
          this.highlightSecond();
383
        } else if (this.showMeridian){
384
          this.highlightMeridian();
385
        } else {
386
          this.highlightHour();
387
        }
388
        break;
389
      case 'second':
390
        if (this.showMeridian) {
391
          this.highlightMeridian();
392
        } else {
393
          this.highlightHour();
394
        }
395
        break;
396
      case 'meridian':
397
        this.highlightHour();
398
        break;
399
      }
400
    },
401
 
402
    highlightPrevUnit: function() {
403
      switch (this.highlightedUnit) {
404
      case 'hour':
405
        if(this.showMeridian){
406
          this.highlightMeridian();
407
        } else if (this.showSeconds) {
408
          this.highlightSecond();
409
        } else {
410
          this.highlightMinute();
411
        }
412
        break;
413
      case 'minute':
414
        this.highlightHour();
415
        break;
416
      case 'second':
417
        this.highlightMinute();
418
        break;
419
      case 'meridian':
420
        if (this.showSeconds) {
421
          this.highlightSecond();
422
        } else {
423
          this.highlightMinute();
424
        }
425
        break;
426
      }
427
    },
428
 
429
    highlightHour: function() {
430
      var $element = this.$element.get(0),
431
          self = this;
432
 
433
      this.highlightedUnit = 'hour';
434
 
435
                        if ($element.setSelectionRange) {
436
                                setTimeout(function() {
437
          if (self.hour < 10) {
438
            $element.setSelectionRange(0,1);
439
          } else {
440
            $element.setSelectionRange(0,2);
441
          }
442
                                }, 0);
443
                        }
444
    },
445
 
446
    highlightMinute: function() {
447
      var $element = this.$element.get(0),
448
          self = this;
449
 
450
      this.highlightedUnit = 'minute';
451
 
452
                        if ($element.setSelectionRange) {
453
                                setTimeout(function() {
454
          if (self.hour < 10) {
455
            $element.setSelectionRange(2,4);
456
          } else {
457
            $element.setSelectionRange(3,5);
458
          }
459
                                }, 0);
460
                        }
461
    },
462
 
463
    highlightSecond: function() {
464
      var $element = this.$element.get(0),
465
          self = this;
466
 
467
      this.highlightedUnit = 'second';
468
 
469
                        if ($element.setSelectionRange) {
470
                                setTimeout(function() {
471
          if (self.hour < 10) {
472
            $element.setSelectionRange(5,7);
473
          } else {
474
            $element.setSelectionRange(6,8);
475
          }
476
                                }, 0);
477
                        }
478
    },
479
 
480
    highlightMeridian: function() {
481
      var $element = this.$element.get(0),
482
          self = this;
483
 
484
      this.highlightedUnit = 'meridian';
485
 
486
                        if ($element.setSelectionRange) {
487
                                if (this.showSeconds) {
488
                                        setTimeout(function() {
489
            if (self.hour < 10) {
490
              $element.setSelectionRange(8,10);
491
            } else {
492
              $element.setSelectionRange(9,11);
493
            }
494
                                        }, 0);
495
                                } else {
496
                                        setTimeout(function() {
497
            if (self.hour < 10) {
498
              $element.setSelectionRange(5,7);
499
            } else {
500
              $element.setSelectionRange(6,8);
501
            }
502
                                        }, 0);
503
                                }
504
                        }
505
    },
506
 
507
    incrementHour: function() {
508
      if (this.showMeridian) {
509
        if (this.hour === 11) {
510
          this.hour++;
511
          return this.toggleMeridian();
512
        } else if (this.hour === 12) {
513
          this.hour = 0;
514
        }
515
      }
516
      if (this.hour === 23) {
517
        this.hour = 0;
518
 
519
        return;
520
      }
521
      this.hour++;
522
    },
523
 
524
    incrementMinute: function(step) {
525
      var newVal;
526
 
527
      if (step) {
528
        newVal = this.minute + step;
529
      } else {
530
        newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep);
531
      }
532
 
533
      if (newVal > 59) {
534
        this.incrementHour();
535
        this.minute = newVal - 60;
536
      } else {
537
        this.minute = newVal;
538
      }
539
    },
540
 
541
    incrementSecond: function() {
542
      var newVal = this.second + this.secondStep - (this.second % this.secondStep);
543
 
544
      if (newVal > 59) {
545
        this.incrementMinute(true);
546
        this.second = newVal - 60;
547
      } else {
548
        this.second = newVal;
549
      }
550
    },
551
 
552
    mousewheel: function(e) {
553
      if (this.disableMousewheel) {
554
        return;
555
      }
556
 
557
      e.preventDefault();
558
      e.stopPropagation();
559
 
560
      var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail,
561
          scrollTo = null;
562
 
563
      if (e.type === 'mousewheel') {
564
        scrollTo = (e.originalEvent.wheelDelta * -1);
565
      }
566
      else if (e.type === 'DOMMouseScroll') {
567
        scrollTo = 40 * e.originalEvent.detail;
568
      }
569
 
570
      if (scrollTo) {
571
        e.preventDefault();
572
        $(this).scrollTop(scrollTo + $(this).scrollTop());
573
      }
574
 
575
      switch (this.highlightedUnit) {
576
      case 'minute':
577
        if (delta > 0) {
578
          this.incrementMinute();
579
        } else {
580
          this.decrementMinute();
581
        }
582
        this.highlightMinute();
583
        break;
584
      case 'second':
585
        if (delta > 0) {
586
          this.incrementSecond();
587
        } else {
588
          this.decrementSecond();
589
        }
590
        this.highlightSecond();
591
        break;
592
      case 'meridian':
593
        this.toggleMeridian();
594
        this.highlightMeridian();
595
        break;
596
      default:
597
        if (delta > 0) {
598
          this.incrementHour();
599
        } else {
600
          this.decrementHour();
601
        }
602
        this.highlightHour();
603
        break;
604
      }
605
 
606
      return false;
607
    },
608
 
609
    // This method was adapted from bootstrap-datepicker.
610
    place : function() {
611
      if (this.isInline) {
612
        return;
613
      }
614
      var widgetWidth = this.$widget.outerWidth(), widgetHeight = this.$widget.outerHeight(), visualPadding = 10, windowWidth =
615
        $(window).width(), windowHeight = $(window).height(), scrollTop = $(window).scrollTop();
616
 
617
      var zIndex = parseInt(this.$element.parents().filter(function() {}).first().css('z-index'), 10) + 10;
618
      var offset = this.component ? this.component.parent().offset() : this.$element.offset();
619
      var height = this.component ? this.component.outerHeight(true) : this.$element.outerHeight(false);
620
      var width = this.component ? this.component.outerWidth(true) : this.$element.outerWidth(false);
621
      var left = offset.left, top = offset.top;
622
 
623
      this.$widget.removeClass('timepicker-orient-top timepicker-orient-bottom timepicker-orient-right timepicker-orient-left');
624
 
625
      if (this.orientation.x !== 'auto') {
626
        this.picker.addClass('datepicker-orient-' + this.orientation.x);
627
        if (this.orientation.x === 'right') {
628
          left -= widgetWidth - width;
629
        }
630
      } else{
631
        // auto x orientation is best-placement: if it crosses a window edge, fudge it sideways
632
        // Default to left
633
        this.$widget.addClass('timepicker-orient-left');
634
        if (offset.left < 0) {
635
          left -= offset.left - visualPadding;
636
        } else if (offset.left + widgetWidth > windowWidth) {
637
          left = windowWidth - widgetWidth - visualPadding;
638
        }
639
      }
640
      // auto y orientation is best-situation: top or bottom, no fudging, decision based on which shows more of the widget
641
      var yorient = this.orientation.y, topOverflow, bottomOverflow;
642
      if (yorient === 'auto') {
643
        topOverflow = -scrollTop + offset.top - widgetHeight;
644
        bottomOverflow = scrollTop + windowHeight - (offset.top + height + widgetHeight);
645
        if (Math.max(topOverflow, bottomOverflow) === bottomOverflow) {
646
          yorient = 'top';
647
        } else {
648
          yorient = 'bottom';
649
        }
650
      }
651
      this.$widget.addClass('timepicker-orient-' + yorient);
652
      if (yorient === 'top'){
653
        top += height;
654
      } else{
655
        top -= widgetHeight + parseInt(this.$widget.css('padding-top'), 10);
656
      }
657
 
658
      this.$widget.css({
659
        top : top,
660
        left : left,
661
        zIndex : zIndex
662
      });
663
    },
664
 
665
    remove: function() {
666
      $('document').off('.timepicker');
667
      if (this.$widget) {
668
        this.$widget.remove();
669
      }
670
      delete this.$element.data().timepicker;
671
    },
672
 
673
    setDefaultTime: function(defaultTime) {
674
      if (!this.$element.val()) {
675
        if (defaultTime === 'current') {
676
          var dTime = new Date(),
677
            hours = dTime.getHours(),
678
            minutes = dTime.getMinutes(),
679
            seconds = dTime.getSeconds(),
680
            meridian = 'AM';
681
 
682
          if (seconds !== 0) {
683
            seconds = Math.ceil(dTime.getSeconds() / this.secondStep) * this.secondStep;
684
            if (seconds === 60) {
685
              minutes += 1;
686
              seconds = 0;
687
            }
688
          }
689
 
690
          if (minutes !== 0) {
691
            minutes = Math.ceil(dTime.getMinutes() / this.minuteStep) * this.minuteStep;
692
            if (minutes === 60) {
693
              hours += 1;
694
              minutes = 0;
695
            }
696
          }
697
 
698
          if (this.showMeridian) {
699
            if (hours === 0) {
700
              hours = 12;
701
            } else if (hours >= 12) {
702
              if (hours > 12) {
703
                hours = hours - 12;
704
              }
705
              meridian = 'PM';
706
            } else {
707
              meridian = 'AM';
708
            }
709
          }
710
 
711
          this.hour = hours;
712
          this.minute = minutes;
713
          this.second = seconds;
714
          this.meridian = meridian;
715
 
716
          this.update();
717
 
718
        } else if (defaultTime === false) {
719
          this.hour = 0;
720
          this.minute = 0;
721
          this.second = 0;
722
          this.meridian = 'AM';
723
        } else {
724
          this.setTime(defaultTime);
725
        }
726
      } else {
727
        this.updateFromElementVal();
728
      }
729
    },
730
 
731
    setTime: function(time, ignoreWidget) {
732
      if (!time) {
733
        this.clear();
734
        return;
735
      }
736
 
737
      var timeArray,
738
          hour,
739
          minute,
740
          second,
741
          meridian;
742
 
743
      if (typeof time === 'object' && time.getMonth){
744
        // this is a date object
745
        hour    = time.getHours();
746
        minute  = time.getMinutes();
747
        second  = time.getSeconds();
748
 
749
        if (this.showMeridian){
750
          meridian = 'AM';
751
          if (hour > 12){
752
            meridian = 'PM';
753
            hour = hour % 12;
754
          }
755
 
756
          if (hour === 12){
757
            meridian = 'PM';
758
          }
759
        }
760
      } else {
761
        if (time.match(/p/i) !== null) {
762
          meridian = 'PM';
763
        } else {
764
          meridian = 'AM';
765
        }
766
 
767
        time = time.replace(/[^0-9\:]/g, '');
768
 
769
        timeArray = time.split(':');
770
 
771
        hour = timeArray[0] ? timeArray[0].toString() : timeArray.toString();
772
        minute = timeArray[1] ? timeArray[1].toString() : '';
773
        second = timeArray[2] ? timeArray[2].toString() : '';
774
 
775
        // idiot proofing
776
        if (hour.length > 4) {
777
          second = hour.substr(4, 2);
778
        }
779
        if (hour.length > 2) {
780
          minute = hour.substr(2, 2);
781
          hour = hour.substr(0, 2);
782
        }
783
        if (minute.length > 2) {
784
          second = minute.substr(2, 2);
785
          minute = minute.substr(0, 2);
786
        }
787
        if (second.length > 2) {
788
          second = second.substr(2, 2);
789
        }
790
 
791
        hour = parseInt(hour, 10);
792
        minute = parseInt(minute, 10);
793
        second = parseInt(second, 10);
794
 
795
        if (isNaN(hour)) {
796
          hour = 0;
797
        }
798
        if (isNaN(minute)) {
799
          minute = 0;
800
        }
801
        if (isNaN(second)) {
802
          second = 0;
803
        }
804
 
805
        if (this.showMeridian) {
806
          if (hour < 1) {
807
            hour = 1;
808
          } else if (hour > 12) {
809
            hour = 12;
810
          }
811
        } else {
812
          if (hour >= 24) {
813
            hour = 23;
814
          } else if (hour < 0) {
815
            hour = 0;
816
          }
817
          if (hour < 13 && meridian === 'PM') {
818
            hour = hour + 12;
819
          }
820
        }
821
 
822
        if (minute < 0) {
823
          minute = 0;
824
        } else if (minute >= 60) {
825
          minute = 59;
826
        }
827
 
828
        if (this.showSeconds) {
829
          if (isNaN(second)) {
830
            second = 0;
831
          } else if (second < 0) {
832
            second = 0;
833
          } else if (second >= 60) {
834
            second = 59;
835
          }
836
        }
837
      }
838
 
839
      this.hour = hour;
840
      this.minute = minute;
841
      this.second = second;
842
      this.meridian = meridian;
843
 
844
      this.update(ignoreWidget);
845
    },
846
 
847
    showWidget: function() {
848
      if (this.isOpen) {
849
        return;
850
      }
851
 
852
      if (this.$element.is(':disabled')) {
853
        return;
854
      }
855
 
856
      // show/hide approach taken by datepicker
857
      this.$widget.appendTo(this.appendWidgetTo);
858
      var self = this;
859
      $(document).on('mousedown.timepicker, touchend.timepicker', function (e) {
860
        // This condition was inspired by bootstrap-datepicker.
861
        // The element the timepicker is invoked on is the input but it has a sibling for addon/button.
862
        if (!(self.$element.parent().find(e.target).length ||
863
            self.$widget.is(e.target) ||
864
            self.$widget.find(e.target).length)) {
865
          self.hideWidget();
866
        }
867
      });
868
 
869
      this.$element.trigger({
870
        'type': 'show.timepicker',
871
        'time': {
872
          'value': this.getTime(),
873
          'hours': this.hour,
874
          'minutes': this.minute,
875
          'seconds': this.second,
876
          'meridian': this.meridian
877
        }
878
      });
879
 
880
      this.place();
881
      if (this.disableFocus) {
882
        this.$element.blur();
883
      }
884
 
885
      // widget shouldn't be empty on open
886
      if (this.hour === '') {
887
        if (this.defaultTime) {
888
          this.setDefaultTime(this.defaultTime);
889
        } else {
890
          this.setTime('0:0:0');
891
        }
892
      }
893
 
894
      if (this.template === 'modal' && this.$widget.modal) {
895
        this.$widget.modal('show').on('hidden', $.proxy(this.hideWidget, this));
896
      } else {
897
        if (this.isOpen === false) {
898
          this.$widget.addClass('open');
899
        }
900
      }
901
 
902
      this.isOpen = true;
903
    },
904
 
905
    toggleMeridian: function() {
906
      this.meridian = this.meridian === 'AM' ? 'PM' : 'AM';
907
    },
908
 
909
    update: function(ignoreWidget) {
910
      this.updateElement();
911
      if (!ignoreWidget) {
912
        this.updateWidget();
913
      }
914
 
915
      this.$element.trigger({
916
        'type': 'changeTime.timepicker',
917
        'time': {
918
          'value': this.getTime(),
919
          'hours': this.hour,
920
          'minutes': this.minute,
921
          'seconds': this.second,
922
          'meridian': this.meridian
923
        }
924
      });
925
    },
926
 
927
    updateElement: function() {
928
      this.$element.val(this.getTime()).change();
929
    },
930
 
931
    updateFromElementVal: function() {
932
      this.setTime(this.$element.val());
933
    },
934
 
935
    updateWidget: function() {
936
      if (this.$widget === false) {
937
        return;
938
      }
939
 
940
      var hour = this.hour,
941
          minute = this.minute.toString().length === 1 ? '0' + this.minute : this.minute,
942
          second = this.second.toString().length === 1 ? '0' + this.second : this.second;
943
 
944
      if (this.showInputs) {
945
        this.$widget.find('input.bootstrap-timepicker-hour').val(hour);
946
        this.$widget.find('input.bootstrap-timepicker-minute').val(minute);
947
 
948
        if (this.showSeconds) {
949
          this.$widget.find('input.bootstrap-timepicker-second').val(second);
950
        }
951
        if (this.showMeridian) {
952
          this.$widget.find('input.bootstrap-timepicker-meridian').val(this.meridian);
953
        }
954
      } else {
955
        this.$widget.find('span.bootstrap-timepicker-hour').text(hour);
956
        this.$widget.find('span.bootstrap-timepicker-minute').text(minute);
957
 
958
        if (this.showSeconds) {
959
          this.$widget.find('span.bootstrap-timepicker-second').text(second);
960
        }
961
        if (this.showMeridian) {
962
          this.$widget.find('span.bootstrap-timepicker-meridian').text(this.meridian);
963
        }
964
      }
965
    },
966
 
967
    updateFromWidgetInputs: function() {
968
      if (this.$widget === false) {
969
        return;
970
      }
971
 
972
      var t = this.$widget.find('input.bootstrap-timepicker-hour').val() + ':' +
973
              this.$widget.find('input.bootstrap-timepicker-minute').val() +
974
              (this.showSeconds ? ':' + this.$widget.find('input.bootstrap-timepicker-second').val() : '') +
975
              (this.showMeridian ? this.$widget.find('input.bootstrap-timepicker-meridian').val() : '')
976
      ;
977
 
978
      this.setTime(t, true);
979
    },
980
 
981
    widgetClick: function(e) {
982
      e.stopPropagation();
983
      e.preventDefault();
984
 
985
      var $input = $(e.target),
986
          action = $input.closest('a').data('action');
987
 
988
      if (action) {
989
        this[action]();
990
      }
991
      this.update();
992
 
993
      if ($input.is('input')) {
994
        $input.get(0).setSelectionRange(0,2);
995
      }
996
    },
997
 
998
    widgetKeydown: function(e) {
999
      var $input = $(e.target),
1000
          name = $input.attr('class').replace('bootstrap-timepicker-', '');
1001
 
1002
      switch (e.keyCode) {
1003
      case 9: //tab
1004
        if ((this.showMeridian && name === 'meridian') || (this.showSeconds && name === 'second') || (!this.showMeridian && !this.showSeconds && name === 'minute')) {
1005
          return this.hideWidget();
1006
        }
1007
        break;
1008
      case 27: // escape
1009
        this.hideWidget();
1010
        break;
1011
      case 38: // up arrow
1012
        e.preventDefault();
1013
        switch (name) {
1014
        case 'hour':
1015
          this.incrementHour();
1016
          break;
1017
        case 'minute':
1018
          this.incrementMinute();
1019
          break;
1020
        case 'second':
1021
          this.incrementSecond();
1022
          break;
1023
        case 'meridian':
1024
          this.toggleMeridian();
1025
          break;
1026
        }
1027
        this.setTime(this.getTime());
1028
        $input.get(0).setSelectionRange(0,2);
1029
        break;
1030
      case 40: // down arrow
1031
        e.preventDefault();
1032
        switch (name) {
1033
        case 'hour':
1034
          this.decrementHour();
1035
          break;
1036
        case 'minute':
1037
          this.decrementMinute();
1038
          break;
1039
        case 'second':
1040
          this.decrementSecond();
1041
          break;
1042
        case 'meridian':
1043
          this.toggleMeridian();
1044
          break;
1045
        }
1046
        this.setTime(this.getTime());
1047
        $input.get(0).setSelectionRange(0,2);
1048
        break;
1049
      }
1050
    },
1051
 
1052
    widgetKeyup: function(e) {
1053
      if ((e.keyCode === 65) || (e.keyCode === 77) || (e.keyCode === 80) || (e.keyCode === 46) || (e.keyCode === 8) || (e.keyCode >= 46 && e.keyCode <= 57)) {
1054
        this.updateFromWidgetInputs();
1055
      }
1056
    }
1057
  };
1058
 
1059
  //TIMEPICKER PLUGIN DEFINITION
1060
  $.fn.timepicker = function(option) {
1061
    var args = Array.apply(null, arguments);
1062
    args.shift();
1063
    return this.each(function() {
1064
      var $this = $(this),
1065
        data = $this.data('timepicker'),
1066
        options = typeof option === 'object' && option;
1067
 
1068
      if (!data) {
1069
        $this.data('timepicker', (data = new Timepicker(this, $.extend({}, $.fn.timepicker.defaults, options, $(this).data()))));
1070
      }
1071
 
1072
      if (typeof option === 'string') {
1073
        data[option].apply(data, args);
1074
      }
1075
    });
1076
  };
1077
 
1078
  $.fn.timepicker.defaults = {
1079
    defaultTime: 'current',
1080
    disableFocus: false,
1081
    disableMousewheel: false,
1082
    isOpen: false,
1083
    minuteStep: 15,
1084
    modalBackdrop: false,
1085
    orientation: { x: 'auto', y: 'auto'},
1086
    secondStep: 15,
1087
    showSeconds: false,
1088
    showInputs: true,
1089
    showMeridian: true,
1090
    template: 'dropdown',
1091
    appendWidgetTo: 'body',
1092
    showWidgetOnAddonClick: true
1093
  };
1094
 
1095
  $.fn.timepicker.Constructor = Timepicker;
1096
 
1097
})(jQuery, window, document);