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