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"> </td>'+ 260 '<td><a href="#" data-action="incrementMinute"><i class="glyphicon glyphicon-chevron-up"></i></a></td>'+ 261 (this.showSeconds ? 262 '<td class="separator"> </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"> </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"> </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"> </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"> </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.