PluginProbe
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services / 8.7.3
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services v8.7.3
8.7.6 8.7.5 8.7.4 8.7.3 8.7.2 8.7.1 8.7.0 8.6.9 8.6.8 8.6.7 8.6.6 8.6.5 8.6.4 8.6.2 8.6.1 8.6.0 8.5.9 8.5.8 8.5.7 8.5.6 8.5.5 8.5.4 8.5.3 8.5.2 8.5.0 All 532 releases
chatbot / js / jquery.timepicker.js

jquery.timepicker.js in WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services 8.7.3, at js/jquery.timepicker.js

1,384 lines 36.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 />", { 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 });
1384