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