PageSourceSearch

https://jobs.pointhr.com/Scripts/bootstrap-timepicker.js

js pointhr.com collected 2026-10-05 20:40:12 UTC 32,937 bytes, 1,113 lines download raw bytes

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-group') || this.$element.parent().hasClass('input-prepend'))) {
43        this.$element.parent('.input-group, .input-prepend').find('.input-group-addon').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" data-name="hour" class="bootstrap-timepicker-hour form-control" maxlength="2"/>';
246        minuteTemplate = '<input type="text" data-name="minute" class="bootstrap-timepicker-minute form-control" maxlength="2"/>';
247        secondTemplate = '<input type="text" data-name="second" class="bootstrap-timepicker-second form-control" maxlength="2"/>';
248        meridianTemplate = '<input type="text" data-name="meridian" class="bootstrap-timepicker-meridian form-control" 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="glyphicon glyphicon-chevron-up"></i></a></td>'+
259           '<td class="separator">&nbsp;</td>'+
260           '<td><a href="#" data-action="incrementMinute"><i class="glyphicon glyphicon-chevron-up"></i></a></td>'+
261           (this.showSeconds ?
262             '<td class="separator">&nbsp;</td>'+
263             '<td><a href="#" data-action="incrementSecond"><i class="glyphicon glyphicon-chevron-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="glyphicon glyphicon-chevron-up"></i></a></td>'
268           : '') +
269         '</tr>'+
270         '<tr>'+
271           '<td><span>'+ hourTemplate +'</span></td> '+
272           '<td class="separator">:</td>'+
273           '<td><span>'+ minuteTemplate +'</span></td> '+
274           (this.showSeconds ?
275            '<td class="separator">:</td>'+
276            '<td><span>'+ secondTemplate +'</span></td>'
277           : '') +
278           (this.showMeridian ?
279            '<td class="separator">&nbsp;</td>'+
280            '<td><span>'+ meridianTemplate +'</span></td>'
281           : '') +
282         '</tr>'+
283         '<tr>'+
284           '<td><a href="#" data-action="decrementHour"><i class="glyphicon glyphicon-chevron-down"></i></a></td>'+
285           '<td class="separator"></td>'+
286           '<td><a href="#" data-action="decrementMinute"><i class="glyphicon glyphicon-chevron-down"></i></a></td>'+
287           (this.showSeconds ?
288            '<td class="separator">&nbsp;</td>'+
289            '<td><a href="#" data-action="decrementSecond"><i class="glyphicon glyphicon-chevron-down"></i></a></td>'
290           : '') +
291           (this.showMeridian ?
292            '<td class="separator">&nbsp;</td>'+
293            '<td><a href="#" data-action="toggleMeridian"><i class="glyphicon glyphicon-chevron-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 fade" data-backdrop="'+ (this.modalBackdrop ? 'true' : 'false') +'">'+
301          '<div class="modal-dialog">' +
302            '<div class="modal-content">' +
303              '<div class="modal-header">'+
304                '<a href="#" class="close" data-dismiss="modal">
304×</a>'+
305                '<h3>Pick a Time</h3>'+
306              '</div>'+
307              '<div class="modal-body">'+
308                templateContent +
309              '</div>'+
310              '<div class="modal-footer">'+
311                '<a href="#" class="btn btn-primary" data-dismiss="modal">OK</a>'+
312              '</div>'+
313            '</div>'+
314          '</div>' +
315        '</div>';
316        break;
317      case 'dropdown':
318        template = '<div class="bootstrap-timepicker-widget dropdown-menu">'+ templateContent +'</div>';
319        break;
320      }
321
322      return template;
323    },
324
325    getTime: function() {
326      if (this.hour === '') {
327        return '';
328      }
329
330      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 : '');
331    },
332
333    hideWidget: function() {
334      if (this.isOpen === false) {
335        return;
336      }
337
338      this.$element.trigger({
339        'type': 'hide.timepicker',
340        'time': {
341          'value': this.getTime(),
342          'hours': this.hour,
343          'minutes': this.minute,
344          'seconds': this.second,
345          'meridian': this.meridian
346        }
347      });
348
349      if (this.template === 'modal' && this.$widget.modal) {
350        this.$widget.modal('hide');
351      } else {
352        this.$widget.removeClass('open');
353      }
354
355      $(document).off('mousedown.timepicker, touchend.timepicker');
356
357      this.isOpen = false;
358      // show/hide approach taken by datepicker
359      this.$widget.detach();
360    },
361
362    highlightUnit: function() {
363      this.position = this.getCursorPosition();
364      if (this.position >= 0 && this.position <= 2) {
365        this.highlightHour();
366      } else if (this.position >= 3 && this.position <= 5) {
367        this.highlightMinute();
368      } else if (this.position >= 6 && this.position <= 8) {
369        if (this.showSeconds) {
370          this.highlightSecond();
371        } else {
372          this.highlightMeridian();
373        }
374      } else if (this.position >= 9 && this.position <= 11) {
375        this.highlightMeridian();
376      }
377    },
378
379    highlightNextUnit: function() {
380      switch (this.highlightedUnit) {
381      case 'hour':
382        this.highlightMinute();
383        break;
384      case 'minute':
385        if (this.showSeconds) {
386          this.highlightSecond();
387        } else if (this.showMeridian){
388          this.highlightMeridian();
389        } else {
390          this.highlightHour();
391        }
392        break;
393      case 'second':
394        if (this.showMeridian) {
395          this.highlightMeridian();
396        } else {
397          this.highlightHour();
398        }
399        break;
400      case 'meridian':
401        this.highlightHour();
402        break;
403      }
404    },
405
406    highlightPrevUnit: function() {
407      switch (this.highlightedUnit) {
408      case 'hour':
409        if(this.showMeridian){
410          this.highlightMeridian();
411        } else if (this.showSeconds) {
412          this.highlightSecond();
413        } else {
414          this.highlightMinute();
415        }
416        break;
417      case 'minute':
418        this.highlightHour();
419        break;
420      case 'second':
421        this.highlightMinute();
422        break;
423      case 'meridian':
424        if (this.showSeconds) {
425          this.highlightSecond();
426        } else {
427          this.highlightMinute();
428        }
429        break;
430      }
431    },
432
433    highlightHour: function() {
434      var $element = this.$element.get(0),
435          self = this;
436
437      this.highlightedUnit = 'hour';
438
439			if ($element.setSelectionRange) {
440				setTimeout(function() {
441          if (self.hour < 10) {
442            $element.setSelectionRange(0,1);
443          } else {
444            $element.setSelectionRange(0,2);
445          }
446				}, 0);
447			}
448    },
449
450    highlightMinute: function() {
451      var $element = this.$element.get(0),
452          self = this;
453
454      this.highlightedUnit = 'minute';
455
456			if ($element.setSelectionRange) {
457				setTimeout(function() {
458          if (self.hour < 10) {
459            $element.setSelectionRange(2,4);
460          } else {
461            $element.setSelectionRange(3,5);
462          }
463				}, 0);
464			}
465    },
466
467    highlightSecond: function() {
468      var $element = this.$element.get(0),
469          self = this;
470
471      this.highlightedUnit = 'second';
472
473			if ($element.setSelectionRange) {
474				setTimeout(function() {
475          if (self.hour < 10) {
476            $element.setSelectionRange(5,7);
477          } else {
478            $element.setSelectionRange(6,8);
479          }
480				}, 0);
481			}
482    },
483
484    highlightMeridian: function() {
485      var $element = this.$element.get(0),
486          self = this;
487
488      this.highlightedUnit = 'meridian';
489
490			if ($element.setSelectionRange) {
491				if (this.showSeconds) {
492					setTimeout(function() {
493            if (self.hour < 10) {
494              $element.setSelectionRange(8,10);
495            } else {
496              $element.setSelectionRange(9,11);
497            }
498					}, 0);
499				} else {
500					setTimeout(function() {
501            if (self.hour < 10) {
502              $element.setSelectionRange(5,7);
503            } else {
504              $element.setSelectionRange(6,8);
505            }
506					}, 0);
507				}
508			}
509    },
510
511    incrementHour: function() {
512      if (this.showMeridian) {
513        if (this.hour === 11) {
514          this.hour++;
515          return this.toggleMeridian();
516        } else if (this.hour === 12) {
517          this.hour = 0;
518        }
519      }
520      if (this.hour === 23) {
521        this.hour = 0;
522
523        return;
524      }
525      this.hour++;
526    },
527
528    incrementMinute: function(step) {
529      var newVal;
530
531      if (step) {
532        newVal = this.minute + step;
533      } else {
534        newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep);
535      }
536
537      if (newVal > 59) {
538        this.incrementHour();
539        this.minute = newVal - 60;
540      } else {
541        this.minute = newVal;
542      }
543    },
544
545    incrementSecond: function() {
546      var newVal = this.second + this.secondStep - (this.second % this.secondStep);
547
548      if (newVal > 59) {
549        this.incrementMinute(true);
550        this.second = newVal - 60;
551      } else {
552        this.second = newVal;
553      }
554    },
555
556    mousewheel: function(e) {
557      if (this.disableMousewheel) {
558        return;
559      }
560
561      e.preventDefault();
562      e.stopPropagation();
563
564      var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail,
565          scrollTo = null;
566
567      if (e.type === 'mousewheel') {
568        scrollTo = (e.originalEvent.wheelDelta * -1);
569      }
570      else if (e.type === 'DOMMouseScroll') {
571        scrollTo = 40 * e.originalEvent.detail;
572      }
573
574      if (scrollTo) {
575        e.preventDefault();
576        $(this).scrollTop(scrollTo + $(this).scrollTop());
577      }
578
579      switch (this.highlightedUnit) {
580      case 'minute':
581        if (delta > 0) {
582          this.incrementMinute();
583        } else {
584          this.decrementMinute();
585        }
586        this.highlightMinute();
587        break;
588      case 'second':
589        if (delta > 0) {
590          this.incrementSecond();
591        } else {
592          this.decrementSecond();
593        }
594        this.highlightSecond();
595        break;
596      case 'meridian':
597        this.toggleMeridian();
598        this.highlightMeridian();
599        break;
600      default:
601        if (delta > 0) {
602          this.incrementHour();
603        } else {
604          this.decrementHour();
605        }
606        this.highlightHour();
607        break;
608      }
609
610      return false;
611    },
612
613    // This method was adapted from bootstrap-datepicker.
614    place : function() {
615      if (this.isInline) {
616        return;
617      }
618
619      if (this.template === 'modal'){
620        return ;
621      }
622
623      var widgetWidth = this.$widget.outerWidth(); 
624      var widgetHeight = this.$widget.outerHeight();
625      var visualPadding = 10;
626      var windowWidth = $(window).width();
627      var windowHeight = $(window).height();
628      var scrollTop = $(window).scrollTop();
629
630      var zIndex = parseInt(this.$element.parents().filter(function() {}).first().css('z-index'), 10) + 10;
631      var offset = this.component ? this.component.parent().offset() : this.$element.offset();
632      var height = this.component ? this.component.outerHeight(true) : this.$element.outerHeight(false);
633      var width = this.component ? this.component.outerWidth(true) : this.$element.outerWidth(false);
634      var left = offset.left, top = offset.top;
635      
636      this.$widget.removeClass('timepicker-orient-top timepicker-orient-bottom timepicker-orient-right timepicker-orient-left');
637
638      if (this.orientation.x !== 'auto') {
639        this.picker.addClass('datepicker-orient-' + this.orientation.x);
640        if (this.orientation.x === 'right') {
641          left -= widgetWidth - width;
642        }
643      } else{
644        // auto x orientation is best-placement: if it crosses a window edge, fudge it sideways
645        // Default to left
646        this.$widget.addClass('timepicker-orient-left');
647        if (offset.left < 0) {
648          left -= offset.left - visualPadding;
649        } else if (offset.left + widgetWidth > windowWidth) {
650          left = windowWidth - widgetWidth - visualPadding;
651        }
652      }
653      // auto y orientation is best-situation: top or bottom, no fudging, decision based on which shows more of the widget
654      var yorient = this.orientation.y, topOverflow, bottomOverflow;
655      if (yorient === 'auto') {
656        topOverflow = -scrollTop + offset.top - widgetHeight;
657        bottomOverflow = scrollTop + windowHeight - (offset.top + height + widgetHeight);
658        if (Math.max(topOverflow, bottomOverflow) === bottomOverflow) {
659          yorient = 'top';
660        } else {
661          yorient = 'bottom';
662        }
663      }
664      this.$widget.addClass('timepicker-orient-' + yorient);
665      if (yorient === 'top'){
666        top += height;
667      } else{
668        top -= widgetHeight + parseInt(this.$widget.css('padding-top'), 10);
669      }
670
671      this.$widget.css({
672        top : top,
673        left : left,
674        zIndex : zIndex
675      });
676    },
677
678    remove: function() {
679      $('document').off('.timepicker');
680      if (this.$widget) {
681        this.$widget.remove();
682      }
683      delete this.$element.data().timepicker;
684    },
685
686    setDefaultTime: function(defaultTime) {
687      if (!this.$element.val()) {
688        if (defaultTime === 'current') {
689          var dTime = new Date(),
690            hours = dTime.getHours(),
691            minutes = dTime.getMinutes(),
692            seconds = dTime.getSeconds(),
693            meridian = 'AM';
694
695          if (seconds !== 0) {
696            seconds = Math.ceil(dTime.getSeconds() / this.secondStep) * this.secondStep;
697            if (seconds === 60) {
698              minutes += 1;
699              seconds = 0;
700            }
701          }
702
703          if (minutes !== 0) {
704            minutes = Math.ceil(dTime.getMinutes() / this.minuteStep) * this.minuteStep;
705            if (minutes === 60) {
706              hours += 1;
707              minutes = 0;
708            }
709          }
710
711          if (this.showMeridian) {
712            if (hours === 0) {
713              hours = 12;
714            } else if (hours >= 12) {
715              if (hours > 12) {
716                hours = hours - 12;
717              }
718              meridian = 'PM';
719            } else {
720              meridian = 'AM';
721            }
722          }
723
724          this.hour = hours;
725          this.minute = minutes;
726          this.second = seconds;
727          this.meridian = meridian;
728
729          this.update();
730
731        } else if (defaultTime === false) {
732          this.hour = 0;
733          this.minute = 0;
734          this.second = 0;
735          this.meridian = 'AM';
736        } else {
737          this.setTime(defaultTime);
738        }
739      } else {
740        this.updateFromElementVal();
741      }
742    },
743
744    setTime: function(time, ignoreWidget) {
745      if (!time) {
746        this.clear();
747        return;
748      }
749
750      var timeArray,
751          hour,
752          minute,
753          second,
754          meridian;
755
756      if (typeof time === 'object' && time.getMonth){
757        // this is a date object
758        hour    = time.getHours();
759        minute  = time.getMinutes();
760        second  = time.getSeconds();
761
762        if (this.showMeridian){
763          meridian = 'AM';
764          if (hour > 12){
765            meridian = 'PM';
766            hour = hour % 12;
767          }
768
769          if (hour === 12){
770            meridian = 'PM';
771          }
772        }
773      } else {
774        if (time.match(/p/i) !== null) {
775          meridian = 'PM';
776        } else {
777          meridian = 'AM';
778        }
779
780        time = time.replace(/[^0-9\:]/g, '');
781
782        timeArray = time.split(':');
783
784        hour = timeArray[0] ? timeArray[0].toString() : timeArray.toString();
785        minute = timeArray[1] ? timeArray[1].toString() : '';
786        second = timeArray[2] ? timeArray[2].toString() : '';
787
788        // idiot proofing
789        if (hour.length > 4) {
790          second = hour.substr(4, 2);
791        }
792        if (hour.length > 2) {
793          minute = hour.substr(2, 2);
794          hour = hour.substr(0, 2);
795        }
796        if (minute.length > 2) {
797          second = minute.substr(2, 2);
798          minute = minute.substr(0, 2);
799        }
800        if (second.length > 2) {
801          second = second.substr(2, 2);
802        }
803
804        hour = parseInt(hour, 10);
805        minute = parseInt(minute, 10);
806        second = parseInt(second, 10);
807
808        if (isNaN(hour)) {
809          hour = 0;
810        }
811        if (isNaN(minute)) {
812          minute = 0;
813        }
814        if (isNaN(second)) {
815          second = 0;
816        }
817
818        if (this.showMeridian) {
819          if (hour < 1) {
820            hour = 1;
821          } else if (hour > 12) {
822            hour = 12;
823          }
824        } else {
825          if (hour >= 24) {
826            hour = 23;
827          } else if (hour < 0) {
828            hour = 0;
829          }
830          if (hour < 13 && meridian === 'PM') {
831            hour = hour + 12;
832          }
833        }
834
835        if (minute < 0) {
836          minute = 0;
837        } else if (minute >= 60) {
838          minute = 59;
839        }
840
841        if (this.showSeconds) {
842          if (isNaN(second)) {
843            second = 0;
844          } else if (second < 0) {
845            second = 0;
846          } else if (second >= 60) {
847            second = 59;
848          }
849        }
850      }
851
852      this.hour = hour;
853      this.minute = minute;
854      this.second = second;
855      this.meridian = meridian;
856
857      this.update(ignoreWidget);
858    },
859
860    showWidget: function() {
861      if (this.isOpen) {
862        return;
863      }
864
865      if (this.$element.is(':disabled')) {
866        return;
867      }
868
869      // show/hide approach taken by datepicker
870      this.$widget.appendTo(this.appendWidgetTo);
871      var self = this;
872      $(document).on('mousedown.timepicker, touchend.timepicker', function (e) {
873        // This condition was inspired by bootstrap-datepicker.
874        // The element the timepicker is invoked on is the input but it has a sibling for addon/button.
875        if (!(self.$element.parent().find(e.target).length ||
876            self.$widget.is(e.target) ||
877            self.$widget.find(e.target).length)) {
878          self.hideWidget();
879        }
880      });
881
882      this.$element.trigger({
883        'type': 'show.timepicker',
884        'time': {
885          'value': this.getTime(),
886          'hours': this.hour,
887          'minutes': this.minute,
888          'seconds': this.second,
889          'meridian': this.meridian
890        }
891      });
892
893      this.place();
894
895      //this.$widget.popover();
896
897      if (this.disableFocus) {
898        this.$element.blur();
899      }
900
901      // widget shouldn't be empty on open
902      if (this.hour === '') {
903        if (this.defaultTime) {
904          this.setDefaultTime(this.defaultTime);
905        } else {
906          this.setTime('0:0:0');
907        }
908      }
909
910      if (this.template === 'modal' && this.$widget.modal) {
911        this.$widget.modal('show').on('hidden', $.proxy(this.hideWidget, this));
912      } else {
913        if (this.isOpen === false) {
914          this.$widget.addClass('open');
915        }
916      }
917
918      this.isOpen = true;
919    },
920
921    toggleMeridian: function() {
922      this.meridian = this.meridian === 'AM' ? 'PM' : 'AM';
923    },
924
925    update: function(ignoreWidget) {
926      this.updateElement();
927      if (!ignoreWidget) {
928        this.updateWidget();
929      }
930
931      this.$element.trigger({
932        'type': 'changeTime.timepicker',
933        'time': {
934          'value': this.getTime(),
935          'hours': this.hour,
936          'minutes': this.minute,
937          'seconds': this.second,
938          'meridian': this.meridian
939        }
940      });
941    },
942
943    updateElement: function() {
944      this.$element.val(this.getTime()).change();
945    },
946
947    updateFromElementVal: function() {
948      this.setTime(this.$element.val());
949    },
950
951    updateWidget: function() {
952      if (this.$widget === false) {
953        return;
954      }
955
956      var hour = this.hour,
957          minute = this.minute.toString().length === 1 ? '0' + this.minute : this.minute,
958          second = this.second.toString().length === 1 ? '0' + this.second : this.second;
959
960      if (this.showInputs) {
961        this.$widget.find('input.bootstrap-timepicker-hour').val(hour);
962        this.$widget.find('input.bootstrap-timepicker-minute').val(minute);
963
964        if (this.showSeconds) {
965          this.$widget.find('input.bootstrap-timepicker-second').val(second);
966        }
967        if (this.showMeridian) {
968          this.$widget.find('input.bootstrap-timepicker-meridian').val(this.meridian);
969        }
970      } else {
971        this.$widget.find('span.bootstrap-timepicker-hour').text(hour);
972        this.$widget.find('span.bootstrap-timepicker-minute').text(minute);
973
974        if (this.showSeconds) {
975          this.$widget.find('span.bootstrap-timepicker-second').text(second);
976        }
977        if (this.showMeridian) {
978          this.$widget.find('span.bootstrap-timepicker-meridian').text(this.meridian);
979        }
980      }
981    },
982
983    updateFromWidgetInputs: function() {
984      if (this.$widget === false) {
985        return;
986      }
987
988      var t = this.$widget.find('input.bootstrap-timepicker-hour').val() + ':' +
989              this.$widget.find('input.bootstrap-timepicker-minute').val() +
990              (this.showSeconds ? ':' + this.$widget.find('input.bootstrap-timepicker-second').val() : '') +
991              (this.showMeridian ? this.$widget.find('input.bootstrap-timepicker-meridian').val() : '')
992      ;
993
994      this.setTime(t, true);
995    },
996
997    widgetClick: function(e) {
998      e.stopPropagation();
999      e.preventDefault();
1000
1001      var $input = $(e.target),
1002          action = $input.closest('a').data('action');
1003
1004      if (action) {
1005        this[action]();
1006      }
1007      this.update();
1008
1009      if ($input.is('input')) {
1010        $input.get(0).setSelectionRange(0,2);
1011      }
1012    },
1013
1014    widgetKeydown: function(e) {
1015      var $input = $(e.target),
1016          name = $input.data('name');
1017
1018      switch (e.keyCode) {
1019      case 9: //tab
1020        if ((this.showMeridian && name === 'meridian') || (this.showSeconds && name === 'second') || (!this.showMeridian && !this.showSeconds && name === 'minute')) {
1021          return this.hideWidget();
1022        }
1023        break;
1024      case 27: // escape
1025        this.hideWidget();
1026        break;
1027      case 38: // up arrow
1028        e.preventDefault();
1029        switch (name) {
1030        case 'hour':
1031          this.incrementHour();
1032          break;
1033        case 'minute':
1034          this.incrementMinute();
1035          break;
1036        case 'second':
1037          this.incrementSecond();
1038          break;
1039        case 'meridian':
1040          this.toggleMeridian();
1041          break;
1042        }
1043        this.setTime(this.getTime());
1044        $input.get(0).setSelectionRange(0,2);
1045        break;
1046      case 40: // down arrow
1047        e.preventDefault();
1048        switch (name) {
1049        case 'hour':
1050          this.decrementHour();
1051          break;
1052        case 'minute':
1053          this.decrementMinute();
1054          break;
1055        case 'second':
1056          this.decrementSecond();
1057          break;
1058        case 'meridian':
1059          this.toggleMeridian();
1060          break;
1061        }
1062        this.setTime(this.getTime());
1063        $input.get(0).setSelectionRange(0,2);
1064        break;
1065      }
1066    },
1067
1068    widgetKeyup: function(e) {
1069      if ((e.keyCode === 65) || (e.keyCode === 77) || (e.keyCode === 80) || (e.keyCode === 46) || (e.keyCode === 8) || (e.keyCode >= 46 && e.keyCode <= 57)) {
1070        this.updateFromWidgetInputs();
1071      }
1072    }
1073  };
1074
1075  //TIMEPICKER PLUGIN DEFINITION
1076  $.fn.timepicker = function(option) {
1077    var args = Array.apply(null, arguments);
1078    args.shift();
1079    return this.each(function() {
1080      var $this = $(this),
1081        data = $this.data('timepicker'),
1082        options = typeof option === 'object' && option;
1083
1084      if (!data) {
1085        $this.data('timepicker', (data = new Timepicker(this, $.extend({}, $.fn.timepicker.defaults, options, $(this).data()))));
1086      }
1087
1088      if (typeof option === 'string') {
1089        data[option].apply(data, args);
1090      }
1091    });
1092  };
1093
1094  $.fn.timepicker.defaults = {
1095    defaultTime: 'current',
1096    disableFocus: false,
1097    disableMousewheel: false,
1098    isOpen: false,
1099    minuteStep: 15,
1100    modalBackdrop: false,
1101    orientation: { x: 'auto', y: 'auto'},
1102    secondStep: 15,
1103    showSeconds: false,
1104    showInputs: true,
1105    showMeridian: true,
1106    template: 'dropdown',
1107    appendWidgetTo: 'body',
1108    showWidgetOnAddonClick: true
1109  };
1110
1111  $.fn.timepicker.Constructor = Timepicker;
1112
1113})(jQuery, window, document);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.