text: /************************
  jquery-timepicker v1.1.7
  http://jonthornton.github.com/jquery-timepicker/
  
  requires jQuery 1.7+
  ************************/
  
  
  (function (factory) {
      if (typeof define === 'function' && define.amd) {
          // AMD. Register as an anonymous module.
          define(['jquery'], factory);
      } else {
          // Browser globals
          factory(jQuery);
      }
  }(function ($) {
      var _baseDate = _generateBaseDate();
      var _ONE_DAY = 86400;
      var _defaults =	{
          className: null,
          minTime: null,
          maxTime: null,
          durationTime: null,
          step: 30,
          showDuration: false,
          timeFormat: 'g:ia',
          scrollDefaultNow: false,
          scrollDefaultTime: false,
          selectOnBlur: false,
          disableTouchKeyboard: true,
          forceRoundTime: false,
          appendTo: 'body',
          disableTimeRanges: [],
          closeOnWindowScroll: false
      };
      var _lang = {
          decimal: '.',
          mins: 'mins',
          hr: 'hr',
          hrs: 'hrs'
      };
  
      var methods =
      {
          init: function(options)
          {
              return this.each(function()
              {
                  var self = $(this);
  
                  // convert dropdowns to text input
                  if (self[0].tagName == 'SELECT') {
                      var attrs = {
                          'type': 'text', 
                          'value': self.val()
                      };
                      var raw_attrs = self[0].attributes;
  
                      for (var i=0; i < raw_attrs.length; i++) {
                          attrs[raw_attrs[i].nodeName] = raw_attrs[i].nodeValue;
                      }
  
                      var input = $('<input />', attrs);
                      self.replaceWith(input);
                      self = input;
                  }
  
                  var settings = $.extend({}, _defaults);
  
                  if (options) {
                      settings = $.extend(settings, options);
                  }
  
                  if (settings.lang) {
                      _lang = $.extend(_lang, settings.lang);
                  }
  
                  settings = _parseSettings(settings);
  
                  self.data('timepicker-settings', settings);
                  self.prop('autocomplete', 'off');
                  self.on('click.timepicker focus.timepicker', methods.show);
                  self.on('blur.timepicker', _formatValue);
                  self.on('keydown.timepicker', _keydownhandler);
                  self.on('keyup.timepicker', _keyuphandler);
                  self.addClass('ui-timepicker-input');
  
                  _formatValue.call(self.get(0));
              });
          },
  
          show: function(e)
          {
              var self = $(this);
              var settings = self.data('timepicker-settings');
  
              if ('ontouchstart' in document && settings.disableTouchKeyboard) {
                  // block the keyboard on mobile devices
                  self.blur();
              }
  
              var list = self.data('timepicker-list');
  
              // check if input is readonly
              if (self.prop('readonly')) {
                  return;
              }
  
              // check if list needs to be rendered
              if (!list || list.length === 0) {
                  _render(self);
                  list = self.data('timepicker-list');
              }
  
              if (list.is(':visible')) {
                  return;
              }
  
              // make sure other pickers are hidden
              methods.hide();
  
              list.show();
  
              if ((self.offset().top + self.outerHeight(true) + list.outerHeight()) > $(window).height() + $(window).scrollTop()) {
                  // position the dropdown on top
                  list.offset({
                      'left': self.offset().left + parseInt(list.css('marginLeft').replace('px', ''), 10),
                      'top': self.offset().top - list.outerHeight() + parseInt(list.css('marginTop').replace('px', ''), 10)
                  });
              } else {
                  // put it under the input
                  list.offset({
                      'left':self.offset().left + parseInt(list.css('marginLeft').replace('px', ''), 10),
                      'top': self.offset().top + self.outerHeight() + parseInt(list.css('marginTop').replace('px', ''), 10)
                  });
              }
  
              // position scrolling
              var selected = list.find('.ui-timepicker-selected');
  
              if (!selected.length) {
                  if (_getTimeValue(self)) {
                      selected = _findRow(self, list, _time2int(_getTimeValue(self)));
                  } else if (settings.scrollDefaultNow) {
                      selected = _findRow(self, list, _time2int(new Date()));
                  } else if (settings.scrollDefaultTime !== false) {
                      selected = _findRow(self, list, _time2int(settings.scrollDefaultTime));
                  }
              }
  
              if (selected && selected.length) {
                  var topOffset = list.scrollTop() + selected.position().top - selected.outerHeight();
                  list.scrollTop(topOffset);
              } else {
                  list.scrollTop(0);
              }
  
              _attachCloseHandler(settings);
  
              self.trigger('showTimepicker');
          },
  
          hide: function(e)
          {
              $('.ui-timepicker-wrapper:visible').each(function() {
                  var list = $(this);
                  var self = list.data('timepicker-input');
                  var settings = self.data('timepicker-settings');
  
                  if (settings && settings.selectOnBlur) {
                      _selectValue(self);
                  }
  
                  list.hide();
                  self.trigger('hideTimepicker');
              });
          },
  
          option: function(key, value)
          {
              var self = this;
              var settings = self.data('timepicker-settings');
              var list = self.data('timepicker-list');
  
              if (typeof key == 'object') {
                  settings = $.extend(settings, key);
  
              } else if (typeof key == 'string' && typeof value != 'undefined') {
                  settings[key] = value;
  
              } else if (typeof key == 'string') {
                  return settings[key];
              }
  
              settings = _parseSettings(settings);
  
              self.data('timepicker-settings', settings);
  
              if (list) {
                  list.remove();
                  self.data('timepicker-list', false);
              }
  
              return self;
          },
  
          getSecondsFromMidnight: function()
          {
              return _time2int(_getTimeValue(this));
          },
  
          getTime: function()
          {
              var self = this;
              var today = new Date();
              today.setHours(0, 0, 0, 0);
              return new Date(today.valueOf() + (_time2int(_getTimeValue(self))*1000));
          },
  
          setTime: function(value)
          {
              var self = this;
              var prettyTime = _int2time(_time2int(value), self.data('timepicker-settings').timeFormat);
              _setTimeValue(self, prettyTime);
          },
  
          remove: function()
          {
              var self = this;
  
              // check if this element is a timepicker
              if (!self.hasClass('ui-timepicker-input')) {
                  return;
              }
  
              self.removeAttr('autocomplete', 'off');
              self.removeClass('ui-timepicker-input');
              self.removeData('timepicker-settings');
              self.off('.timepicker');
  
              // timepicker-list won't be present unless the user has interacted with this timepicker
              if (self.data('timepicker-list')) {
                  self.data('timepicker-list').remove();
              }
  
              self.removeData('timepicker-list');
          }
      };
  
      // private methods
  
      function _parseSettings(settings)
      {
          if (settings.minTime) {
              settings.minTime = _time2int(settings.minTime);
          }
  
          if (settings.maxTime) {
              settings.maxTime = _time2int(settings.maxTime);
          }
  
          if (settings.durationTime) {
              settings.durationTime = _time2int(settings.durationTime);
          }
  
          if (settings.disableTimeRanges.length > 0) {
              // convert string times to integers
              for (var i in settings.disableTimeRanges) {
                  settings.disableTimeRanges[i] = [
                  _time2int(settings.disableTimeRanges[i][0]),
                  _time2int(settings.disableTimeRanges[i][1])
                  ];
              }
  
              // sort by starting time
              settings.disableTimeRanges = settings.disableTimeRanges.sort(function(a, b){
                  return a[0] - b[0];
              });
          }
  
          return settings;
      }
  
      function _render(self)
      {
          var settings = self.data('timepicker-settings');
          var list = self.data('timepicker-list');
  
          if (list && list.length) {
              list.remove();
              self.data('timepicker-list', false);
          }
  
          list = $('<ul />', {
              'class': 'ui-timepicker-list'
          });
  
          var wrapped_list = $('<div />', {
              'class': 'ui-timepicker-wrapper', 
              'tabindex': -1
          });
          wrapped_list.css({
              'display':'none', 
              'position': 'absolute'
          }).append(list);
  
  
          if (settings.className) {
              wrapped_list.addClass(settings.className);
          }
  
          if ((settings.minTime !== null || settings.durationTime !== null) && settings.showDuration) {
              wrapped_list.addClass('ui-timepicker-with-duration');
          }
  
          var durStart = (settings.durationTime !== null) ? settings.durationTime : settings.minTime;
          var start = (settings.minTime !== null) ? settings.minTime : 0;
          var end = (settings.maxTime !== null) ? settings.maxTime : (start + _ONE_DAY - 1);
  
          if (end <= start) {
              // make sure the end time is greater than start time, otherwise there will be no list to show
              end += _ONE_DAY;
          }
  
          var disabledTimeRanges = settings.disableTimeRanges;
          var disabledRange = disabledTimeRanges.shift();
  
          for (var i=start; i <= end; i += settings.step*60) {
              var timeInt = i%_ONE_DAY;
  
              var row = $('<li />');
              row.data('time', timeInt);
              row.text(_int2time(timeInt, settings.timeFormat));
  
              if ((settings.minTime !== null || settings.durationTime !== null) && settings.showDuration) {
                  var duration = $('<span />');
                  duration.addClass('ui-timepicker-duration');
                  duration.text(' ('+_int2duration(i - durStart)+')');
                  row.append(duration);
              }
  
              if (disabledRange) {
                  if (timeInt >= disabledRange[0] && timeInt < disabledRange[1]) {
                      row.addClass('ui-timepicker-disabled');
                  } else if (timeInt >= disabledRange[1]) {
                      disabledRange = disabledTimeRanges.shift();
                  }
              }
  
              list.append(row);
          }
  
          wrapped_list.data('timepicker-input', self);
          self.data('timepicker-list', wrapped_list);
  
          var appendTo = settings.appendTo;
          if (typeof appendTo === 'string') {
              appendTo = $(appendTo);
          } else if (typeof appendTo === 'function') {
              appendTo = appendTo(self);
          }
          appendTo.append(wrapped_list);
          _setSelected(self, list);
  
          list.on('click', 'li', function(e) {
  
              // hack: temporarily disable the focus handler
              // to deal with the fact that IE fires 'focus'
              // events asynchronously
              self.off('focus.timepicker');
              self.on('focus.timepicker-ie-hack', function(){
                  self.off('focus.timepicker-ie-hack');
                  self.on('focus.timepicker', methods.show);
              });
              self[0].focus();
  
              // make sure only the clicked row is selected
              list.find('li').removeClass('ui-timepicker-selected');
              $(this).addClass('ui-timepicker-selected');
  
              if (_selectValue(self)) {
                  wrapped_list.hide();
              }
          });
      }
  
      function _generateBaseDate()
      {
          return new Date(1970, 1, 1, 0, 0, 0);
      }
  
      function _attachCloseHandler(settings)
      {
          if ('ontouchstart' in document) {
              $('body').on('touchstart.ui-timepicker', _closeHandler);
          } else {
              $('body').on('mousedown.ui-timepicker', _closeHandler);
              if (settings.closeOnWindowScroll) {
                  $(window).on('scroll.ui-timepicker', _closeHandler);
              }
          }
      }
  
      // event handler to decide whether to close timepicker
      function _closeHandler(e)
      {
          var target = $(e.target);
          var input = target.closest('.ui-timepicker-input');
          if (input.length === 0 && target.closest('.ui-timepicker-wrapper').length === 0) {
              methods.hide();
              $('body').unbind('.ui-timepicker');
              $(window).unbind('.ui-timepicker');
          }
      }
  
      function _findRow(self, list, value)
      {
          if (!value && value !== 0) {
              return false;
          }
  
          var settings = self.data('timepicker-settings');
          var out = false;
          var halfStep = settings.step*30;
  
          // loop through the menu items
          list.find('li').each(function(i, obj) {
              var jObj = $(obj);
  
              var offset = jObj.data('time') - value;
  
              // check if the value is less than half a step from each row
              if (Math.abs(offset) < halfStep || offset == halfStep) {
                  out = jObj;
                  return false;
              }
          });
  
          return out;
      }
  
      function _setSelected(self, list)
      {
          list.find('li').removeClass('ui-timepicker-selected');
  
          var timeValue = _time2int(_getTimeValue(self));
          if (!timeValue) {
              return;
          }
  
          var selected = _findRow(self, list, timeValue);
          if (selected) {
  
              var topDelta = selected.offset().top - list.offset().top;
  
              if (topDelta + selected.outerHeight() > list.outerHeight() || topDelta < 0) {
                  list.scrollTop(list.scrollTop() + selected.position().top - selected.outerHeight());
              }
  
              selected.addClass('ui-timepicker-selected');
          }
      }
  
  
      function _formatValue()
      {
          if (this.value === '') {
              return;
          }
  
          var self = $(this);
          var seconds = _time2int(this.value);
  
          if (seconds === null) {
              self.trigger('timeFormatError');
              return;
          }
  
          var settings = self.data('timepicker-settings');
  
          // check that the time in within bounds
          if (settings.minTime !== null && seconds < settings.minTime) {
              self.trigger('timeRangeError');
          } else if (settings.maxTime !== null && seconds > settings.maxTime) {
              self.trigger('timeRangeError');
          }
  
          if (settings.forceRoundTime) {
              var offset = seconds % (settings.step*60); // step is in minutes
  
              if (offset >= settings.step*30) {
                  // if offset is larger than a half step, round up
                  seconds += (settings.step*60) - offset;
              } else {
                  // round down
                  seconds -= offset;
              }
          }
  
          var prettyTime = _int2time(seconds, settings.timeFormat);
          _setTimeValue(self, prettyTime);
      }
  
      function _getTimeValue(self)
      {
          if (self.is('input')) {
              return self.val();
          } else {
              // use the element's data attributes to store values
              return self.data('ui-timepicker-value');
          }
      }
  
      function _setTimeValue(self, value)
      {
          if (self.is('input')) {
              self.val(value);
          } else {
              // use the element's data attributes to store values
              self.data('ui-timepicker-value', value);
          }
      }
  
      /*
  	*  Keyboard navigation via arrow keys
  	*/
      function _keydownhandler(e)
      {
          var self = $(this);
          var list = self.data('timepicker-list');
  
          if (!list || !list.is(':visible')) {
              if (e.keyCode == 40) {
                  self.focus();
              } else {
                  return true;
              }
          }
  
          switch (e.keyCode) {
  
              case 13: // return
                  if (_selectValue(self)) {
                      methods.hide.apply(this);
                  }
  
                  e.preventDefault();
                  return false;
  
              case 38: // up
                  var selected = list.find('.ui-timepicker-selected');
  
                  if (!selected.length) {
                      list.find('li').each(function(i, obj) {
                          if ($(obj).position().top > 0) {
                              selected = $(obj);
                              return false;
                          }
                      });
                      selected.addClass('ui-timepicker-selected');
  
                  } else if (!selected.is(':first-child')) {
                      selected.removeClass('ui-timepicker-selected');
                      selected.prev().addClass('ui-timepicker-selected');
  
                      if (selected.prev().position().top < selected.outerHeight()) {
                          list.scrollTop(list.scrollTop() - selected.outerHeight());
                      }
                  }
  
                  return false;
  
              case 40: // down
                  selected = list.find('.ui-timepicker-selected');
  
                  if (selected.length === 0) {
                      list.find('li').each(function(i, obj) {
                          if ($(obj).position().top > 0) {
                              selected = $(obj);
                              return false;
                          }
                      });
  
                      selected.addClass('ui-timepicker-selected');
                  } else if (!selected.is(':last-child')) {
                      selected.removeClass('ui-timepicker-selected');
                      selected.next().addClass('ui-timepicker-selected');
  
                      if (selected.next().position().top + 2*selected.outerHeight() > list.outerHeight()) {
                          list.scrollTop(list.scrollTop() + selected.outerHeight());
                      }
                  }
  
                  return false;
  
              case 27: // escape
                  list.find('li').removeClass('ui-timepicker-selected');
                  list.hide();
                  break;
  
              case 9: //tab
                  methods.hide();
                  break;
  
              default:
                  return;
          }
      }
  
      /*
  	*	Time typeahead
  	*/
      function _keyuphandler(e)
      {
          var self = $(this);
          var list = self.data('timepicker-list');
  
          if (!list || !list.is(':visible')) {
              return true;
          }
  
          switch (e.keyCode) {
  
              case 48: // numerals
              case 49:
              case 50:
              case 51:
              case 52:
              case 53:
              case 54:
              case 55:
              case 56:
              case 57:
              case 65: // a
              case 77: // m
              case 80: // p
              case 186: // colon
              case 8: // backspace
              case 46: // delete
                  _setSelected(self, list);
                  break;
  
              default:
                  // list.find('li').removeClass('ui-timepicker-selected');
                  return;
          }
      }
  
      function _selectValue(self)
      {
          var settings = self.data('timepicker-settings');
          var list = self.data('timepicker-list');
          var timeValue = null;
  
          var cursor = list.find('.ui-timepicker-selected');
  
          if (cursor.hasClass('ui-timepicker-disabled')) {
              return false;
          }
  
          if (cursor.length) {
              // selected value found
              timeValue = cursor.data('time');
  
          } else if (_getTimeValue(self)) {
  
              // no selected value; fall back on input value
              timeValue = _time2int(_getTimeValue(self));
  
              _setSelected(self, list);
          }
  
          if (timeValue !== null) {
              var timeString = _int2time(timeValue, settings.timeFormat);
              _setTimeValue(self, timeString);
          }
  
          self.trigger('change').trigger('changeTime');
          return true;
      }
  
      function _int2duration(seconds)
      {
          var minutes = Math.round(seconds/60);
          var duration;
  
          if (Math.abs(minutes) < 60) {
              duration = [minutes, _lang.mins];
          } else if (minutes == 60) {
              duration = ['1', _lang.hr];
          } else {
              var hours = (minutes/60).toFixed(1);
              if (_lang.decimal != '.') hours = hours.replace('.', _lang.decimal);
              duration = [hours, _lang.hrs];
          }
  
          return duration.join(' ');
      }
  
      function _int2time(seconds, format)
      {
          if (seconds === null) {
              return;
          }
  
          var time = new Date(_baseDate.valueOf() + (seconds*1000));
          var output = '';
          var hour, code;
  
          for (var i=0; i<format.length; i++) {
  
              code = format.charAt(i);
              switch (code) {
  
                  case 'a':
                      output += (time.getHours() > 11) ? 'pm' : 'am';
                      break;
  
                  case 'A':
                      output += (time.getHours() > 11) ? 'PM' : 'AM';
                      break;
  
                  case 'g':
                      hour = time.getHours() % 12;
                      output += (hour === 0) ? '12' : hour;
                      break;
  
                  case 'G':
                      output += time.getHours();
                      break;
  
                  case 'h':
                      hour = time.getHours() % 12;
  
                      if (hour !== 0 && hour < 10) {
                          hour = '0'+hour;
                      }
  
                      output += (hour === 0) ? '12' : hour;
                      break;
  
                  case 'H':
                      hour = time.getHours();
                      output += (hour > 9) ? hour : '0'+hour;
                      break;
  
                  case 'i':
                      var minutes = time.getMinutes();
                      output += (minutes > 9) ? minutes : '0'+minutes;
                      break;
  
                  case 's':
                      seconds = time.getSeconds();
                      output += (seconds > 9) ? seconds : '0'+seconds;
                      break;
  
                  default:
                      output += code;
              }
          }
  
          return output;
      }
  
      function _time2int(timeString)
      {
          if (timeString === '') return null;
          if (!timeString || timeString+0 == timeString) return timeString;
  
          if (typeof(timeString) == 'object') {
              timeString = timeString.getHours()+':'+timeString.getMinutes()+':'+timeString.getSeconds();
          }
  
          timeString = timeString.toLowerCase();
  
          var d = new Date(0);
          var time;
  
          // try to parse time input
          if (timeString.indexOf(":") === -1) {
              // no colon present
              time = timeString.match(/^([0-9]):?([0-5][0-9])?:?([0-5][0-9])?\s*([pa]?)m?$/);
  
              if (!time) {
                  time = timeString.match(/^([0-2][0-9]):?([0-5][0-9])?:?([0-5][0-9])?\s*([pa]?)m?$/);
              }
          } else {
              time = timeString.match(/^(\d{1,2})(?::([0-5][0-9]))?(?::([0-5][0-9]))?\s*([pa]?)m?$/);
          }
  
          if (!time) {
              return null;
          }
  
          var hour = parseInt(time[1]*1, 10);
          var hours;
  
          if (time[4]) {
              if (hour == 12) {
                  hours = (time[4] == 'p') ? 12 : 0;
              } else {
                  hours = (hour + (time[4] == 'p' ? 12 : 0));
              }
  
          } else {
              hours = hour;
          }
  
          var minutes = ( time[2]*1 || 0 );
          var seconds = ( time[3]*1 || 0 );
          return hours*3600 + minutes*60 + seconds;
      }
  
      // Plugin entry
      $.fn.timepicker = function(method)
      {
          if(methods[method]) {
              return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
          }
          else if(typeof method === "object" || !method) {
              return methods.init.apply(this, arguments);
          }
          else {
              $.error("Method "+ method + " does not exist on jQuery.timepicker");
          }
      };
  }));
  