text: var SpanCreatorTimeField = function(
    topContainer, 
    analyzer, 
    timeResolver,
    timeUtil) {
    
    var topContainer;
    var date; 
    var timeEl;
    var periodEl;
    var nowButton;
    var ONE_MINUTE = 60*1000;
    var integers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  
    var init = function() {
      gatherComponents();
      addBehavior();
    };
  
    var gatherComponents = function() {
      timeEl = topContainer.find('.time');
      periodEl = topContainer.find('.period');
      nowButton = topContainer.find('button.now');
    };
  
    var addBehavior = function() {
      App.dispatcher.subscribe('DATE_CHANGED', onDateChanged);
      timeEl
        .keydown(onKeyDown)
        .blur(onBlur)
        .focus(onFocus);
      periodEl.click(togglePeriod);
      nowButton.click(now);
    };
  
    var onDateChanged = function(data) {
      date = timeUtil.parseUtcYmd(data.date);
    };
  
    var onFocus = function() {
      topContainer.addClass('has_focus');
    };
  
    var onBlur = function() {
      topContainer.removeClass('has_focus');
      timeEl.text(timeResolver.resolve(timeEl.text()));
      adjustInvalidTreatment();
    };
  
    var onKeyDown = function(e) {
      switch (e.key) {
  
        case 'a':
        case 'A':
          setPeriodAm();
          break;
  
        case 'p':
        case 'P':
          setPeriodPm();
          break;
  
        case 'Backspace':
          removeChar();
          break;
  
        case ':':
          addColon();
          break;
  
        case 'n':
          now();
          break;
  
        case 'ArrowUp':
          minuteUp(e);
          break;
  
        case 'ArrowDown':
          minuteDown(e);
          break;
  
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          addNumber(e.key);
          break;
      }
  
      App.dispatcher.publish('SPAN_CHANGED');
    };
  
    var minuteUp = function(e) {
      adjustMinute(e, 1);
    };
  
    var minuteDown = function(e) {
      adjustMinute(e, -1);
    };
  
    var adjustMinute = function(e, valueFlipper) {
      if (!isEmpty() && !isValid())
        return;
  
      e.preventDefault();
      var delta = ONE_MINUTE*valueFlipper;
      var oldDate = isEmpty() ? getNow() : getTimeFromElements();
      var newDate = new Date(oldDate.getTime() + delta);
      setTime(newDate);
    };
  
    var setTime = function(dateObj) {
      var munged = timeUtil.get12Hour(dateObj);
      timeEl.text(munged.time);
      if (munged.period == 'AM')
        setPeriodAm();
      else
        setPeriodPm();
      checkForFix();
    };
  
    var removeChar = function() {
      var text = timeEl.text();
      if (text.length > 0)
        timeEl.text(text.substr(0, text.length -1));
    };
  
    var addNumber = function(key) {
      var text = timeEl.text();
      if (analyzer.isNumerable(text)) {
        timeEl.text(text + key);
        checkForFix();
        checkForErrorOnCompletion();
      }
    };
  
    var addColon = function() {
      var text = timeEl.text();
      if (analyzer.isColonable(text))
        timeEl.text(text + ':');
    };
  
    var setPeriodAm = function() {
      periodEl.text('AM');
    };
  
    var setPeriodPm = function() {
      periodEl.text('PM');
    };
  
    var togglePeriod = function() {
      if (periodEl.text() == 'AM')
        periodEl.text('PM');
      else 
        periodEl.text('AM');
      App.dispatcher.publish('SPAN_CHANGED');
    };
  
    var checkForErrorOnCompletion = function() {
      var timeStr = timeEl.text();
      if (analyzer.isCorrectFormat(timeStr) && !isValid())
        timeEl.addClass('error');
    };
  
    var adjustInvalidTreatment = function() {
      if (isValid())
        timeEl.removeClass('error');
      else
        timeEl.addClass('error');
    };
  
    var isValid = function() {
      return analyzer.isValid(timeEl.text());
    };
  
    var isEmpty = function() {
      return timeEl.text() == '';
    };
  
    var checkForFix = function() {
      if (timeEl.hasClass('error') && isValid())
        timeEl.removeClass('error')
    };
  
    var now = function() {
      setTime(new Date());
      timeEl.focus();
      App.dispatcher.publish('SPAN_CHANGED');
    };
  
    var getTimeFromElements = function() {
      if (!date)
        return;
      
      var dateCopy = new Date(date.getTime());
  
      var parts = timeEl.text().split(':');
      dateCopy.setMinutes(parts[1]);
  
      var hours = parseInt(parts[0]);
      if (periodEl.text() == 'PM' && hours != 12)
         hours += 12;
      if (periodEl.text() == 'AM' && hours == 12)
        hours = 0;
      dateCopy.setHours(hours);
  
      return dateCopy;
    };
  
    var getNow = function() {
      var nowDate = new Date();
      var dateCopy = new Date(date.getTime());
      dateCopy.setHours(nowDate.getHours());
      dateCopy.setMinutes(nowDate.getMinutes());
      return dateCopy;
    };
  
    init();
  
    return {
      getTime:function() {
        adjustInvalidTreatment();
        if (!isValid())
          return;
  
        if (isEmpty())
          return;
  
        return getTimeFromElements();
      },
      clear:function() {
        timeEl.text('');
  
        var date = new Date();
        if (date.getHours() >= 12)
          setPeriodPm();
        else
          setPeriodAm();
      },
      now:now,
      setTime:setTime,
      focus:function() {
        timeEl.focus();
      }
    };
  };
  