/**
 * @class CM_FormField_Abstract
 * @extends CM_View_Abstract
 */
var CM_FormField_Abstract = CM_View_Abstract.extend({
  _class: 'CM_FormField_Abstract',

  /** @type Object **/
  fieldOptions: {},

  initialize: function() {
    CM_View_Abstract.prototype.initialize.call(this);

    this.fieldOptions = {};
  },

  ready: function() {
  },

  validate: function() {
    var value = this.getValue();
    if (this.isEmpty(value)) {
      this.error(null);
      return;
    }
    var self = this;
    this.ajax('validate', {'userInput': value, 'form': this.getForm().getClass(), 'fieldName': this.getName()})
      .then(function() {
        if (value == self.getValue()) {
          self.error();
        }
      })
      .catch(CM_Exception, function(error) {
        if (error instanceof CM_Exception_FormFieldValidation) {
          self.error(error.message);
        } else if (value == self.getValue()) {
          throw error;
        }
      });
  },

  reset: function() {
  },

  /**
   * @return CM_Form_Abstract
   */
  getForm: function() {
    return this.getParent();
  },

  /**
   * @return jQuery
   */
  $: function(selector) {
    if (!selector) {
      return this.$el;
    }
    return $(selector, this.el);
  },

  /**
   * @return String
   */
  getName: function() {
    return this.options.params.name;
  },

  /**
   * @returns {jQuery}
   */
  getInput: function() {
    return this.$('input:first, select:first, textarea:first')
  },

  /**
   * @returns {*|String|null}
   */
  getValue: function() {
    return this.getInput().val();
  },

  /**
   * @param {*|String|null} value
   */
  setValue: function(value) {
    this.getInput().val(value);
  },

  /**
   * @returns {Boolean}
   */
  getEnabled: function() {
    return this.getInput().filter(':enabled').length > 0;
  },

  /**
   * @return Object
   */
  getOptions: function() {
    return this.fieldOptions;
  },

  /**
   * @param {String} name
   * @return mixed|null
   */
  getOption: function(name) {
    var options = this.getOptions();
    if (null === options[name] || '' === options[name]) {
      return null;
    }
    return options[name];
  },

  setFocus: function() {
    this.getInput().focus();
  },

  /**
   * @param {String|Null} [message]
   */
  error: function(message) {
    var $container = this.$('.messages');
    var $errorMessage = $container.find('.formField-error');
    this.$el.removeClass('hasError');

    if (message) {
      if ($container.length) {
        this.$el[0].offsetWidth;	// Force reflow for CSS-animation
        this.$el.addClass('hasError');

        if ($errorMessage.length) {
          $errorMessage.html(message);
        } else {
          $errorMessage = $('<div class="formField-error"></div>').hide().appendTo($container);
          $errorMessage.html(message);
          $errorMessage.slideDown('fast');
        }
        this.setFocus();

      } else {
        throw new CM_Exception('FormField `' + this.getName() + '`: ' + message);
      }
    } else {
      $errorMessage.remove();
    }
  },

  /**
   * @param {Object} value
   * @returns {Boolean}
   */
  isEmpty: function(value) {
    return _.isEmpty(value);
  }
});

@@ -1392,30 +1392,36 @@
 ion() %7B%0A    
-return
+var $input =
  this.$('inp
@@ -1460,16 +1460,159 @@
 :first')
+;%0A    if ($input.length === 0) %7B%0A      throw new CM_Exception('Can%5C't find input for %60' + this.getName() + '%60 field');%0A    %7D%0A    return $input;
 %0A  %7D,%0A%0A 

Throw if no input found for default getInput() method