1/*! 2 * jquery-timepicker v1.8.1 - A jQuery timepicker plugin inspired by Google Calendar. It supports both mouse and keyboard navigation. 3 * Copyright (c) 2015 Jon Thornton - http://jonthornton.github.com/jquery-timepicker/ 4 * License: MIT 5 */ 6 7 8(function (factory) { 9 if (typeof exports === "object" && exports && 10 typeof module === "object" && module && module.exports === exports) { 11 // Browserify. Attach to jQuery module. 12 factory(require("jquery")); 13 } else if (typeof define === 'function' && define.amd) { 14 // AMD. Register as an anonymous module. 15 define(['jquery'], factory); 16 } else { 17 // Browser globals 18 factory(jQuery); 19 } 20}(function ($) { 21 var _baseDate = _generateBaseDate(); 22 var _ONE_DAY = 86400; 23 var _lang = { 24 am: 'am', 25 pm: 'pm', 26 AM: 'AM', 27 PM: 'PM', 28 decimal: '.', 29 mins: 'mins', 30 hr: 'hr', 31 hrs: 'hrs' 32 }; 33 34 var methods = { 35 init: function(options) 36 { 37 return this.each(function() 38 { 39 var self = $(this); 40 41 // pick up settings from data attributes 42 var attributeOptions = []; 43 for (var key in $.fn.timepicker.defaults) { 44 if (self.data(key)) { 45 attributeOptions[key] = self.data(key); 46 } 47 } 48 49 var settings = $.extend({}, $.fn.timepicker.defaults, attributeOptions, options); 50 51 if (settings.lang) { 52 _lang = $.extend(_lang, settings.lang); 53 } 54 55 settings = _parseSettings(settings); 56 self.data('timepicker-settings', settings); 57 self.addClass('ui-timepicker-input'); 58 59 if (settings.useSelect) { 60 _render(self); 61 } else { 62 self.prop('autocomplete', 'off'); 63 if (settings.showOn) { 64 for (i in settings.showOn) { 65 self.on(settings.showOn[i]+'.timepicker', methods.show); 66 } 67 } 68 self.on('change.timepicker', _formatValue); 69 self.on('keydown.timepicker', _keydownhandler); 70 self.on('keyup.timepicker', _keyuphandler); 71 if (settings.disableTextInput) { 72 self.on('keypress.timepicker', function(e) { e.preventDefault(); }); 73 } 74 75 _formatValue.call(self.get(0)); 76 } 77 }); 78 }, 79 80 show: function(e) 81 { 82 var self = $(this); 83 var settings = self.data('timepicker-settings'); 84 85 if (e) { 86 e.preventDefault(); 87 } 88 89 if (settings.useSelect) { 90 self.data('timepicker-list').focus(); 91 return; 92 } 93 94 if (_hideKeyboard(self)) { 95 // block the keyboard on mobile devices 96 self.blur(); 97 } 98 99 var list = self.data('timepicker-list'); 100 101 // check if input is readonly 102 if (self.prop('readonly')) { 103 return; 104 } 105 106 // check if list needs to be rendered 107 if (!list || list.length === 0 || typeof settings.durationTime === 'function') { 108 _render(self); 109 list = self.data('timepicker-list'); 110 } 111 112 if (_isVisible(list)) { 113 return; 114 } 115 116 self.data('ui-timepicker-value', self.val()); 117 _setSelected(self, list); 118 119 // make sure other pickers are hidden 120 methods.hide(); 121 122 // position the dropdown relative to the input 123 list.show(); 124 var listOffset = {}; 125 126 if (settings.orientation.match(/r/)) { 127 // right-align the dropdown 128 listOffset.left = self.offset().left + self.outerWidth() - list.outerWidth() + parseInt(list.css('marginLeft').replace('px', ''), 10); 129 } else { 130 // left-align the dropdown 131 listOffset.left = self.offset().left + parseInt(list.css('marginLeft').replace('px', ''), 10); 132 } 133 134 var verticalOrientation; 135 if (settings.orientation.match(/t/)) { 136 verticalOrientation = 't'; 137 } else if (settings.orientation.match(/b/)) { 138 verticalOrientation = 'b'; 139 } else if ((self.offset().top + self.outerHeight(true) + list.outerHeight()) > $(window).height() + $(window).scrollTop()) { 140 verticalOrientation = 't'; 141 } else { 142 verticalOrientation = 'b'; 143 } 144 145 if (verticalOrientation == 't') { 146 // position the dropdown on top 147 list.addClass('ui-timepicker-positioned-top'); 148 listOffset.top = self.offset().top - list.outerHeight() + parseInt(list.css('marginTop').replace('px', ''), 10); 149 } else { 150 // put it under the input 151 list.removeClass('ui-timepicker-positioned-top'); 152 listOffset.top = self.offset().top + self.outerHeight() + parseInt(list.css('marginTop').replace('px', ''), 10); 153 } 154 155 list.offset(listOffset); 156 157 // position scrolling 158 var selected = list.find('.ui-timepicker-selected'); 159 160 if (!selected.length) { 161 if (_getTimeValue(self)) { 162 selected = _findRow(self, list, _time2int(_getTimeValue(self))); 163 } else if (settings.scrollDefault) { 164 selected = _findRow(self, list, settings.scrollDefault()); 165 } 166 } 167 168 if (selected && selected.length) { 169 var topOffset = list.scrollTop() + selected.position().top - selected.outerHeight(); 170 list.scrollTop(topOffset); 171 } else { 172 list.scrollTop(0); 173 } 174 175 // prevent scroll propagation 176 if(settings.stopScrollPropagation) { 177 $(document).on('wheel.ui-timepicker', '.ui-timepicker-wrapper', function(e){
178 e.preventDefault(); 179 var currentScroll = $(this).scrollTop(); 180 $(this).scrollTop(currentScroll + e.originalEvent.deltaY); 181 }); 182 } 183 184 // attach close handlers 185 $(document).on('touchstart.ui-timepicker mousedown.ui-timepicker', _closeHandler); 186 $(window).on('resize.ui-timepicker', _closeHandler); 187 if (settings.closeOnWindowScroll) { 188 $(document).on('scroll.ui-timepicker', _closeHandler); 189 } 190 191 self.trigger('showTimepicker'); 192 193 return this; 194 }, 195 196 hide: function(e) 197 { 198 var self = $(this); 199 var settings = self.data('timepicker-settings'); 200 201 if (settings && settings.useSelect) { 202 self.blur(); 203 } 204 205 $('.ui-timepicker-wrapper').each(function() { 206 var list = $(this); 207 if (!_isVisible(list)) { 208 return; 209 } 210 211 var self = list.data('timepicker-input'); 212 var settings = self.data('timepicker-settings'); 213 214 if (settings && settings.selectOnBlur) { 215 _selectValue(self); 216 } 217 218 list.hide(); 219 self.trigger('hideTimepicker'); 220 }); 221 222 return this; 223 }, 224 225 option: function(key, value) 226 { 227 return this.each(function(){ 228 var self = $(this); 229 var settings = self.data('timepicker-settings'); 230 var list = self.data('timepicker-list'); 231 232 if (typeof key == 'object') { 233 settings = $.extend(settings, key); 234 235 } else if (typeof key == 'string' && typeof value != 'undefined') { 236 settings[key] = value; 237 238 } else if (typeof key == 'string') { 239 return settings[key]; 240 } 241 242 settings = _parseSettings(settings); 243 244 self.data('timepicker-settings', settings); 245 246 if (list) { 247 list.remove(); 248 self.data('timepicker-list', false); 249 } 250 251 if (settings.useSelect) { 252 _render(self); 253 } 254 }); 255 }, 256 257 getSecondsFromMidnight: function() 258 { 259 return _time2int(_getTimeValue(this)); 260 }, 261 262 getTime: function(relative_date) 263 { 264 var self = this; 265 266 var time_string = _getTimeValue(self); 267 if (!time_string) { 268 return null; 269 } 270 271 var offset = _time2int(time_string); 272 if (offset === null) { 273 return null; 274 } 275 276 if (!relative_date) { 277 relative_date = new Date(); 278 } 279 280 // construct a Date with today's date, and offset's time 281 var time = new Date(relative_date); 282 time.setHours(offset / 3600); 283 time.setMinutes(offset % 3600 / 60); 284 time.setSeconds(offset % 60); 285 time.setMilliseconds(0); 286 287 return time; 288 }, 289 290 setTime: function(value) 291 { 292 var self = this; 293 var settings = self.data('timepicker-settings'); 294 295 if (settings.forceRoundTime) { 296 var prettyTime = _roundAndFormatTime(_time2int(value), settings) 297 } else { 298 var prettyTime = _int2time(_time2int(value), settings); 299 } 300 301 if (value && prettyTime === null && settings.noneOption) { 302 prettyTime = value; 303 } 304 305 _setTimeValue(self, prettyTime); 306 if (self.data('timepicker-list')) { 307 _setSelected(self, self.data('timepicker-list')); 308 } 309 310 return this; 311 }, 312 313 remove: function() 314 { 315 var self = this; 316 317 // check if this element is a timepicker 318 if (!self.hasClass('ui-timepicker-input')) { 319 return; 320 } 321 322 var settings = self.data('timepicker-settings'); 323 self.removeAttr('autocomplete', 'off'); 324 self.removeClass('ui-timepicker-input'); 325 self.removeData('timepicker-settings'); 326 self.off('.timepicker'); 327 328 // timepicker-list won't be present unless the user has interacted with this timepicker 329 if (self.data('timepicker-list')) { 330 self.data('timepicker-list').remove(); 331 } 332 333 if (settings.useSelect) { 334 self.show(); 335 } 336 337 self.removeData('timepicker-list'); 338 339 return this; 340 } 341 }; 342 343 // private methods 344 345 function _isVisible(elem) 346 { 347 var el = elem[0]; 348 return el.offsetWidth > 0 && el.offsetHeight > 0; 349 } 350 351 function _parseSettings(settings) 352 { 353 if (settings.minTime) {
354 settings.minTime = _time2int(settings.minTime); 355 } 356 357 if (settings.maxTime) { 358 settings.maxTime = _time2int(settings.maxTime); 359 } 360 361 if (settings.durationTime && typeof settings.durationTime !== 'function') { 362 settings.durationTime = _time2int(settings.durationTime); 363 } 364 365 if (settings.scrollDefault == 'now') { 366 settings.scrollDefault = function() { 367 return settings.roundingFunction(_time2int(new Date()), settings); 368 } 369 } else if (settings.scrollDefault && typeof settings.scrollDefault != 'function') { 370 var val = settings.scrollDefault; 371 settings.scrollDefault = function() { 372 return settings.roundingFunction(_time2int(val), settings); 373 } 374 } else if (settings.minTime) { 375 settings.scrollDefault = function() { 376 return settings.roundingFunction(settings.minTime, settings); 377 } 378 } 379 380 if ($.type(settings.timeFormat) === "string" && settings.timeFormat.match(/[gh]/)) { 381 settings._twelveHourTime = true; 382 } 383 384 if (settings.showOnFocus === false && settings.showOn.indexOf('focus') != -1) { 385 settings.showOn.splice(settings.showOn.indexOf('focus'), 1); 386 } 387 388 if (settings.disableTimeRanges.length > 0) { 389 // convert string times to integers 390 for (var i in settings.disableTimeRanges) { 391 settings.disableTimeRanges[i] = [ 392 _time2int(settings.disableTimeRanges[i][0]), 393 _time2int(settings.disableTimeRanges[i][1]) 394 ]; 395 } 396 397 // sort by starting time 398 settings.disableTimeRanges = settings.disableTimeRanges.sort(function(a, b){ 399 return a[0] - b[0]; 400 }); 401 402 // merge any overlapping ranges 403 for (var i = settings.disableTimeRanges.length-1; i > 0; i--) { 404 if (settings.disableTimeRanges[i][0] <= settings.disableTimeRanges[i-1][1]) { 405 settings.disableTimeRanges[i-1] = [ 406 Math.min(settings.disableTimeRanges[i][0], settings.disableTimeRanges[i-1][0]), 407 Math.max(settings.disableTimeRanges[i][1], settings.disableTimeRanges[i-1][1]) 408 ]; 409 settings.disableTimeRanges.splice(i, 1); 410 } 411 } 412 } 413 414 return settings; 415 } 416 417 function _render(self) 418 { 419 var settings = self.data('timepicker-settings'); 420 var list = self.data('timepicker-list'); 421 422 if (list && list.length) { 423 list.remove(); 424 self.data('timepicker-list', false); 425 } 426 427 if (settings.useSelect) { 428 list = $('<select />', { 'class': 'ui-timepicker-select' }); 429 var wrapped_list = list; 430 } else { 431 list = $('<ul />', { 'class': 'ui-timepicker-list' }); 432 433 var wrapped_list = $('<div />', { 'class': 'ui-timepicker-wrapper', 'tabindex': -1 }); 434 wrapped_list.css({'display':'none', 'position': 'absolute' }).append(list); 435 } 436 437 if (settings.noneOption) { 438 if (settings.noneOption === true) { 439 settings.noneOption = (settings.useSelect) ? 'Time...' : 'None'; 440 } 441 442 if ($.isArray(settings.noneOption)) { 443 for (var i in settings.noneOption) { 444 if (parseInt(i, 10) == i){ 445 var noneElement = _generateNoneElement(settings.noneOption[i], settings.useSelect); 446 list.append(noneElement); 447 } 448 } 449 } else { 450 var noneElement = _generateNoneElement(settings.noneOption, settings.useSelect); 451 list.append(noneElement); 452 } 453 } 454 455 if (settings.className) { 456 wrapped_list.addClass(settings.className); 457 } 458 459 if ((settings.minTime !== null || settings.durationTime !== null) && settings.showDuration) { 460 var stepval = typeof settings.step == 'function' ? 'function' : settings.step; 461 wrapped_list.addClass('ui-timepicker-with-duration'); 462 wrapped_list.addClass('ui-timepicker-step-'+settings.step); 463 } 464 465 var durStart = settings.minTime; 466 if (typeof settings.durationTime === 'function') {
467 durStart = _time2int(settings.durationTime()); 468 } else if (settings.durationTime !== null) { 469 durStart = settings.durationTime; 470 } 471 var start = (settings.minTime !== null) ? settings.minTime : 0; 472 var end = (settings.maxTime !== null) ? settings.maxTime : (start + _ONE_DAY - 1); 473 474 if (end < start) { 475 // make sure the end time is greater than start time, otherwise there will be no list to show 476 end += _ONE_DAY; 477 } 478 479 if (end === _ONE_DAY-1 && $.type(settings.timeFormat) === "string" && settings.show2400) { 480 // show a 24:00 option when using military time 481 end = _ONE_DAY; 482 } 483 484 var dr = settings.disableTimeRanges; 485 var drCur = 0; 486 var drLen = dr.length; 487 488 var stepFunc = settings.step; 489 if (typeof stepFunc != 'function') { 490 stepFunc = function() { 491 return settings.step; 492 } 493 } 494 495 for (var i=start, j=0; i <= end; j++, i += stepFunc(j)*60) { 496 var timeInt = i; 497 var timeString = _int2time(timeInt, settings); 498 499 if (settings.useSelect) { 500 var row = $('<option />', { 'value': timeString }); 501 row.text(timeString); 502 } else { 503 var row = $('<li />'); 504 row.data('time', (timeInt <= 86400 ? timeInt : timeInt % 86400)); 505 row.text(timeString); 506 } 507 508 if ((settings.minTime !== null || settings.durationTime !== null) && settings.showDuration) { 509 var durationString = _int2duration(i - durStart, settings.step); 510 if (settings.useSelect) { 511 row.text(row.text()+' ('+durationString+')'); 512 } else { 513 var duration = $('<span />', { 'class': 'ui-timepicker-duration' }); 514 duration.text(' ('+durationString+')'); 515 row.append(duration); 516 } 517 } 518 519 if (drCur < drLen) { 520 if (timeInt >= dr[drCur][1]) { 521 drCur += 1; 522 } 523 524 if (dr[drCur] && timeInt >= dr[drCur][0] && timeInt < dr[drCur][1]) { 525 if (settings.useSelect) { 526 row.prop('disabled', true); 527 } else { 528 row.addClass('ui-timepicker-disabled'); 529 } 530 } 531 } 532 533 list.append(row); 534 } 535 536 wrapped_list.data('timepicker-input', self); 537 self.data('timepicker-list', wrapped_list); 538 539 if (settings.useSelect) { 540 if (self.val()) { 541 list.val(_roundAndFormatTime(_time2int(self.val()), settings)); 542 } 543 544 list.on('focus', function(){ 545 $(this).data('timepicker-input').trigger('showTimepicker'); 546 }); 547 list.on('blur', function(){ 548 $(this).data('timepicker-input').trigger('hideTimepicker'); 549 }); 550 list.on('change', function(){ 551 _setTimeValue(self, $(this).val(), 'select'); 552 }); 553 554 _setTimeValue(self, list.val(), 'initial'); 555 self.hide().after(list); 556 } else { 557 var appendTo = settings.appendTo; 558 if (typeof appendTo === 'string') { 559 appendTo = $(appendTo); 560 } else if (typeof appendTo === 'function') { 561 appendTo = appendTo(self); 562 } 563 appendTo.append(wrapped_list); 564 _setSelected(self, list); 565 566 list.on('mousedown', 'li', function(e) { 567 568 // hack: temporarily disable the focus handler 569 // to deal with the fact that IE fires 'focus' 570 // events asynchronously 571 self.off('focus.timepicker'); 572 self.on('focus.timepicker-ie-hack', function(){ 573 self.off('focus.timepicker-ie-hack'); 574 self.on('focus.timepicker', methods.show); 575 }); 576 577 if (!_hideKeyboard(self)) { 578 self[0].focus(); 579 } 580 581 // make sure only the clicked row is selected 582 list.find('li').removeClass('ui-timepicker-selected'); 583 $(this).addClass('ui-timepicker-selected'); 584 585 if (_selectValue(self)) { 586 self.trigger('hideTimepicker'); 587 588 list.on('mouseup.timepicker', 'li', function(e) { 589 list.off('mouseup.timepicker'); 590 wrapped_list.hide(); 591 }); 592 } 593 }); 594 } 595 } 596 597 function _generateNoneElement(optionValue, useSelect) 598 { 599 var label, className, value; 600 601 if (typeof optionValue == 'object') { 602 label = optionValue.label; 603 className = optionValue.className; 604 value = optionValue.value; 605 } else if (typeof optionValue == 'string') { 606 label = optionValue; 607 } else { 608 $.error('Invalid noneOption value'); 609 } 610 611 if (useSelect) { 612 return $('<option />', { 613 'value': value, 614 'class': className, 615 'text': label 616 }); 617 } else { 618 return $('<li />', { 619 'class': className, 620 'text': label 621 }).data('time', value); 622 } 623 } 624 625 function _roundAndFormatTime(seconds, settings) 626 { 627 seconds = settings.roundingFunction(seconds, settings); 628 if (seconds !== null) { 629 return _int2time(seconds, settings); 630 } 631 } 632 633 function _generateBaseDate() 634 { 635 return new Date(1970, 1, 1, 0, 0, 0); 636 } 637 638 // event handler to decide whether to close timepicker 639 function _closeHandler(e) 640 { 641 var target = $(e.target); 642 var input = target.closest('.ui-timepicker-input'); 643 if (input.length === 0 && target.closest('.ui-timepicker-wrapper').length === 0) { 644 methods.hide(); 645 $(document).unbind('.ui-timepicker'); 646 $(window).unbind('.ui-timepicker'); 647 } 648 } 649 650 function _hideKeyboard(self) 651 { 652 var settings = self.data('timepicker-settings'); 653 return ((window.navigator.msMaxTouchPoints || 'ontouchstart' in document) && settings.disableTouchKeyboard); 654 } 655 656 function _findRow(self, list, value) 657 { 658 if (!value && value !== 0) { 659 return false; 660 } 661 662 var settings = self.data('timepicker-settings'); 663 var out = false; 664 var value = settings.roundingFunction(value, settings); 665 666 // loop through the menu items 667 list.find('li').each(function(i, obj) { 668 var jObj = $(obj); 669 if (typeof jObj.data('time') != 'number') { 670 return; 671 } 672 673 if (jObj.data('time') == value) { 674 out = jObj; 675 return false; 676 } 677 }); 678 679 return out; 680 } 681 682 function _setSelected(self, list) 683 { 684 list.find('li').removeClass('ui-timepicker-selected'); 685 686 var timeValue = _time2int(_getTimeValue(self), self.data('timepicker-settings')); 687 if (timeValue === null) { 688 return; 689 } 690 691 var selected = _findRow(self, list, timeValue); 692 if (selected) { 693 694 var topDelta = selected.offset().top - list.offset().top; 695 696 if (topDelta + selected.outerHeight() > list.outerHeight() || topDelta < 0) { 697 list.scrollTop(list.scrollTop() + selected.position().top - selected.outerHeight()); 698 } 699 700 selected.addClass('ui-timepicker-selected'); 701 } 702 } 703 704 705 function _formatValue(e, origin) 706 { 707 if (this.value === '' || origin == 'timepicker') { 708 return; 709 } 710 711 var self = $(this); 712 713 if (self.is(':focus') && (!e || e.type != 'change')) { 714 return; 715 } 716 717 var settings = self.data('timepicker-settings'); 718 var seconds = _time2int(this.value, settings); 719 720 if (seconds === null) { 721 self.trigger('timeFormatError'); 722 return; 723 } 724 725 var rangeError = false;
726 // check that the time in within bounds 727 if (settings.minTime !== null && seconds < settings.minTime) { 728 rangeError = true; 729 } else if (settings.maxTime !== null && seconds > settings.maxTime) { 730 rangeError = true; 731 } 732 733 // check that time isn't within disabled time ranges 734 $.each(settings.disableTimeRanges, function(){ 735 if (seconds >= this[0] && seconds < this[1]) { 736 rangeError = true; 737 return false; 738 } 739 }); 740 741 if (settings.forceRoundTime) { 742 seconds = settings.roundingFunction(seconds, settings); 743 } 744 745 var prettyTime = _int2time(seconds, settings); 746 747 if (rangeError) { 748 if (_setTimeValue(self, prettyTime, 'error')) { 749 self.trigger('timeRangeError'); 750 } 751 } else { 752 _setTimeValue(self, prettyTime); 753 } 754 } 755 756 function _getTimeValue(self) 757 { 758 if (self.is('input')) { 759 return self.val(); 760 } else { 761 // use the element's data attributes to store values 762 return self.data('ui-timepicker-value'); 763 } 764 } 765 766 function _setTimeValue(self, value, source) 767 { 768 if (self.is('input')) { 769 self.val(value); 770 771 var settings = self.data('timepicker-settings'); 772 if (settings.useSelect && source != 'select' && source != 'initial') { 773 self.data('timepicker-list').val(_roundAndFormatTime(_time2int(value), settings)); 774 } 775 } 776 777 if (self.data('ui-timepicker-value') != value) { 778 self.data('ui-timepicker-value', value); 779 if (source == 'select') { 780 self.trigger('selectTime').trigger('changeTime').trigger('change', 'timepicker'); 781 } else if (source != 'error') { 782 self.trigger('changeTime'); 783 } 784 785 return true; 786 } else { 787 self.trigger('selectTime'); 788 return false; 789 } 790 } 791 792 /* 793 * Keyboard navigation via arrow keys 794 */ 795 function _keydownhandler(e) 796 { 797 var self = $(this); 798 var list = self.data('timepicker-list'); 799 800 if (!list || !_isVisible(list)) { 801 if (e.keyCode == 40) { 802 // show the list! 803 methods.show.call(self.get(0)); 804 list = self.data('timepicker-list'); 805 if (!_hideKeyboard(self)) { 806 self.focus(); 807 } 808 } else { 809 return true; 810 } 811 } 812 813 switch (e.keyCode) { 814 815 case 13: // return 816 if (_selectValue(self)) { 817 methods.hide.apply(this); 818 } 819 820 e.preventDefault(); 821 return false; 822 823 case 38: // up 824 var selected = list.find('.ui-timepicker-selected'); 825 826 if (!selected.length) { 827 list.find('li').each(function(i, obj) { 828 if ($(obj).position().top > 0) { 829 selected = $(obj); 830 return false; 831 } 832 }); 833 selected.addClass('ui-timepicker-selected'); 834 835 } else if (!selected.is(':first-child')) { 836 selected.removeClass('ui-timepicker-selected'); 837 selected.prev().addClass('ui-timepicker-selected'); 838 839 if (selected.prev().position().top < selected.outerHeight()) { 840 list.scrollTop(list.scrollTop() - selected.outerHeight()); 841 } 842 } 843 844 return false; 845 846 case 40: // down 847 selected = list.find('.ui-timepicker-selected'); 848 849 if (selected.length === 0) { 850 list.find('li').each(function(i, obj) { 851 if ($(obj).position().top > 0) { 852 selected = $(obj); 853 return false; 854 } 855 }); 856 857 selected.addClass('ui-timepicker-selected'); 858 } else if (!selected.is(':last-child')) { 859 selected.removeClass('ui-timepicker-selected'); 860 selected.next().addClass('ui-timepicker-selected'); 861 862 if (selected.next().position().top + 2*selected.outerHeight() > list.outerHeight()) { 863 list.scrollTop(list.scrollTop() + selected.outerHeight()); 864 } 865 } 866 867 return false; 868 869 case 27: // escape 870 list.find('li').removeClass('ui-timepicker-selected'); 871 methods.hide(); 872 break; 873 874 case 9: //tab 875 methods.hide(); 876 break; 877 878 default: 879 return true; 880 } 881 } 882 883 /* 884 * Time typeahead 885 */ 886 function _keyuphandler(e) 887 { 888 var self = $(this); 889 var list = self.data('timepicker-list'); 890 var settings = self.data('timepicker-settings'); 891 892 if (!list || !_isVisible(list) || settings.disableTextInput) { 893 return true; 894 } 895 896 switch (e.keyCode) { 897 898 case 96: // numpad numerals 899 case 97: 900 case 98: 901 case 99: 902 case 100: 903 case 101: 904 case 102: 905 case 103: 906 case 104: 907 case 105: 908 case 48: // numerals 909 case 49: 910 case 50: 911 case 51: 912 case 52: 913 case 53: 914 case 54: 915 case 55: 916 case 56: 917 case 57: 918 case 65: // a 919 case 77: // m 920 case 80: // p 921 case 186: // colon 922 case 8: // backspace 923 case 46: // delete 924 if (settings.typeaheadHighlight) { 925 _setSelected(self, list); 926 } else { 927 list.hide(); 928 } 929 break; 930 } 931 } 932 933 function _selectValue(self) 934 { 935 var settings = self.data('timepicker-settings'); 936 var list = self.data('timepicker-list'); 937 var timeValue = null; 938 939 var cursor = list.find('.ui-timepicker-selected'); 940 941 if (cursor.hasClass('ui-timepicker-disabled')) { 942 return false; 943 } 944 945 if (cursor.length) { 946 // selected value found
947 timeValue = cursor.data('time'); 948 } 949 950 if (timeValue !== null) { 951 if (typeof timeValue != 'string') { 952 timeValue = _int2time(timeValue, settings); 953 } 954 955 _setTimeValue(self, timeValue, 'select'); 956 } 957 958 return true; 959 } 960 961 function _int2duration(seconds, step) 962 { 963 seconds = Math.abs(seconds); 964 var minutes = Math.round(seconds/60), 965 duration = [], 966 hours, mins; 967 968 if (minutes < 60) { 969 // Only show (x mins) under 1 hour 970 duration = [minutes, _lang.mins]; 971 } else { 972 hours = Math.floor(minutes/60); 973 mins = minutes%60; 974 975 // Show decimal notation (eg: 1.5 hrs) for 30 minute steps 976 if (step == 30 && mins == 30) { 977 hours += _lang.decimal + 5; 978 } 979 980 duration.push(hours); 981 duration.push(hours == 1 ? _lang.hr : _lang.hrs); 982 983 // Show remainder minutes notation (eg: 1 hr 15 mins) for non-30 minute steps 984 // and only if there are remainder minutes to show 985 if (step != 30 && mins) { 986 duration.push(mins); 987 duration.push(_lang.mins);
988 } 989 } 990 991 return duration.join(' '); 992 } 993 994 function _int2time(seconds, settings) 995 { 996 if (seconds === null) { 997 return null; 998 } 999 1000 var time = new Date(_baseDate.valueOf() + (seconds*1000)); 1001 1002 if (isNaN(time.getTime())) { 1003 return null; 1004 } 1005 1006 if ($.type(settings.timeFormat) === "function") { 1007 return settings.timeFormat(time); 1008 } 1009 1010 var output = ''; 1011 var hour, code; 1012 for (var i=0; i<settings.timeFormat.length; i++) { 1013 1014 code = settings.timeFormat.charAt(i); 1015 switch (code) { 1016 1017 case 'a': 1018 output += (time.getHours() > 11) ? _lang.pm : _lang.am; 1019 break; 1020 1021 case 'A': 1022 output += (time.getHours() > 11) ? _lang.PM : _lang.AM; 1023 break; 1024 1025 case 'g': 1026 hour = time.getHours() % 12; 1027 output += (hour === 0) ? '12' : hour; 1028 break; 1029 1030 case 'G': 1031 hour = time.getHours(); 1032 if (seconds === _ONE_DAY) hour = 24; 1033 output += hour; 1034 break; 1035 1036 case 'h': 1037 hour = time.getHours() % 12; 1038 1039 if (hour !== 0 && hour < 10) { 1040 hour = '0'+hour; 1041 } 1042 1043 output += (hour === 0) ? '12' : hour; 1044 break; 1045 1046 case 'H': 1047 hour = time.getHours(); 1048 if (seconds === _ONE_DAY) hour = 24; 1049 output += (hour > 9) ? hour : '0'+hour; 1050 break; 1051 1052 case 'i': 1053 var minutes = time.getMinutes(); 1054 output += (minutes > 9) ? minutes : '0'+minutes; 1055 break; 1056 1057 case 's': 1058 seconds = time.getSeconds(); 1059 output += (seconds > 9) ? seconds : '0'+seconds; 1060 break; 1061 1062 case '\\': 1063 // escape character; add the next character and skip ahead 1064 i++; 1065 output += settings.timeFormat.charAt(i); 1066 break; 1067 1068 default: 1069 output += code; 1070 } 1071 } 1072 1073 return output; 1074 } 1075 1076 function _time2int(timeString, settings) 1077 { 1078 if (timeString === '') return null; 1079 if (!timeString || timeString+0 == timeString) return timeString; 1080 1081 if (typeof(timeString) == 'object') { 1082 return timeString.getHours()*3600 + timeString.getMinutes()*60 + timeString.getSeconds(); 1083 } 1084 1085 timeString = timeString.toLowerCase().replace(/[\s\.]/g, ''); 1086 1087 // if the last character is an "a" or "p", add the "m" 1088 if (timeString.slice(-1) == 'a' || timeString.slice(-1) == 'p') { 1089 timeString += 'm'; 1090 } 1091 1092 var ampmRegex = '(' + 1093 _lang.am.replace('.', '')+'|' + 1094 _lang.pm.replace('.', '')+'|' + 1095 _lang.AM.replace('.', '')+'|' + 1096 _lang.PM.replace('.', '')+')?'; 1097 1098 // try to parse time input 1099 var pattern = new RegExp('^'+ampmRegex+'([0-2]?[0-9])\\W?([0-5][0-9])?\\W?([0-5][0-9])?'+ampmRegex+'$'); 1100
1101 var time = timeString.match(pattern); 1102 if (!time) { 1103 return null; 1104 } 1105 1106 var hour = parseInt(time[2]*1, 10); 1107 var ampm = time[1] || time[5]; 1108 var hours = hour; 1109 1110 if (hour <= 12 && ampm) { 1111 var isPm = (ampm == _lang.pm || ampm == _lang.PM); 1112 1113 if (hour == 12) { 1114 hours = isPm ? 12 : 0; 1115 } else { 1116 hours = (hour + (isPm ? 12 : 0)); 1117 } 1118 } 1119 1120 var minutes = ( time[3]*1 || 0 ); 1121 var seconds = ( time[4]*1 || 0 ); 1122 var timeInt = hours*3600 + minutes*60 + seconds; 1123 1124 // if no am/pm provided, intelligently guess based on the scrollDefault 1125 if (!ampm && settings && settings._twelveHourTime && settings.scrollDefault) { 1126 var delta = timeInt - settings.scrollDefault(); 1127 if (delta < 0 && delta >= _ONE_DAY / -2) { 1128 timeInt = (timeInt + (_ONE_DAY / 2)) % _ONE_DAY; 1129 } 1130 } 1131 1132 return timeInt; 1133 } 1134 1135 function _pad2(n) { 1136 return ("0" + n).slice(-2); 1137 } 1138 1139 // Plugin entry 1140 $.fn.timepicker = function(method) 1141 { 1142 if (!this.length) return this; 1143 if (methods[method]) { 1144 // check if this element is a timepicker 1145 if (!this.hasClass('ui-timepicker-input')) { 1146 return this; 1147 } 1148 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 1149 } 1150 else if(typeof method === "object" || !method) { return methods.init.apply(this, arguments); } 1151 else { $.error("Method "+ method + " does not exist on jQuery.timepicker"); } 1152 }; 1153 // Global defaults 1154 $.fn.timepicker.defaults = { 1155 className: null, 1156 minTime: null, 1157 maxTime: null, 1158 durationTime: null, 1159 step: 30, 1160 showDuration: false, 1161 showOnFocus: true, 1162 showOn: ['click', 'focus'], 1163 timeFormat: 'g:ia', 1164 scrollDefault: null, 1165 selectOnBlur: false, 1166 disableTextInput: false, 1167 disableTouchKeyboard: false, 1168 forceRoundTime: false, 1169 roundingFunction: function(seconds, settings) { 1170 if (seconds === null) { 1171 return null; 1172 } else { 1173 var offset = seconds % (settings.step*60); // step is in minutes 1174 1175 if (offset >= settings.step*30) { 1176 // if offset is larger than a half step, round up 1177 seconds += (settings.step*60) - offset; 1178 } else { 1179 // round down 1180 seconds -= offset; 1181 } 1182 1183 return seconds; 1184 } 1185 }, 1186 appendTo: 'body', 1187 orientation: 'l', 1188 disableTimeRanges: [], 1189 closeOnWindowScroll: false, 1190 typeaheadHighlight: true, 1191 noneOption: false, 1192 show2400: false, 1193 stopScrollPropagation: false 1194 }; 1195}));
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.