-/*
- * Copyright (c) 2006 Sam Collett (http://www.texotela.co.uk)
- * Licensed under the MIT License:
- * http://www.opensource.org/licenses/mit-license.php
- */
-
+/** https://github.com/perifer/timePicker - b5195df3 */
/*
* A time picker for jQuery
- * Based on original timePicker by Sam Collet (http://www.texotela.co.uk)
+ *
+ * Dual licensed under the MIT and GPL licenses.
+ * Copyright (c) 2009 Anders Fajerson
* @name timePicker
- * @version 0.1
* @author Anders Fajerson (http://perifer.se)
* @example $("#mytime").timePicker();
- * @example $("#mytime").timePicker({step:30, startTime:"15:00", endTime:"18:00"});
+ * @example $("#mytime").timePicker({step:30, startTime:"15:00", endTime:"18:00"});
+ *
+ * Based on timePicker by Sam Collet (http://www.texotela.co.uk/code/jquery/timepicker/)
+ *
+ * Options:
+ * step: # of minutes to step the time by
+ * startTime: beginning of the range of acceptable times
+ * endTime: end of the range of acceptable times
+ * separator: separator string to use between hours and minutes (e.g. ':')
+ * show24Hours: use a 24-hour scheme
*/
-;if (window.jQuery)
+
(function($){
-
$.fn.timePicker = function(options) {
// Build main options before element iteration
- var settings = $.extend({}, $.fn.timePicker.defaults, options);
-
+ var settings = $.extend({}, $.fn.timePicker.defaults, options);
+
return this.each(function() {
$.timePicker(this, settings);
});
};
-
+
$.timePicker = function (elm, settings) {
- var elm = $(elm)[0];
- return elm.timePicker || (elm.timePicker = new jQuery._timePicker(elm, settings));
+ var e = $(elm)[0];
+ return e.timePicker || (e.timePicker = new jQuery._timePicker(e, settings));
};
-
+
+ $.timePicker.version = '0.3';
+
$._timePicker = function(elm, settings) {
-
+
var tpOver = false;
- var startTime = normaliseTime(settings.startTime);
- var endTime = normaliseTime(settings.endTime);
-
+ var keyDown = false;
+ var startTime = timeToDate(settings.startTime, settings);
+ var endTime = timeToDate(settings.endTime, settings);
+ var selectedClass = "selected";
+ var selectedSelector = "li." + selectedClass;
+
$(elm).attr('autocomplete', 'OFF'); // Disable browser autocomplete
-
+
var times = [];
var time = new Date(startTime); // Create a new date object.
while(time <= endTime) {
var $tpDiv = $('<div class="time-picker'+ (settings.show24Hours ? '' : ' time-picker-12hours') +'"></div>');
var $tpList = $('<ul></ul>');
-
+
// Build the list.
for(var i = 0; i < times.length; i++) {
$tpList.append("<li>" + times[i] + "</li>");
}
$tpDiv.append($tpList);
- // Store element offset.
- var elmOffset = $(elm).offset();
// Append the timPicker to the body and position it.
- $tpDiv.appendTo('body').css({'top':elmOffset.top, 'left':elmOffset.left}).hide();
-
- $("li", $tpList).unbind().mouseover(function() {
- $("li.selected", $tpDiv).removeClass("selected"); // TODO: only needs to run once.
- $(this).addClass("selected");
+ $tpDiv.appendTo('body').hide();
+
+ // Store the mouse state, used by the blur event. Use mouseover instead of
+ // mousedown since Opera fires blur before mousedown.
+ $tpDiv.mouseover(function() {
+ tpOver = true;
+ }).mouseout(function() {
+ tpOver = false;
+ });
+
+ $("li", $tpList).mouseover(function() {
+ if (!keyDown) {
+ $(selectedSelector, $tpDiv).removeClass(selectedClass);
+ $(this).addClass(selectedClass);
+ }
}).mousedown(function() {
tpOver = true;
}).click(function() {
setTimeVal(elm, this, $tpDiv, settings);
tpOver = false;
});
-
- // Store ananymous function in variable since it's used twice.
+
var showPicker = function() {
- var elmOffset = $(elm).offset();
- $tpDiv.css({'top':elmOffset.top, 'left':elmOffset.left}).show(); // Show picker.
- $tpDiv.mouseover(function() { // Have to use mouseover instead of mousedown because of Opera
- tpOver = true;
- }).mouseout(function() {
- tpOver = false;
- });
- $("li", $tpDiv).removeClass("selected");
-
+ if ($tpDiv.is(":visible")) {
+ return false;
+ }
+ $("li", $tpDiv).removeClass(selectedClass);
+
+ // Position
+ var elmOffset = $(elm).offset();
+ $tpDiv.css({'top':elmOffset.top + elm.offsetHeight, 'left':elmOffset.left});
+
+ // Show picker. This has to be done before scrollTop is set since that
+ // can't be done on hidden elements.
+ $tpDiv.show();
+
// Try to find a time in the list that matches the entered time.
- var time = this.value ? timeStringToDate(this.value, settings) : startTime;
+ var time = elm.value ? timeStringToDate(elm.value, settings) : startTime;
var startMin = startTime.getHours() * 60 + startTime.getMinutes();
var min = (time.getHours() * 60 + time.getMinutes()) - startMin;
var steps = Math.round(min / settings.step);
- var roundTime = normaliseTime(new Date(2001, 0, 0, 0, (steps * settings.step + (startMin)), 0));
- roundTime = (startTime < roundTime && roundTime < endTime) ? roundTime : startTime;
-
+ var roundTime = normaliseTime(new Date(0, 0, 0, 0, (steps * settings.step + startMin), 0));
+ roundTime = (startTime < roundTime && roundTime <= endTime) ? roundTime : startTime;
var $matchedTime = $("li:contains(" + formatTime(roundTime, settings) + ")", $tpDiv);
-
+
if ($matchedTime.length) {
- $matchedTime.addClass("selected");
+ $matchedTime.addClass(selectedClass);
// Scroll to matched time.
$tpDiv[0].scrollTop = $matchedTime[0].offsetTop;
}
+ return true;
};
-
- $(elm).unbind().focus(showPicker).click(showPicker)
+ // Attach to click as well as focus so timePicker can be shown again when
+ // clicking on the input when it already has focus.
+ $(elm).focus(showPicker).click(showPicker);
// Hide timepicker on blur
- .blur(function() {
- if (!tpOver && $tpDiv[0].parentNode) { // Don't remove when timePicker is clicked or when already removed
+ $(elm).blur(function() {
+ if (!tpOver) {
$tpDiv.hide();
}
- })
-
- // Key support
- .keypress(function(e) {
+ });
+ // Keypress doesn't repeat on Safari for non-text keys.
+ // Keydown doesn't repeat on Firefox and Opera on Mac.
+ // Using kepress for Opera and Firefox and keydown for the rest seems to
+ // work with up/down/enter/esc.
+ var event = ($.browser.opera || $.browser.mozilla) ? 'keypress' : 'keydown';
+ $(elm)[event](function(e) {
+ var $selected;
+ keyDown = true;
+ var top = $tpDiv[0].scrollTop;
switch (e.keyCode) {
case 38: // Up arrow.
- case 63232: // Safari up arrow.
- var $selected = $("li.selected", $tpList);
- var prev = $selected.prev().addClass("selected")[0];
+ // Just show picker if it's hidden.
+ if (showPicker()) {
+ return false;
+ };
+ $selected = $(selectedSelector, $tpList);
+ var prev = $selected.prev().addClass(selectedClass)[0];
if (prev) {
- $selected.removeClass("selected");
- $tpDiv[0].scrollTop = prev.offsetTop;
+ $selected.removeClass(selectedClass);
+ // Scroll item into view.
+ if (prev.offsetTop < top) {
+ $tpDiv[0].scrollTop = top - prev.offsetHeight;
+ }
+ }
+ else {
+ // Loop to next item.
+ $selected.removeClass(selectedClass);
+ prev = $("li:last", $tpList).addClass(selectedClass)[0];
+ $tpDiv[0].scrollTop = prev.offsetTop - prev.offsetHeight;
}
return false;
break;
- case 40: // Down arrow.
- case 63233: // Safari down arrow.
- var $selected = $("li.selected", $tpList);
- var next = $selected.length ? $selected.next().addClass("selected")[0] : $("li:first").addClass("selected")[0];
+ case 40: // Down arrow, similar in behaviour to up arrow.
+ if (showPicker()) {
+ return false;
+ };
+ $selected = $(selectedSelector, $tpList);
+ var next = $selected.next().addClass(selectedClass)[0];
if (next) {
- $selected.removeClass("selected");
- $tpDiv[0].scrollTop = next.offsetTop;
+ $selected.removeClass(selectedClass);
+ if (next.offsetTop + next.offsetHeight > top + $tpDiv[0].offsetHeight) {
+ $tpDiv[0].scrollTop = top + next.offsetHeight;
+ }
+ }
+ else {
+ $selected.removeClass(selectedClass);
+ next = $("li:first", $tpList).addClass(selectedClass)[0];
+ $tpDiv[0].scrollTop = 0;
}
return false;
break;
case 13: // Enter
- if (!$tpDiv.is(":hidden")) {
- var sel = $("li.selected", $tpList)[0];
+ if ($tpDiv.is(":visible")) {
+ var sel = $(selectedSelector, $tpList)[0];
setTimeVal(elm, sel, $tpDiv, settings);
- return false;
}
+ return false;
+ break;
+ case 27: // Esc
+ $tpDiv.hide();
+ return false;
break;
}
+ return true;
+ });
+ $(elm).keyup(function(e) {
+ keyDown = false;
});
-
// Helper function to get an inputs current time as Date object.
// Returns a Date object.
this.getTime = function() {
return timeStringToDate(elm.value, settings);
};
// Helper function to set a time input.
- // Takes a Date object.
+ // Takes a Date object or string.
this.setTime = function(time) {
- elm.value = formatTime(normaliseTime(time), settings);
+ elm.value = formatTime(timeToDate(time, settings), settings);
// Trigger element's change events.
$(elm).change();
};
-
- }; // End fn;
-
+
+ }; // End fn;
+
// Plugin defaults.
$.fn.timePicker.defaults = {
step:30,
separator: ':',
show24Hours: true
};
-
+
// Private functions.
-
+
function setTimeVal(elm, sel, $tpDiv, settings) {
// Update input field
elm.value = $(sel).text();
// Hide picker
$tpDiv.hide();
}
-
+
function formatTime(time, settings) {
var h = time.getHours();
var hours = settings.show24Hours ? h : (((h + 11) % 12) + 1);
var minutes = time.getMinutes();
return formatNumber(hours) + settings.separator + formatNumber(minutes) + (settings.show24Hours ? '' : ((h < 12) ? ' AM' : ' PM'));
}
-
- function formatNumber(value) {
- return (value < 10 ? '0' : '') + value;
- }
-
+
+ function formatNumber(value) {
+ return (value < 10 ? '0' : '') + value;
+ }
+
+ function timeToDate(input, settings) {
+ return (typeof input == 'object') ? normaliseTime(input) : timeStringToDate(input, settings);
+ }
+
function timeStringToDate(input, settings) {
if (input) {
var array = input.split(settings.separator);
var hours = parseFloat(array[0]);
var minutes = parseFloat(array[1]);
+
+ // Convert AM/PM hour to 24-hour format.
+ if (!settings.show24Hours) {
+ if (hours === 12 && input.indexOf('AM') !== -1) {
+ hours = 0;
+ }
+ else if (hours !== 12 && input.indexOf('PM') !== -1) {
+ hours += 12;
+ }
+ }
var time = new Date(0, 0, 0, hours, minutes, 0);
return normaliseTime(time);
}
return null;
}
-
+
/* Normalise time object to a common date. */
- function normaliseTime(time) {
- time.setFullYear(2001);
- time.setMonth(0);
- time.setDate(0);
- return time;
- }
-
+ function normaliseTime(time) {
+ time.setFullYear(2001);
+ time.setMonth(0);
+ time.setDate(0);
+ return time;
+ }
+
})(jQuery);