text: /*
   * A time picker for jQuery
   * Based on original timePicker by Sam Collet (http://www.texotela.co.uk) -
   * copyright (c) 2006 Sam Collett (http://www.texotela.co.uk)
   *
   * Dual licensed under the MIT and GPL licenses.
   * Copyright (c) 2009 Anders Fajerson
   * @name     timePicker
   * @version  0.2
   * @author   Anders Fajerson (http://perifer.se)
   * @example  $("#mytime").timePicker();
   * @example  $("#mytime").timePicker({step:30, startTime:"15:00", endTime:"18:00"});
   */
  
  (function($){
    $.fn.timePicker = function(options) {
      // Build main options before element iteration
      var settings = $.extend({}, $.fn.timePicker.defaults, options);
  
      return this.each(function() {
        $.timePicker(this, settings);
      });
    };
  
    $.timePicker = function (elm, settings) {
      var e = $(elm)[0];
      return e.timePicker || (e.timePicker = new jQuery._timePicker(e, settings));
    };
  
    $._timePicker = function(elm, settings) {
  
      var tpOver = false;
      var keyDown = false;
      var startTime = timeToDate(settings.startTime, settings);
      var endTime = timeToDate(settings.endTime, settings);
  
      $(elm).attr('autocomplete', 'OFF'); // Disable browser autocomplete
  
      var times = [];
      var time = new Date(startTime); // Create a new date object.
      while(time <= endTime) {
        times[times.length] = formatTime(time, settings);
        time = new Date(time.setMinutes(time.getMinutes() + settings.step));
      }
  
      //A tpDiv is generated for every timepicker, so give it a unique id based settings.id
      //so the GWT TimePickerTextBox can be fully functional.
      //If there is no settings.id then do not bother with an id because it is not being used
      //with GWT TimePickerTextBox.
      var $tpDiv = $('<div ' + (settings.tpDivId ? 'id="' + settings.tpDivId + '" ' : '') + '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);
      // Append the timPicker to the body and position it.
      var elmOffset = $(elm).offset();
      
      $tpDiv.appendTo('body').css({
        'top':(elmOffset.top - 48) + "px", 
        'left':elmOffset.left,
        'width': ($(elm).width() + 5) + "px"
      }).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) {
          $("li.selected", $tpDiv).removeClass("selected");
          $(this).addClass("selected");
        }
      }).mousedown(function() {
         tpOver = true;
      }).click(function() {
        setTimeVal(elm, this, $tpDiv, settings);
        tpOver = false;
      });
  
      var showPicker = function() {
        if ($tpDiv.is(":visible")) {
          return false;
        }
        $("li", $tpDiv).removeClass("selected");
  
        //Reposition the picker in case the page has changed (other things hidden or shown)
        //and the textbox element is no longer where it was when the tpDiv was first appended to the DOM body.
        var elmOffset = $(elm).offset();
        
        $tpDiv.css({
          'top':(elmOffset.top + $(elm).height() + 10) + "px", 
          'left':elmOffset.left,
          'width': ($(elm).width() + 5) + "px"
        });
        
        // 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 = 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(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");
          // Scroll to matched time.
          $tpDiv[0].scrollTop = $matchedTime[0].offsetTop;
        }
        return true;
      };
      // 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
      $(elm).blur(function() {
        if (!tpOver) {
          $tpDiv.hide();
        }
      });
      // 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 = 'keydown';
      $(elm)[event](function(e) {
        var $selected;
        keyDown = true;
        var top = $tpDiv[0].scrollTop;
        switch (e.keyCode) {
          case 38: // Up arrow.
            // Just show picker if it's hidden.
            if (showPicker()) {
              return false;
            };
            $selected = $("li.selected", $tpList);
            var prev = $selected.prev().addClass("selected")[0];
            if (prev) {
              $selected.removeClass("selected");
              // Scroll item into view.
              if (prev.offsetTop < top) {
                $tpDiv[0].scrollTop = top - prev.offsetHeight;
              }
            }
            else {
              // Loop to next item.
              $selected.removeClass("selected");
              prev = $("li:last", $tpList).addClass("selected")[0];
              $tpDiv[0].scrollTop = prev.offsetTop - prev.offsetHeight;
            }
            return false;
            break;
          case 40: // Down arrow, similar in behaviour to up arrow.
            if (showPicker()) {
              return false;
            };
            $selected = $("li.selected", $tpList);
            var next = $selected.next().addClass("selected")[0];
            if (next) {
              $selected.removeClass("selected");
              if (next.offsetTop + next.offsetHeight > top + $tpDiv[0].offsetHeight) {
                $tpDiv[0].scrollTop = top + next.offsetHeight;
              }
            }
            else {
              $selected.removeClass("selected");
              next = $("li:first", $tpList).addClass("selected")[0];
              $tpDiv[0].scrollTop = 0;
            }
            return false;
            break;
          case 13: // Enter
            if ($tpDiv.is(":visible")) {
              var sel = $("li.selected", $tpList)[0];
              setTimeVal(elm, sel, $tpDiv, settings);
            }
            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.
      this.setTime = function(time) {
        elm.value = formatTime(normaliseTime(time), settings);
        // Trigger element's change events.
        $(elm).change();
      };
  
    }; // End fn;
  
    // Plugin defaults.
    $.fn.timePicker.defaults = {
      step:30,
      startTime: new Date(0, 0, 0, 0, 0, 0),
      endTime: new Date(0, 0, 0, 23, 30, 0),
      separator: ':',
      show24Hours: true,
      timeFormat: 'hh:mm tt',
      amDesignator: 'AM',
      pmDesignator: 'PM'
    };
  
    // Private functions.
  
    function setTimeVal(elm, sel, $tpDiv, settings) {
      // Update input field
      elm.value = $(sel).text();
      // Trigger element's change events.
      $(elm).change();
      // Keep focus for all but IE (which doesn't like it)
      var isMSIE = /*@cc_on!@*/0;
      if (!isMSIE) {
        elm.focus();
      }
      // Hide picker
      $tpDiv.hide();
    }
  
    function formatTime(time, settings) {
      var h = time.getHours();
      var hours = settings.show24Hours ? h : (((h + 11) % 12) + 1);
      hours = formatHour(hours, settings);
      var minutes = time.getMinutes();
      
      // Now that we have the current time we take the time format
      // and replace the time values into that string.
      var time = settings.timeFormat;
      time = time.replace('hh', hours);
      time = time.replace('h', hours);
      time = time.replace('HH', hours);
      time = time.replace('H', hours);
      time = time.replace('mm', formatNumber(minutes));
      time = time.replace('tt', (h < 12) ? settings.amDesignator : settings.pmDesignator);
      
      return time;
    }
  
    function formatNumber(value) {
      return (value < 10 ? '0' : '') + value;
    }
    
    function formatHour(value, settings) {
      if (settings.show24Hours) {
        return (value < 10 ? '0' : '') + value;
      } else {
        return 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.substr(settings.amDesignator) !== -1) {
            hours = 0;
          }
          else if (hours !== 12 && input.indexOf(settings.pmDesignator) !== -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;
    }
  
  })(jQuery);
  
  