    /**
     * @class Ext.form.field.Text
     * @extends Ext.form.field.Base

    A basic text field.  Can be used as a direct replacement for traditional text inputs,
    or as the base class for more sophisticated input controls (like {@link Ext.form.field.TextArea}
    and {@link Ext.form.field.ComboBox}). Has support for empty-field placeholder values (see {@link #emptyText}).

    #Validation#

    The Text field has a useful set of validations built in:

    - {@link #allowBlank} for making the field required
    - {@link #minLength} for requiring a minimum value length
    - {@link #maxLength} for setting a maximum value length (with {@link #enforceMaxLength} to add it
      as the `maxlength` attribute on the input element)
    - {@link #regex} to specify a custom regular expression for validation

    In addition, custom validations may be added:

    - {@link #vtype} specifies a virtual type implementation from {@link Ext.form.field.VTypes} which can contain
      custom validation logic
    - {@link #validator} allows a custom arbitrary function to be called during validation

    The details around how and when each of these validation options get used are described in the
    documentation for {@link #getErrors}.

    By default, the field value is checked for validity immediately while the user is typing in the
    field. This can be controlled with the {@link #validateOnChange}, {@link #checkChangeEvents}, and
    {@link #checkChangeBugger} configurations. Also see the details on Form Validation in the
    {@link Ext.form.Panel} class documentation.

    #Masking and Character Stripping#

    Text fields can be configured with custom regular expressions to be applied to entered values before
    validation: see {@link #maskRe} and {@link #stripCharsRe} for details.
    {@img Ext.form.Text/Ext.form.Text.png Ext.form.Text component}
    #Example usage:#

        Ext.create('Ext.form.Panel', {
            title: 'Contact Info',
            width: 300,
            bodyPadding: 10,
            renderTo: Ext.getBody(),
            items: [{
                xtype: 'textfield',
                name: 'name',
                fieldLabel: 'Name',
                allowBlank: false  // requires a non-empty value
            }, {
                xtype: 'textfield',
                name: 'email',
                fieldLabel: 'Email Address',
                vtype: 'email'  // requires value to be a valid email address format
            }]
        });

     *
     * @markdown
     * @docauthor Jason Johnston <jason@sencha.com>
     */
    Ext.define('Ext.form.field.Text', {
        extend:'Ext.form.field.Base',
        alias: 'widget.textfield',
        requires: ['Ext.form.field.VTypes', 'Ext.layout.component.field.Text'],
        alternateClassName: ['Ext.form.TextField', 'Ext.form.Text'],

        /**
         * @cfg {String} vtypeText A custom error message to display in place of the default message provided
         * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>undefined</tt>).
         * <b>Note</b>: only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
         */

        /**
         * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
         * before validation (defaults to <tt>undefined</tt>).
         */

        /**
         * @cfg {Number} size An initial value for the 'size' attribute on the text input element. This is only
         * used if the field has no configured {@link #width} and is not given a width by its container's layout.
         * Defaults to <tt>20</tt>.
         */
        size: 20,

        /**
         * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
         * (defaults to <tt>false</tt>)
         */

        /**
         * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
         * to <tt>30</tt>)
         */
        growMin : 30,

        /**
         * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
         * to <tt>800</tt>)
         */
        growMax : 800,

        /**
         * @cfg {String} growAppend
         * A string that will be appended to the field's current value for the purposes of calculating the target
         * field size. Only used when the {@link #grow} config is <tt>true</tt>. Defaults to a single capital "W"
         * (the widest character in common fonts) to leave enough space for the next typed character and avoid the
         * field value shifting before the width is adjusted.
         */
        growAppend: 'W',

        /**
         * @cfg {String} vtype A validation type name as defined in {@link Ext.form.field.VTypes} (defaults to <tt>undefined</tt>)
         */

        /**
         * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
         * not match (defaults to <tt>undefined</tt>)
         */

        /**
         * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
         * to <tt>false</tt>)
         */

        /**
         * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
         * <tt>true</tt>)
         */
        allowBlank : true,

        /**
         * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
         */
        minLength : 0,

        /**
         * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
         * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
         * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
         * entered into the field use the <tt><b>{@link Ext.form.field.Text#enforceMaxLength enforceMaxLength}</b></tt> option.
         */
        maxLength : Number.MAX_VALUE,

        /**
         * @cfg {Boolean} enforceMaxLength True to set the maxLength property on the underlying input field. Defaults to <tt>false</tt>
         */

        /**
         * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
         * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
         */
        minLengthText : 'The minimum length for this field is {0}',

        /**
         * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
         * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
         */
        maxLengthText : 'The maximum length for this field is {0}',

        /**
         * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
         * receives input focus (defaults to <tt>false</tt>)
         */

        /**
         * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
         * fails (defaults to <tt>'This field is required'</tt>)
         */
        blankText : 'This field is required',

        /**
         * @cfg {Function} validator
         * <p>A custom validation function to be called during field validation ({@link #getErrors})
         * (defaults to <tt>undefined</tt>). If specified, this function will be called first, allowing the
         * developer to override the default validation process.</p>
         * <br><p>This function will be passed the following Parameters:</p>
         * <div class="mdetail-params"><ul>
         * <li><code>value</code>: <i>Mixed</i>
         * <div class="sub-desc">The current field value</div></li>
         * </ul></div>
         * <br><p>This function is to Return:</p>
         * <div class="mdetail-params"><ul>
         * <li><code>true</code>: <i>Boolean</i>
         * <div class="sub-desc"><code>true</code> if the value is valid</div></li>
         * <li><code>msg</code>: <i>String</i>
         * <div class="sub-desc">An error message if the value is invalid</div></li>
         * </ul></div>
         */

        /**
         * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
         * (defaults to <tt>undefined</tt>). If the test fails, the field will be marked invalid using
         * <b><tt>{@link #regexText}</tt></b>.
         */

        /**
         * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
         * test fails during validation (defaults to <tt>''</tt>)
         */
        regexText : '',

        /**
         * @cfg {String} emptyText
         * <p>The default text to place into an empty field (defaults to <tt>undefined</tt>).</p>
         * <p>Note that normally this value will be submitted to the server if this field is enabled; to prevent this
         * you can set the {@link Ext.form.action.Action#submitEmptyText submitEmptyText} option of
         * {@link Ext.form.Basic#submit} to <tt>false</tt>.</p>
         * <p>Also note that if you use <tt>{@link #inputType inputType}:'file'</tt>, {@link #emptyText} is not
         * supported and should be avoided.</p>
         */

        /**
         * @cfg {String} emptyCls The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
         * (defaults to <tt>'x-form-empty-field'</tt>).  This class is automatically added and removed as needed
         * depending on the current field value.
         */
        emptyCls : Ext.baseCSSPrefix + 'form-empty-field',

        ariaRole: 'textbox',

        /**
         * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input field (defaults to <tt>false</tt>)
         */

        componentLayout: 'textfield',

        initComponent : function(){
            this.callParent();
            this.addEvents(
                /**
                 * @event autosize
                 * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered and the field is
                 * resized according to the {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result.
                 * This event provides a hook for the developer to apply additional logic at runtime to resize the
                 * field if needed.
                 * @param {Ext.form.field.Text} this This text field
                 * @param {Number} width The new field width
                 */
                'autosize',

                /**
                 * @event keydown
                 * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
                 * is set to true.
                 * @param {Ext.form.field.Text} this This text field
                 * @param {Ext.EventObject} e
                 */
                'keydown',
                /**
                 * @event keyup
                 * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
                 * is set to true.
                 * @param {Ext.form.field.Text} this This text field
                 * @param {Ext.EventObject} e
                 */
                'keyup',
                /**
                 * @event keypress
                 * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
                 * is set to true.
                 * @param {Ext.form.field.Text} this This text field
                 * @param {Ext.EventObject} e
                 */
                'keypress'
            );
        },

        // private
        initEvents : function(){
            var me = this,
                el = me.inputEl;

            me.callParent();
            if(me.selectOnFocus || me.emptyText){
                me.mon(el, 'mousedown', me.onMouseDown, me);
            }
            if(me.maskRe || (me.vtype && me.disableKeyFilter !== true && (me.maskRe = Ext.form.field.VTypes[me.vtype+'Mask']))){
                me.mon(el, 'keypress', me.filterKeys, me);
            }

            if (me.enableKeyEvents) {
                me.mon(el, {
                    scope: me,
                    keyup: me.onKeyUp,
                    keydown: me.onKeyDown,
                    keypress: me.onKeyPress
                });
            }
        },

        /**
         * @private override - treat undefined and null values as equal to an empty string value
         */
        isEqual: function(value1, value2) {
            return String(Ext.value(value1, '')) === String(Ext.value(value2, ''));
        },

        /**
         * @private
         * If grow=true, invoke the autoSize method when the field's value is changed.
         */
        onChange: function() {
            this.callParent();
            this.autoSize();
        },

        afterRender: function(){
            var me = this;
            if (me.enforceMaxLength) {
                me.inputEl.dom.maxLength = me.maxLength;
            }
            me.applyEmptyText();
            me.autoSize();
            me.callParent();
        },

        onMouseDown: function(e){
            var me = this;
            if(!me.hasFocus){
                me.mon(me.inputEl, 'mouseup', Ext.emptyFn, me, { single: true, preventDefault: true });
            }
        },

        /**
         * Performs any necessary manipulation of a raw String value to prepare it for {@link #stringToValue conversion}
         * and/or {@link #validate validation}. For text fields this applies the configured {@link #stripCharsRe} to the
         * raw value.
         * @param {String} value The unprocessed string value
         * @return {String} The processed string value
         */
        processRawValue: function(value) {
            var me = this,
                stripRe = me.stripCharsRe,
                newValue;

            if (stripRe) {
                newValue = value.replace(stripRe, '');
                if (newValue !== value) {
                    me.setRawValue(newValue);
                    value = newValue;
                }
            }
            return value;
        },

        //private
        onDisable: function(){
            this.callParent();
            if (Ext.isIE) {
                this.inputEl.dom.unselectable = 'on';
            }
        },

        //private
        onEnable: function(){
            this.callParent();
            if (Ext.isIE) {
                this.inputEl.dom.unselectable = '';
            }
        },

        onKeyDown: function(e) {
            this.fireEvent('keydown', this, e);
        },

        onKeyUp: function(e) {
            this.fireEvent('keyup', this, e);
        },

        onKeyPress: function(e) {
            this.fireEvent('keypress', this, e);
        },

        /**
         * Resets the current field value to the originally-loaded value and clears any validation messages.
         * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyCls}</b></tt> if the
         * original value was blank.
         */
        reset : function(){
            this.callParent();
            this.applyEmptyText();
        },

        applyEmptyText : function(){
            var me = this,
                emptyText = me.emptyText,
                isEmpty;

            if (me.rendered && emptyText) {
                isEmpty = me.getRawValue().length < 1 && !me.hasFocus;

                if (Ext.supports.Placeholder) {
                    me.inputEl.dom.placeholder = emptyText;
                } else if (isEmpty) {
                    me.setRawValue(emptyText);
                }

                //all browsers need this because of a styling issue with chrome + placeholders.
                //the text isnt vertically aligned when empty (and using the placeholder)
                if (isEmpty) {
                    me.inputEl.addCls(me.emptyCls);
                }

                me.autoSize();
            }
        },

        // private
        preFocus : function(){
            var me = this,
                inputEl = me.inputEl,
                emptyText = me.emptyText,
                isEmpty;

            if (emptyText && !Ext.supports.Placeholder && inputEl.dom.value === emptyText) {
                me.setRawValue('');
                isEmpty = true;
                inputEl.removeCls(me.emptyCls);
            } else if (Ext.supports.Placeholder) {
                me.inputEl.removeCls(me.emptyCls);
            }
            if (me.selectOnFocus || isEmpty) {
                inputEl.dom.select();
            }
        },

        onFocus: function() {
            var me = this;
            me.callParent(arguments);
            if (me.emptyText) {
                me.autoSize();
            }
        },

        // private
        postBlur : function(){
            this.applyEmptyText();
        },

        // private
        filterKeys : function(e){
            if(e.ctrlKey){
                return;
            }
            var key = e.getKey(),
                charCode = String.fromCharCode(e.getCharCode());

            if(Ext.isGecko && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){
                return;
            }

            if(!Ext.isGecko && e.isSpecialKey() && !charCode){
                return;
            }
            if(!this.maskRe.test(charCode)){
                e.stopEvent();
            }
        },

        /**
         * Returns the raw String value of the field, without performing any normalization, conversion, or validation.
         * Gets the current value of the input element if the field has been rendered, ignoring the value if it is the
         * {@link #emptyText}. To get a normalized and converted value see {@link #getValue}.
         * @return {String} value The raw String value of the field
         */
        getRawValue: function() {
            var me = this,
                v = me.callParent();
            if (v === me.emptyText) {
                v = '';
            }
            return v;
        },

        /**
         * Sets a data value into the field and runs the change detection and validation. Also applies any configured
         * {@link #emptyText} for text fields. To set the value directly without these inspections see {@link #setRawValue}.
         * @param {Mixed} value The value to set
         * @return {Ext.form.field.Text} this
         */
        setValue: function(value) {
            var me = this,
                inputEl = me.inputEl;

            if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
                inputEl.removeCls(me.emptyCls);
            }

            me.callParent(arguments);

            me.applyEmptyText();
            return me;
        },

        /**
    Validates a value according to the field's validation rules and returns an array of errors
    for any failing validations. Validation rules are processed in the following order:

    1. **Field specific validator**

        A validator offers a way to customize and reuse a validation specification.
        If a field is configured with a `{@link #validator}`
        function, it will be passed the current field value.  The `{@link #validator}`
        function is expected to return either:

        - Boolean `true`  if the value is valid (validation continues).
        - a String to represent the invalid message if invalid (validation halts).

    2. **Basic Validation**

        If the `{@link #validator}` has not halted validation,
        basic validation proceeds as follows:

        - `{@link #allowBlank}` : (Invalid message = `{@link #emptyText}`)

            Depending on the configuration of <code>{@link #allowBlank}</code>, a
            blank field will cause validation to halt at this step and return
            Boolean true or false accordingly.

        - `{@link #minLength}` : (Invalid message = `{@link #minLengthText}`)

            If the passed value does not satisfy the `{@link #minLength}`
            specified, validation halts.

        -  `{@link #maxLength}` : (Invalid message = `{@link #maxLengthText}`)

            If the passed value does not satisfy the `{@link #maxLength}`
            specified, validation halts.

    3. **Preconfigured Validation Types (VTypes)**

        If none of the prior validation steps halts validation, a field
        configured with a `{@link #vtype}` will utilize the
        corresponding {@link Ext.form.field.VTypes VTypes} validation function.
        If invalid, either the field's `{@link #vtypeText}` or
        the VTypes vtype Text property will be used for the invalid message.
        Keystrokes on the field will be filtered according to the VTypes
        vtype Mask property.

    4. **Field specific regex test**

        If none of the prior validation steps halts validation, a field's
        configured <code>{@link #regex}</code> test will be processed.
        The invalid message for this test is configured with `{@link #regexText}`

         * @param {Mixed} value The value to validate. The processed raw value will be used if nothing is passed
         * @return {Array} Array of any validation errors
         * @markdown
         */
        getErrors: function(value) {
            var me = this,
                errors = me.callParent(arguments),
                validator = me.validator,
                emptyText = me.emptyText,
                allowBlank = me.allowBlank,
                vtype = me.vtype,
                vtypes = Ext.form.field.VTypes,
                regex = me.regex,
                format = Ext.String.format,
                msg;

            value = value || me.processRawValue(me.getRawValue());

            if (Ext.isFunction(validator)) {
                msg = validator.call(me, value);
                if (msg !== true) {
                    errors.push(msg);
                }
            }

            if (value.length < 1 || value === emptyText) {
                if (!allowBlank) {
                    errors.push(me.blankText);
                }
                //if value is blank, there cannot be any additional errors
                return errors;
            }

            if (value.length < me.minLength) {
                errors.push(format(me.minLengthText, me.minLength));
            }

            if (value.length > me.maxLength) {
                errors.push(format(me.maxLengthText, me.maxLength));
            }

            if (vtype) {
                if(!vtypes[vtype](value, me)){
                    errors.push(me.vtypeText || vtypes[vtype +'Text']);
                }
            }

            if (regex && !regex.test(value)) {
                errors.push(me.regexText || me.invalidText);
            }

            return errors;
        },

        /**
         * Selects text in this field
         * @param {Number} start (optional) The index where the selection should start (defaults to 0)
         * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
         */
        selectText : function(start, end){
            var me = this,
                v = me.getRawValue(),
                doFocus = true,
                el = me.inputEl.dom,
                undef,
                range;

            if (v.length > 0) {
                start = start === undef ? 0 : start;
                end = end === undef ? v.length : end;
                if (el.setSelectionRange) {
                    el.setSelectionRange(start, end);
                }
                else if(el.createTextRange) {
                    range = el.createTextRange();
                    range.moveStart('character', start);
                    range.moveEnd('character', end - v.length);
                    range.select();
                }
                doFocus = Ext.isGecko || Ext.isOpera;
            }
            if (doFocus) {
                me.focus();
            }
        },

        /**
         * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
         * This only takes effect if <tt>{@link #grow} = true</tt>, and fires the {@link #autosize} event if the
         * width changes.
         */
        autoSize: function() {
            var me = this,
                width;
            if (me.grow && me.rendered) {
                me.doComponentLayout();
                width = me.inputEl.getWidth();
                if (width !== me.lastInputWidth) {
                    me.fireEvent('autosize', width);
                    me.lastInputWidth = width;
                }
            }
        },

        initAria: function() {
            this.callParent();
            this.getActionEl().dom.setAttribute('aria-required', this.allowBlank === false);
        },

        /**
         * @protected override
         * To get the natural width of the inputEl, we do a simple calculation based on the
         * 'size' config. We use hard-coded numbers to approximate what browsers do natively,
         * to avoid having to read any styles which would hurt performance.
         */
        getBodyNaturalWidth: function() {
            return Math.round(this.size * 6.5) + 20;
        }

    });