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