3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @docauthor Jason Johnston <jason@sencha.com>
18 * A basic text field. Can be used as a direct replacement for traditional text inputs,
19 * or as the base class for more sophisticated input controls (like {@link Ext.form.field.TextArea}
20 * and {@link Ext.form.field.ComboBox}). Has support for empty-field placeholder values (see {@link #emptyText}).
24 * The Text field has a useful set of validations built in:
26 * - {@link #allowBlank} for making the field required
27 * - {@link #minLength} for requiring a minimum value length
28 * - {@link #maxLength} for setting a maximum value length (with {@link #enforceMaxLength} to add it
29 * as the `maxlength` attribute on the input element)
30 * - {@link #regex} to specify a custom regular expression for validation
32 * In addition, custom validations may be added:
34 * - {@link #vtype} specifies a virtual type implementation from {@link Ext.form.field.VTypes} which can contain
35 * custom validation logic
36 * - {@link #validator} allows a custom arbitrary function to be called during validation
38 * The details around how and when each of these validation options get used are described in the
39 * documentation for {@link #getErrors}.
41 * By default, the field value is checked for validity immediately while the user is typing in the
42 * field. This can be controlled with the {@link #validateOnChange}, {@link #checkChangeEvents}, and
43 * {@link #checkChangeBuffer} configurations. Also see the details on Form Validation in the
44 * {@link Ext.form.Panel} class documentation.
46 * # Masking and Character Stripping
48 * Text fields can be configured with custom regular expressions to be applied to entered values before
49 * validation: see {@link #maskRe} and {@link #stripCharsRe} for details.
54 * Ext.create('Ext.form.Panel', {
55 * title: 'Contact Info',
58 * renderTo: Ext.getBody(),
63 * allowBlank: false // requires a non-empty value
67 * fieldLabel: 'Email Address',
68 * vtype: 'email' // requires value to be a valid email address format
72 Ext.define('Ext.form.field.Text', {
73 extend:'Ext.form.field.Base',
74 alias: 'widget.textfield',
75 requires: ['Ext.form.field.VTypes', 'Ext.layout.component.field.Text'],
76 alternateClassName: ['Ext.form.TextField', 'Ext.form.Text'],
79 * @cfg {String} vtypeText
80 * A custom error message to display in place of the default message provided for the **`{@link #vtype}`** currently
81 * set for this field. **Note**: only applies if **`{@link #vtype}`** is set, else ignored.
85 * @cfg {RegExp} stripCharsRe
86 * A JavaScript RegExp object used to strip unwanted content from the value
87 * before validation. If <tt>stripCharsRe</tt> is specified,
88 * every character matching <tt>stripCharsRe</tt> will be removed before fed to validation.
89 * This does not change the value of the field.
94 * An initial value for the 'size' attribute on the text input element. This is only used if the field has no
95 * configured {@link #width} and is not given a width by its container's layout. Defaults to 20.
100 * @cfg {Boolean} [grow=false]
101 * true if this field should automatically grow and shrink to its content
105 * @cfg {Number} growMin
106 * The minimum width to allow when `{@link #grow} = true`
111 * @cfg {Number} growMax
112 * The maximum width to allow when `{@link #grow} = true`
117 * @cfg {String} growAppend
118 * A string that will be appended to the field's current value for the purposes of calculating the target field
119 * size. Only used when the {@link #grow} config is true. Defaults to a single capital "W" (the widest character in
120 * common fonts) to leave enough space for the next typed character and avoid the field value shifting before the
126 * @cfg {String} vtype
127 * A validation type name as defined in {@link Ext.form.field.VTypes}
131 * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes (character being
132 * typed) that do not match.
133 * Note: It dose not filter characters already in the input.
137 * @cfg {Boolean} [disableKeyFilter=false]
138 * Specify true to disable input keystroke filtering
142 * @cfg {Boolean} allowBlank
143 * Specify false to validate that the value's length is > 0
148 * @cfg {Number} minLength
149 * Minimum input field length required
154 * @cfg {Number} maxLength
155 * Maximum input field length allowed by validation (defaults to Number.MAX_VALUE). This behavior is intended to
156 * provide instant feedback to the user by improving usability to allow pasting and editing or overtyping and back
157 * tracking. To restrict the maximum number of characters that can be entered into the field use the **{@link
158 * Ext.form.field.Text#enforceMaxLength enforceMaxLength}** option.
160 maxLength : Number.MAX_VALUE,
163 * @cfg {Boolean} enforceMaxLength
164 * True to set the maxLength property on the underlying input field. Defaults to false
168 * @cfg {String} minLengthText
169 * Error text to display if the **{@link #minLength minimum length}** validation fails.
171 minLengthText : 'The minimum length for this field is {0}',
174 * @cfg {String} maxLengthText
175 * Error text to display if the **{@link #maxLength maximum length}** validation fails
177 maxLengthText : 'The maximum length for this field is {0}',
180 * @cfg {Boolean} [selectOnFocus=false]
181 * true to automatically select any existing field text when the field receives input focus
185 * @cfg {String} blankText
186 * The error text to display if the **{@link #allowBlank}** validation fails
188 blankText : 'This field is required',
191 * @cfg {Function} validator
192 * A custom validation function to be called during field validation ({@link #getErrors}).
193 * If specified, this function will be called first, allowing the developer to override the default validation
196 * This function will be passed the following parameters:
198 * @cfg {Object} validator.value The current field value
199 * @cfg {Boolean/String} validator.return
201 * - True if the value is valid
202 * - An error message if the value is invalid
206 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation.
207 * If the test fails, the field will be marked invalid using
208 * either <b><tt>{@link #regexText}</tt></b> or <b><tt>{@link #invalidText}</tt></b>.
212 * @cfg {String} regexText
213 * The error text to display if **{@link #regex}** is used and the test fails during validation
218 * @cfg {String} emptyText
219 * The default text to place into an empty field.
221 * Note that normally this value will be submitted to the server if this field is enabled; to prevent this you can
222 * set the {@link Ext.form.action.Action#submitEmptyText submitEmptyText} option of {@link Ext.form.Basic#submit} to
225 * Also note that if you use {@link #inputType inputType}:'file', {@link #emptyText} is not supported and should be
230 * @cfg {String} [emptyCls='x-form-empty-field']
231 * The CSS class to apply to an empty field to style the **{@link #emptyText}**.
232 * This class is automatically added and removed as needed depending on the current field value.
234 emptyCls : Ext.baseCSSPrefix + 'form-empty-field',
239 * @cfg {Boolean} [enableKeyEvents=false]
240 * true to enable the proxying of key events for the HTML input field
243 componentLayout: 'textfield',
245 initComponent : function(){
250 * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the
251 * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the
252 * developer to apply additional logic at runtime to resize the field if needed.
253 * @param {Ext.form.field.Text} this This text field
254 * @param {Number} width The new field width
260 * Keydown input field event. This event only fires if **{@link #enableKeyEvents}** is set to true.
261 * @param {Ext.form.field.Text} this This text field
262 * @param {Ext.EventObject} e
267 * Keyup input field event. This event only fires if **{@link #enableKeyEvents}** is set to true.
268 * @param {Ext.form.field.Text} this This text field
269 * @param {Ext.EventObject} e
274 * Keypress input field event. This event only fires if **{@link #enableKeyEvents}** is set to true.
275 * @param {Ext.form.field.Text} this This text field
276 * @param {Ext.EventObject} e
283 initEvents : function(){
288 if(me.selectOnFocus || me.emptyText){
289 me.mon(el, 'mousedown', me.onMouseDown, me);
291 if(me.maskRe || (me.vtype && me.disableKeyFilter !== true && (me.maskRe = Ext.form.field.VTypes[me.vtype+'Mask']))){
292 me.mon(el, 'keypress', me.filterKeys, me);
295 if (me.enableKeyEvents) {
299 keydown: me.onKeyDown,
300 keypress: me.onKeyPress
307 * Override. Treat undefined and null values as equal to an empty string value.
309 isEqual: function(value1, value2) {
310 return this.isEqualAsString(value1, value2);
315 * If grow=true, invoke the autoSize method when the field's value is changed.
317 onChange: function() {
322 afterRender: function(){
324 if (me.enforceMaxLength) {
325 me.inputEl.dom.maxLength = me.maxLength;
332 onMouseDown: function(e){
335 me.mon(me.inputEl, 'mouseup', Ext.emptyFn, me, { single: true, preventDefault: true });
340 * Performs any necessary manipulation of a raw String value to prepare it for conversion and/or
341 * {@link #validate validation}. For text fields this applies the configured {@link #stripCharsRe}
343 * @param {String} value The unprocessed string value
344 * @return {String} The processed string value
346 processRawValue: function(value) {
348 stripRe = me.stripCharsRe,
352 newValue = value.replace(stripRe, '');
353 if (newValue !== value) {
354 me.setRawValue(newValue);
362 onDisable: function(){
365 this.inputEl.dom.unselectable = 'on';
370 onEnable: function(){
373 this.inputEl.dom.unselectable = '';
377 onKeyDown: function(e) {
378 this.fireEvent('keydown', this, e);
381 onKeyUp: function(e) {
382 this.fireEvent('keyup', this, e);
385 onKeyPress: function(e) {
386 this.fireEvent('keypress', this, e);
390 * Resets the current field value to the originally-loaded value and clears any validation messages.
391 * Also adds **{@link #emptyText}** and **{@link #emptyCls}** if the original value was blank.
395 this.applyEmptyText();
398 applyEmptyText : function(){
400 emptyText = me.emptyText,
403 if (me.rendered && emptyText) {
404 isEmpty = me.getRawValue().length < 1 && !me.hasFocus;
406 if (Ext.supports.Placeholder) {
407 me.inputEl.dom.placeholder = emptyText;
408 } else if (isEmpty) {
409 me.setRawValue(emptyText);
412 //all browsers need this because of a styling issue with chrome + placeholders.
413 //the text isnt vertically aligned when empty (and using the placeholder)
415 me.inputEl.addCls(me.emptyCls);
423 preFocus : function(){
425 inputEl = me.inputEl,
426 emptyText = me.emptyText,
429 if (emptyText && !Ext.supports.Placeholder && inputEl.dom.value === emptyText) {
432 inputEl.removeCls(me.emptyCls);
433 } else if (Ext.supports.Placeholder) {
434 me.inputEl.removeCls(me.emptyCls);
436 if (me.selectOnFocus || isEmpty) {
437 inputEl.dom.select();
441 onFocus: function() {
443 me.callParent(arguments);
450 postBlur : function(){
451 this.applyEmptyText();
455 filterKeys : function(e){
457 * On European keyboards, the right alt key, Alt Gr, is used to type certain special characters.
458 * JS detects a keypress of this as ctrlKey & altKey. As such, we check that alt isn't pressed
459 * so we can still process these special characters.
461 if (e.ctrlKey && !e.altKey) {
464 var key = e.getKey(),
465 charCode = String.fromCharCode(e.getCharCode());
467 if(Ext.isGecko && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){
471 if(!Ext.isGecko && e.isSpecialKey() && !charCode){
474 if(!this.maskRe.test(charCode)){
480 * Returns the raw String value of the field, without performing any normalization, conversion, or validation. Gets
481 * the current value of the input element if the field has been rendered, ignoring the value if it is the
482 * {@link #emptyText}. To get a normalized and converted value see {@link #getValue}.
483 * @return {String} The raw String value of the field
485 getRawValue: function() {
488 if (v === me.emptyText) {
495 * Sets a data value into the field and runs the change detection and validation. Also applies any configured
496 * {@link #emptyText} for text fields. To set the value directly without these inspections see {@link #setRawValue}.
497 * @param {Object} value The value to set
498 * @return {Ext.form.field.Text} this
500 setValue: function(value) {
502 inputEl = me.inputEl;
504 if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
505 inputEl.removeCls(me.emptyCls);
508 me.callParent(arguments);
515 * Validates a value according to the field's validation rules and returns an array of errors
516 * for any failing validations. Validation rules are processed in the following order:
518 * 1. **Field specific validator**
520 * A validator offers a way to customize and reuse a validation specification.
521 * If a field is configured with a `{@link #validator}`
522 * function, it will be passed the current field value. The `{@link #validator}`
523 * function is expected to return either:
525 * - Boolean `true` if the value is valid (validation continues).
526 * - a String to represent the invalid message if invalid (validation halts).
528 * 2. **Basic Validation**
530 * If the `{@link #validator}` has not halted validation,
531 * basic validation proceeds as follows:
533 * - `{@link #allowBlank}` : (Invalid message = `{@link #emptyText}`)
535 * Depending on the configuration of `{@link #allowBlank}`, a
536 * blank field will cause validation to halt at this step and return
537 * Boolean true or false accordingly.
539 * - `{@link #minLength}` : (Invalid message = `{@link #minLengthText}`)
541 * If the passed value does not satisfy the `{@link #minLength}`
542 * specified, validation halts.
544 * - `{@link #maxLength}` : (Invalid message = `{@link #maxLengthText}`)
546 * If the passed value does not satisfy the `{@link #maxLength}`
547 * specified, validation halts.
549 * 3. **Preconfigured Validation Types (VTypes)**
551 * If none of the prior validation steps halts validation, a field
552 * configured with a `{@link #vtype}` will utilize the
553 * corresponding {@link Ext.form.field.VTypes VTypes} validation function.
554 * If invalid, either the field's `{@link #vtypeText}` or
555 * the VTypes vtype Text property will be used for the invalid message.
556 * Keystrokes on the field will be filtered according to the VTypes
557 * vtype Mask property.
559 * 4. **Field specific regex test**
561 * If none of the prior validation steps halts validation, a field's
562 * configured <code>{@link #regex}</code> test will be processed.
563 * The invalid message for this test is configured with `{@link #regexText}`
565 * @param {Object} value The value to validate. The processed raw value will be used if nothing is passed.
566 * @return {String[]} Array of any validation errors
568 getErrors: function(value) {
570 errors = me.callParent(arguments),
571 validator = me.validator,
572 emptyText = me.emptyText,
573 allowBlank = me.allowBlank,
575 vtypes = Ext.form.field.VTypes,
577 format = Ext.String.format,
580 value = value || me.processRawValue(me.getRawValue());
582 if (Ext.isFunction(validator)) {
583 msg = validator.call(me, value);
589 if (value.length < 1 || value === emptyText) {
591 errors.push(me.blankText);
593 //if value is blank, there cannot be any additional errors
597 if (value.length < me.minLength) {
598 errors.push(format(me.minLengthText, me.minLength));
601 if (value.length > me.maxLength) {
602 errors.push(format(me.maxLengthText, me.maxLength));
606 if(!vtypes[vtype](value, me)){
607 errors.push(me.vtypeText || vtypes[vtype +'Text']);
611 if (regex && !regex.test(value)) {
612 errors.push(me.regexText || me.invalidText);
619 * Selects text in this field
620 * @param {Number} [start=0] The index where the selection should start
621 * @param {Number} [end] The index where the selection should end (defaults to the text length)
623 selectText : function(start, end){
625 v = me.getRawValue(),
632 start = start === undef ? 0 : start;
633 end = end === undef ? v.length : end;
634 if (el.setSelectionRange) {
635 el.setSelectionRange(start, end);
637 else if(el.createTextRange) {
638 range = el.createTextRange();
639 range.moveStart('character', start);
640 range.moveEnd('character', end - v.length);
643 doFocus = Ext.isGecko || Ext.isOpera;
651 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed. This
652 * only takes effect if {@link #grow} = true, and fires the {@link #autosize} event if the width changes.
654 autoSize: function() {
657 if (me.grow && me.rendered) {
658 me.doComponentLayout();
659 width = me.inputEl.getWidth();
660 if (width !== me.lastInputWidth) {
661 me.fireEvent('autosize', width);
662 me.lastInputWidth = width;
667 initAria: function() {
669 this.getActionEl().dom.setAttribute('aria-required', this.allowBlank === false);
673 * To get the natural width of the inputEl, we do a simple calculation based on the 'size' config. We use
674 * hard-coded numbers to approximate what browsers do natively, to avoid having to read any styles which would hurt
675 * performance. Overrides Labelable method.
678 getBodyNaturalWidth: function() {
679 return Math.round(this.size * 6.5) + 20;