]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/form/field/Text.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / form / field / Text.js
CommitLineData
6527f429
DM
1/**\r
2 * A basic text field. Can be used as a direct replacement for traditional text inputs,\r
3 * or as the base class for more sophisticated input controls (like {@link Ext.form.field.TextArea}\r
4 * and {@link Ext.form.field.ComboBox}). Has support for empty-field placeholder values (see {@link #emptyText}).\r
5 *\r
6 * # Validation\r
7 *\r
8 * The Text field has a useful set of validations built in:\r
9 *\r
10 * - {@link #allowBlank} for making the field required\r
11 * - {@link #minLength} for requiring a minimum value length\r
12 * - {@link #maxLength} for setting a maximum value length (with {@link #enforceMaxLength} to add it\r
13 * as the `maxlength` attribute on the input element)\r
14 * - {@link #regex} to specify a custom regular expression for validation\r
15 *\r
16 * In addition, custom validations may be added:\r
17 *\r
18 * - {@link #vtype} specifies a virtual type implementation from {@link Ext.form.field.VTypes} which can contain\r
19 * custom validation logic\r
20 * - {@link #validator} allows a custom arbitrary function to be called during validation\r
21 *\r
22 * The details around how and when each of these validation options get used are described in the\r
23 * documentation for {@link #getErrors}.\r
24 *\r
25 * By default, the field value is checked for validity immediately while the user is typing in the\r
26 * field. This can be controlled with the {@link #validateOnChange}, {@link #checkChangeEvents}, and\r
27 * {@link #checkChangeBuffer} configurations. Also see the details on Form Validation in the\r
28 * {@link Ext.form.Panel} class documentation.\r
29 *\r
30 * # Masking and Character Stripping\r
31 *\r
32 * Text fields can be configured with custom regular expressions to be applied to entered values before\r
33 * validation: see {@link #maskRe} and {@link #stripCharsRe} for details.\r
34 *\r
35 * # Example usage\r
36 *\r
37 * @example\r
38 * Ext.create('Ext.form.Panel', {\r
39 * title: 'Contact Info',\r
40 * width: 300,\r
41 * bodyPadding: 10,\r
42 * renderTo: Ext.getBody(),\r
43 * items: [{\r
44 * xtype: 'textfield',\r
45 * name: 'name',\r
46 * fieldLabel: 'Name',\r
47 * allowBlank: false // requires a non-empty value\r
48 * }, {\r
49 * xtype: 'textfield',\r
50 * name: 'email',\r
51 * fieldLabel: 'Email Address',\r
52 * vtype: 'email' // requires value to be a valid email address format\r
53 * }]\r
54 * });\r
55 *\r
56 * # Custom Subclasses\r
57 *\r
58 * This class can be extended to provide additional functionality. The example below demonstrates creating\r
59 * a custom search field that uses the HTML5 search input type.\r
60 *\r
61 * @example\r
62 * // A simple subclass of Base that creates a HTML5 search field. Redirects to the\r
63 * // searchUrl when the Enter key is pressed.222\r
64 * Ext.define('Ext.form.SearchField', {\r
65 * extend: 'Ext.form.field.Text',\r
66 * alias: 'widget.searchfield',\r
67 * \r
68 * inputType: 'search',\r
69 * \r
70 * // Config defining the search URL\r
71 * searchUrl: 'http://www.google.com/search?q={0}',\r
72 * \r
73 * // Add specialkey listener\r
74 * initComponent: function() {\r
75 * this.callParent();\r
76 * this.on('specialkey', this.checkEnterKey, this);\r
77 * },\r
78 * \r
79 * // Handle enter key presses, execute the search if the field has a value\r
80 * checkEnterKey: function(field, e) {\r
81 * var value = this.getValue();\r
82 * if (e.getKey() === e.ENTER && !Ext.isEmpty(value)) {\r
83 * location.href = Ext.String.format(this.searchUrl, value);\r
84 * }\r
85 * }\r
86 * });\r
87 * \r
88 * Ext.create('Ext.form.Panel', {\r
89 * title: 'Base Example',\r
90 * bodyPadding: 5,\r
91 * width: 250,\r
92 * \r
93 * // Fields will be arranged vertically, stretched to full width\r
94 * layout: 'anchor',\r
95 * defaults: {\r
96 * anchor: '100%'\r
97 * },\r
98 * items: [{\r
99 * xtype: 'searchfield',\r
100 * fieldLabel: 'Search',\r
101 * name: 'query'\r
102 * }],\r
103 * renderTo: Ext.getBody()\r
104 * });\r
105 */\r
106Ext.define('Ext.form.field.Text', {\r
107 extend:'Ext.form.field.Base',\r
108 alias: 'widget.textfield',\r
109 requires: [\r
110 'Ext.form.field.VTypes',\r
111 'Ext.form.trigger.Trigger',\r
112 'Ext.util.TextMetrics'\r
113 ],\r
114 alternateClassName: ['Ext.form.TextField', 'Ext.form.Text'],\r
115\r
116 config: {\r
117 /**\r
118 * @cfg {Boolean} hideTrigger\r
119 * `true` to hide all triggers\r
120 */\r
121 hideTrigger: false,\r
122\r
123 // @cmd-auto-dependency {aliasPrefix: "trigger.", isKeyedObject: true}\r
124 /**\r
125 * @cfg {Object} triggers\r
126 * {@link Ext.form.trigger.Trigger Triggers} to use in this field. The keys in\r
127 * this object are unique identifiers for the triggers. The values in this object\r
128 * are {@link Ext.form.trigger.Trigger Trigger} configuration objects.\r
129 *\r
130 * Ext.create('Ext.form.field.Text', {\r
131 * renderTo: document.body,\r
132 * fieldLabel: 'My Custom Field',\r
133 * triggers: {\r
134 * foo: {\r
135 * cls: 'my-foo-trigger',\r
136 * handler: function() {\r
137 * console.log('foo trigger clicked');\r
138 * }\r
139 * },\r
140 * bar: {\r
141 * cls: 'my-bar-trigger',\r
142 * handler: function() {\r
143 * console.log('bar trigger clicked');\r
144 * }\r
145 * }\r
146 * }\r
147 * });\r
148 * \r
149 * The weight value may be a negative value in order to position custom triggers \r
150 * ahead of default triggers like that of ComboBox.\r
151 * \r
152 * Ext.create('Ext.form.field.ComboBox', {\r
153 * renderTo: Ext.getBody(),\r
154 * fieldLabel: 'My Custom Field',\r
155 * triggers: {\r
156 * foo: {\r
157 * cls: 'my-foo-trigger',\r
158 * weight: -2, // negative to place before default triggers\r
159 * handler: function() {\r
160 * console.log('foo trigger clicked');\r
161 * }\r
162 * },\r
163 * bar: {\r
164 * cls: 'my-bar-trigger',\r
165 * weight: -1, \r
166 * handler: function() {\r
167 * console.log('bar trigger clicked');\r
168 * }\r
169 * }\r
170 * }\r
171 * });\r
172 */\r
173 triggers: undefined\r
174 },\r
175 \r
176 renderConfig: {\r
177 /**\r
178 * @cfg {Boolean} editable\r
179 * false to prevent the user from typing text directly into the field; the field can\r
180 * only have its value set programmatically or via an action invoked by a trigger.\r
181 */\r
182 editable: true\r
183 },\r
184\r
185 /**\r
186 * @cfg {String} vtypeText\r
187 * A custom error message to display in place of the default message provided for the **`{@link #vtype}`** currently\r
188 * set for this field. **Note**: only applies if **`{@link #vtype}`** is set, else ignored.\r
189 */\r
190\r
191 /**\r
192 * @cfg {RegExp} stripCharsRe\r
193 * A JavaScript RegExp object used to strip unwanted content from the value\r
194 * during input. If `stripCharsRe` is specified,\r
195 * every *character sequence* matching `stripCharsRe` will be removed.\r
196 */\r
197\r
198 /**\r
199 * @cfg {Number} size\r
200 * An initial value for the 'size' attribute on the text input element. This is only\r
201 * used if the field has no configured {@link #width} and is not given a width by its\r
202 * container's layout. Defaults to 20.\r
203 * @deprecated use {@link #width} instead.\r
204 */\r
205\r
206 /**\r
207 * @cfg {Boolean} [grow=false]\r
208 * true if this field should automatically grow and shrink to its content\r
209 */\r
210\r
211 /**\r
212 * @cfg {Number} growMin\r
213 * The minimum width to allow when `{@link #grow} = true`\r
214 */\r
215 growMin : 30,\r
216\r
217 /**\r
218 * @cfg {Number} growMax\r
219 * The maximum width to allow when `{@link #grow} = true`\r
220 */\r
221 growMax : 800,\r
222\r
223 //<locale>\r
224 /**\r
225 * @cfg {String} growAppend\r
226 * A string that will be appended to the field's current value for the purposes of calculating the target field\r
227 * size. Only used when the {@link #grow} config is true. Defaults to a single capital "W" (the widest character in\r
228 * common fonts) to leave enough space for the next typed character and avoid the field value shifting before the\r
229 * width is adjusted.\r
230 */\r
231 growAppend: 'W',\r
232 //</locale>\r
233\r
234 /**\r
235 * @cfg {String} vtype\r
236 * A validation type name as defined in {@link Ext.form.field.VTypes}\r
237 */\r
238\r
239 /**\r
240 * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes (character being\r
241 * typed) that do not match.\r
242 * Note: It does not filter characters already in the input.\r
243 */\r
244\r
245 /**\r
246 * @cfg {Boolean} [disableKeyFilter=false]\r
247 * Specify true to disable input keystroke filtering\r
248 */\r
249\r
250 /**\r
251 * @cfg {Boolean} [allowBlank=true]\r
252 * Specify false to validate that the value's length must be > 0. If `true`, then a blank value is **always** taken to be valid regardless of any {@link #vtype}\r
253 * validation that may be applied.\r
254 *\r
255 * If {@link #vtype} validation must still be applied to blank values, configure {@link #validateBlank} as `true`;\r
256 */\r
257 allowBlank : true,\r
258\r
259 /**\r
260 * @cfg {Boolean} [validateBlank=false]\r
261 * Specify as `true` to modify the behaviour of {@link #allowBlank} so that blank values are not passed as valid, but are subject to any configure {@link #vtype} validation.\r
262 */\r
263 validateBlank: false,\r
264\r
265 /**\r
266 * @cfg {Boolean} allowOnlyWhitespace\r
267 * Specify false to automatically trim the value before validating\r
268 * the whether the value is blank. Setting this to false automatically\r
269 * sets {@link #allowBlank} to false.\r
270 */\r
271 allowOnlyWhitespace: true,\r
272\r
273 /**\r
274 * @cfg {Number} minLength\r
275 * Minimum input field length required\r
276 */\r
277 minLength : 0,\r
278\r
279 /**\r
280 * @cfg {Number} maxLength\r
281 * Maximum input field length allowed by validation. This behavior is intended to\r
282 * provide instant feedback to the user by improving usability to allow pasting and editing or overtyping and back\r
283 * tracking. To restrict the maximum number of characters that can be entered into the field use the\r
284 * **{@link Ext.form.field.Text#enforceMaxLength enforceMaxLength}** option.\r
285 *\r
286 * Defaults to Number.MAX_VALUE.\r
287 */\r
288 maxLength : Number.MAX_VALUE,\r
289\r
290 /**\r
291 * @cfg {Boolean} enforceMaxLength\r
292 * True to set the maxLength property on the underlying input field. Defaults to false\r
293 */\r
294\r
295 //<locale>\r
296 /**\r
297 * @cfg {String} minLengthText\r
298 * Error text to display if the **{@link #minLength minimum length}** validation fails.\r
299 */\r
300 minLengthText : 'The minimum length for this field is {0}',\r
301 //</locale>\r
302\r
303 //<locale>\r
304 /**\r
305 * @cfg {String} maxLengthText\r
306 * Error text to display if the **{@link #maxLength maximum length}** validation fails\r
307 */\r
308 maxLengthText : 'The maximum length for this field is {0}',\r
309 //</locale>\r
310\r
311 /**\r
312 * @cfg {Boolean} [selectOnFocus=false]\r
313 * `true` to automatically select any existing field text when the field receives input\r
314 * focus. Only applies when {@link #editable editable} = true\r
315 */\r
316\r
317 //<locale>\r
318 /**\r
319 * @cfg {String} blankText\r
320 * The error text to display if the **{@link #allowBlank}** validation fails\r
321 */\r
322 blankText : 'This field is required',\r
323 //</locale>\r
324\r
325 /**\r
326 * @cfg {Function} validator\r
327 * A custom validation function to be called during field validation ({@link #getErrors}).\r
328 * If specified, this function will be called first, allowing the developer to override the default validation\r
329 * process.\r
330 * \r
331 * Ext.create('Ext.form.field.Text', {\r
332 * renderTo: document.body,\r
333 * name: 'phone',\r
334 * fieldLabel: 'Phone Number',\r
335 * validator: function (val) {\r
336 * // remove non-numeric characters\r
337 * var tn = val.replace(/[^0-9]/g,''),\r
338 * errMsg = "Must be a 10 digit telephone number";\r
339 * // if the numeric value is not 10 digits return an error message\r
340 * return (tn.length === 10) ? true : errMsg;\r
341 * }\r
342 * });\r
343 *\r
344 * @param {Object} value The current field value\r
345 * @return {Boolean/String} response\r
346 *\r
347 * - True if the value is valid\r
348 * - An error message if the value is invalid\r
349 */\r
350\r
351 /**\r
352 * @cfg {RegExp} regex\r
353 * A JavaScript RegExp object to be tested against the field value during validation.\r
354 * If the test fails, the field will be marked invalid using\r
355 * either **{@link #regexText}** or **{@link #invalidText}**.\r
356 */\r
357\r
358 /**\r
359 * @cfg {String} regexText\r
360 * The error text to display if **{@link #regex}** is used and the test fails during validation\r
361 */\r
362 regexText : '',\r
363\r
364 /**\r
365 * @cfg {String} emptyText\r
366 * The default text to place into an empty field.\r
367 *\r
368 * Note that normally this value will be submitted to the server if this field is enabled; to prevent this you can\r
369 * set the {@link Ext.form.action.Action#submitEmptyText submitEmptyText} option of {@link Ext.form.Basic#submit} to\r
370 * false.\r
371 *\r
372 * Also note that if you use {@link #inputType inputType}:'file', {@link #emptyText} is not supported and should be\r
373 * avoided.\r
374 *\r
375 * Note that for browsers that support it, setting this property will use the HTML 5 placeholder attribute, and for\r
376 * older browsers that don't support the HTML 5 placeholder attribute the value will be placed directly into the input\r
377 * element itself as the raw value. This means that older browsers will obfuscate the {@link #emptyText} value for\r
378 * password input fields.\r
379 */\r
380\r
381 emptyText : '',\r
382\r
383 /**\r
384 * @cfg {String} [emptyCls='x-form-empty-field']\r
385 * The CSS class to apply to an empty field to style the **{@link #emptyText}**.\r
386 * This class is automatically added and removed as needed depending on the current field value.\r
387 */\r
388 emptyCls : Ext.baseCSSPrefix + 'form-empty-field',\r
389\r
390 /**\r
391 * @cfg {String} [requiredCls='x-form-required-field']\r
392 * The CSS class to apply to a required field, i.e. a field where **{@link #allowBlank}** is false.\r
393 */\r
394 requiredCls : Ext.baseCSSPrefix + 'form-required-field',\r
395\r
396 /**\r
397 * @cfg {Boolean} [enableKeyEvents=false]\r
398 * true to enable the proxying of key events for the HTML input field\r
399 */\r
400\r
401 /**\r
402 * @private\r
403 */\r
404 valueContainsPlaceholder : false,\r
405\r
406 ariaRole: 'textbox',\r
407\r
408 /**\r
409 * @cfg {Boolean} repeatTriggerClick\r
410 * `true` to attach a {@link Ext.util.ClickRepeater click repeater} to the trigger(s).\r
411 * Click repeating behavior can also be configured on the individual {@link #triggers\r
412 * trigger instances using the trigger's {@link {Ext.form.trigger.Trigger#repeatClick\r
413 * repeatClick} config.\r
414 */\r
415 repeatTriggerClick: false,\r
416\r
417 /**\r
418 * @cfg {Boolean} readOnly\r
419 * `true` to prevent the user from changing the field, and hide all triggers.\r
420 */\r
421\r
422 /**\r
423 * @cfg stateEvents\r
424 * @inheritdoc Ext.state.Stateful#cfg-stateEvents\r
425 * @localdoc By default the following stateEvents are added:\r
426 * \r
427 * - {@link #event-resize} - _(added by Ext.Component)_\r
428 * - {@link #event-change}\r
429 */\r
430\r
431 /**\r
432 * @cfg {String}\r
433 * The CSS class that is added to the div wrapping the input element and trigger button(s).\r
434 */\r
435 triggerWrapCls: Ext.baseCSSPrefix + 'form-trigger-wrap',\r
436\r
437 triggerWrapFocusCls: Ext.baseCSSPrefix + 'form-trigger-wrap-focus',\r
438 triggerWrapInvalidCls: Ext.baseCSSPrefix + 'form-trigger-wrap-invalid',\r
439\r
440 fieldBodyCls: Ext.baseCSSPrefix + 'form-text-field-body',\r
441\r
442 /**\r
443 * @cfg {String}\r
444 * The CSS class that is added to the element wrapping the input element\r
445 */\r
446 inputWrapCls: Ext.baseCSSPrefix + 'form-text-wrap',\r
447\r
448 inputWrapFocusCls: Ext.baseCSSPrefix + 'form-text-wrap-focus',\r
449 inputWrapInvalidCls: Ext.baseCSSPrefix + 'form-text-wrap-invalid',\r
450 growCls: Ext.baseCSSPrefix + 'form-text-grow',\r
451 \r
452 needArrowKeys: true,\r
453\r
454 // Listener block to preventDefault on the mouseup event..\r
455 // Observable rejects Ext.emptyFn as a no-op and the listener does not get added so the default does not get prevented.\r
456 // We do not want touchend events translated into mouseup, we only want to prevent default on real mouseup events.\r
457 squashMouseUp: {\r
458 mouseup: function(){},\r
459 translate: false,\r
460 single: true,\r
461 preventDefault: true\r
462 },\r
463\r
464 childEls: [\r
465 /**\r
466 * @property {Ext.dom.Element} triggerWrap\r
467 * A reference to the element which encapsulates the input field and all\r
468 * trigger button(s). Only set after the field has been rendered.\r
469 */\r
470 'triggerWrap',\r
471\r
472 /**\r
473 * @property {Ext.dom.Element} inputWrap\r
474 * A reference to the element that wraps the input element. Only set after the\r
475 * field has been rendered.\r
476 */\r
477 'inputWrap'\r
478 ],\r
479\r
480 preSubTpl: [\r
481 '<div id="{cmpId}-triggerWrap" data-ref="triggerWrap"',\r
482 ' role="presentation" class="{triggerWrapCls} {triggerWrapCls}-{ui}">',\r
483 '<div id={cmpId}-inputWrap data-ref="inputWrap"',\r
484 ' role="presentation" class="{inputWrapCls} {inputWrapCls}-{ui}">'\r
485 ],\r
486\r
487 postSubTpl: [\r
488 '</div>', // end inputWrap\r
489 '<tpl for="triggers">{[values.renderTrigger(parent)]}</tpl>',\r
490 '</div>' // end triggerWrap\r
491 ],\r
492\r
493 /**\r
494 * @event autosize\r
495 * Fires when the **{@link #autoSize}** function is triggered and the field is resized according to the\r
496 * {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result. This event provides a hook for the\r
497 * developer to apply additional logic at runtime to resize the field if needed.\r
498 * @param {Ext.form.field.Text} this This text field\r
499 * @param {Number} width The new field width\r
500 */\r
501\r
502 /**\r
503 * @event keydown\r
504 * Keydown input field event. This event only fires if **{@link #enableKeyEvents}** is set to true.\r
505 * @param {Ext.form.field.Text} this This text field\r
506 * @param {Ext.event.Event} e\r
507 */\r
508\r
509 /**\r
510 * @event keyup\r
511 * Keyup input field event. This event only fires if **{@link #enableKeyEvents}** is set to true.\r
512 * @param {Ext.form.field.Text} this This text field\r
513 * @param {Ext.event.Event} e\r
514 */\r
515\r
516 /**\r
517 * @event keypress\r
518 * Keypress input field event. This event only fires if **{@link #enableKeyEvents}** is set to true.\r
519 * @param {Ext.form.field.Text} this This text field\r
520 * @param {Ext.event.Event} e\r
521 */\r
522\r
523 initComponent: function () {\r
524 var me = this,\r
525 emptyCls = me.emptyCls;\r
526\r
527 if (me.allowOnlyWhitespace === false) {\r
528 me.allowBlank = false;\r
529 }\r
530\r
531 //<debug>\r
532 if (me.size) {\r
533 Ext.log.warn('Ext.form.field.Text "size" config was deprecated in Ext 5.0. Please specify a "width" or use a layout instead.');\r
534 }\r
535 //</debug>\r
536 // In Ext JS 4.x the layout system used the following magic formula for converting\r
537 // the "size" config into a pixel value.\r
538 if (me.size) {\r
539 me.defaultBodyWidth = me.size * 6.5 + 20;\r
540 }\r
541\r
542 if (!me.onTrigger1Click) {\r
543 // for compat with 4.x TriggerField\r
544 me.onTrigger1Click = me.onTriggerClick;\r
545 }\r
546\r
547 me.callParent();\r
548\r
549 if (me.readOnly) {\r
550 me.setReadOnly(me.readOnly);\r
551 }\r
552 me.fieldFocusCls = me.baseCls + '-focus';\r
553 me.emptyUICls = emptyCls + ' ' + emptyCls + '-' + me.ui;\r
554 me.addStateEvents('change');\r
555 },\r
556\r
557 initEvents: function(){\r
558 var me = this,\r
559 el = me.inputEl;\r
560\r
561 me.callParent();\r
562\r
563 // Workaround for https://code.google.com/p/chromium/issues/detail?id=4505\r
564 // On mousedown, add a single: true mouseup listener which prevents default.\r
565 // That will prevent deselection of the text that was selected in the onFocus method.\r
566 if(me.selectOnFocus || me.emptyText){\r
567 me.mon(el, 'mousedown', me.onMouseDown, me);\r
568 }\r
569 if(me.maskRe || (me.vtype && me.disableKeyFilter !== true && (me.maskRe = Ext.form.field.VTypes[me.vtype+'Mask']))){\r
570 me.mon(el, 'keypress', me.filterKeys, me);\r
571 }\r
572\r
573 if (me.enableKeyEvents) {\r
574 me.mon(el, {\r
575 scope: me,\r
576 keyup: me.onKeyUp,\r
577 keydown: me.onKeyDown,\r
578 keypress: me.onKeyPress\r
579 });\r
580 }\r
581 },\r
582\r
583 /**\r
584 * @private\r
585 * Treat undefined and null values as equal to an empty string value.\r
586 */\r
587 isEqual: function(value1, value2) {\r
588 return this.isEqualAsString(value1, value2);\r
589 },\r
590\r
591 /**\r
592 * @private\r
593 * If grow=true, invoke the autoSize method when the field's value is changed.\r
594 */\r
595 onChange: function(newVal, oldVal) {\r
596 this.callParent(arguments);\r
597 this.autoSize();\r
598 },\r
599\r
600 getSubTplData: function(fieldData) {\r
601 var me = this,\r
602 value = me.getRawValue(),\r
603 isEmpty = me.emptyText && value.length < 1,\r
604 maxLength = me.maxLength,\r
605 placeholder, data, inputElAttr;\r
606\r
607 // We can't just dump the value here, since MAX_VALUE ends up\r
608 // being something like 1.xxxxe+300, which gets interpreted as 1\r
609 // in the markup\r
610 if (me.enforceMaxLength) {\r
611 if (maxLength === Number.MAX_VALUE) {\r
612 maxLength = undefined;\r
613 }\r
614 } else {\r
615 maxLength = undefined;\r
616 }\r
617\r
618 if (isEmpty) {\r
619 if (Ext.supports.Placeholder) {\r
620 placeholder = me.emptyText;\r
621 } else {\r
622 value = me.emptyText;\r
623 me.valueContainsPlaceholder = true;\r
624 }\r
625 }\r
626\r
627 data = Ext.apply(me.callParent([fieldData]), {\r
628 triggerWrapCls: me.triggerWrapCls,\r
629 inputWrapCls: me.inputWrapCls,\r
630 triggers: me.orderedTriggers,\r
631 maxLength: maxLength,\r
632 readOnly: !me.editable || me.readOnly,\r
633 placeholder: placeholder,\r
634 value: value,\r
635 fieldCls: me.fieldCls + ((isEmpty && (placeholder || value)) ? ' ' + me.emptyUICls : '') + (me.allowBlank ? '' : ' ' + me.requiredCls)\r
636 });\r
637 \r
638 inputElAttr = data.inputElAriaAttributes;\r
639 \r
640 if (inputElAttr) {\r
641 inputElAttr['aria-required'] = !me.allowBlank;\r
642 }\r
643 \r
644 return data;\r
645 },\r
646\r
647 onRender: function() {\r
648 var me = this,\r
649 triggers = me.getTriggers(),\r
650 elements = [],\r
651 id, triggerEl;\r
652\r
653 if (Ext.supports.FixedTableWidthBug) {\r
654 // Workaround for https://bugs.webkit.org/show_bug.cgi?id=130239 and\r
655 // https://code.google.com/p/chromium/issues/detail?id=377190\r
656 // See styleHooks for more details\r
657 me.el._needsTableWidthFix = true;\r
658 }\r
659\r
660 me.callParent();\r
661\r
662 if (triggers) {\r
663 this.invokeTriggers('onFieldRender');\r
664\r
665 /**\r
666 * @property {Ext.CompositeElement} triggerEl\r
667 * @deprecated 5.0\r
668 * A composite of all the trigger button elements. Only set after the field has\r
669 * been rendered.\r
670 */\r
671 for(id in triggers) {\r
672 elements.push(triggers[id].el);\r
673 }\r
674 // for 4.x compat, also set triggerCell\r
675 triggerEl = me.triggerEl = me.triggerCell = new Ext.CompositeElement(elements, true);\r
676 }\r
677\r
678 /**\r
679 * @property {Ext.dom.Element} inputCell\r
680 * A reference to the element that wraps the input element. Only set after the\r
681 * field has been rendered.\r
682 * @deprecated 5.0 use {@link #inputWrap} instead\r
683 */\r
684 me.inputCell = me.inputWrap;\r
685 },\r
686\r
687 afterRender: function(){\r
688 var me = this;\r
689\r
690 me.autoSize();\r
691 me.callParent();\r
692 me.invokeTriggers('afterFieldRender');\r
693 },\r
694\r
695 onMouseDown: function(){\r
696 if (!this.hasFocus) {\r
697 // On the next mouseup, prevent default.\r
698 // 99% of the time, it will be the mouseup of the click into the field, and \r
699 // We will be preventing deselection of selected text: https://code.google.com/p/chromium/issues/detail?id=4505\r
700 // Listener is on the doc in case the pointer moves out before user lets go.\r
701 Ext.getDoc().on(this.squashMouseUp);\r
702 }\r
703 },\r
704\r
705 applyTriggers: function(triggers) {\r
706 var me = this,\r
707 hideAllTriggers = me.getHideTrigger(),\r
708 readOnly = me.readOnly,\r
709 orderedTriggers = me.orderedTriggers = [],\r
710 repeatTriggerClick = me.repeatTriggerClick,\r
711 id, triggerCfg, trigger, triggerCls, i;\r
712\r
713 //<debug>\r
714 if (me.rendered) {\r
715 Ext.raise("Cannot set triggers after field has already been rendered.");\r
716 }\r
717\r
718 // don't warn if we have both triggerCls and triggers, because picker field\r
719 // uses triggerCls to style the "picker" trigger.\r
720 if ((me.triggerCls && !triggers) || me.trigger1Cls) {\r
721 Ext.log.warn("Ext.form.field.Text: 'triggerCls' and 'trigger<n>Cls'" +\r
722 " are deprecated. Use 'triggers' instead.");\r
723 }\r
724 //</debug>\r
725\r
726 if (!triggers) {\r
727 // For compatibility with 4.x, transform the trigger<n>Cls configs into the\r
728 // new "triggers" config.\r
729 triggers = {};\r
730\r
731 if (me.triggerCls && !me.trigger1Cls) {\r
732 me.trigger1Cls = me.triggerCls;\r
733 }\r
734\r
735 // Assignment in conditional test is deliberate here\r
736 for (i = 1; triggerCls = me['trigger' + i + 'Cls']; i++) { // jshint ignore:line\r
737 triggers['trigger' + i] = {\r
738 cls: triggerCls,\r
739 extraCls: Ext.baseCSSPrefix + 'trigger-index-' + i,\r
740 handler: 'onTrigger' + i + 'Click',\r
741 compat4Mode: true,\r
742 scope: me\r
743 };\r
744 }\r
745 }\r
746\r
747 for(id in triggers) {\r
748 if (triggers.hasOwnProperty(id)) {\r
749 triggerCfg = triggers[id];\r
750 triggerCfg.field = me;\r
751 triggerCfg.id = id;\r
752\r
753 /*\r
754 * An explicitly-configured 'triggerConfig.hideOnReadOnly : false' allows {@link #hideTrigger} analysis\r
755 */\r
756 if ((readOnly && triggerCfg.hideOnReadOnly !== false) || (hideAllTriggers && triggerCfg.hidden !== false)) {\r
757 triggerCfg.hidden = true;\r
758 }\r
759 if (repeatTriggerClick && (triggerCfg.repeatClick !== false)) {\r
760 triggerCfg.repeatClick = true;\r
761 }\r
762\r
763 trigger = triggers[id] = Ext.form.trigger.Trigger.create(triggerCfg);\r
764 orderedTriggers.push(trigger);\r
765 }\r
766 }\r
767\r
768 Ext.Array.sort(orderedTriggers, Ext.form.trigger.Trigger.weightComparator);\r
769\r
770 return triggers;\r
771 },\r
772\r
773 /**\r
774 * Invokes a method on all triggers.\r
775 * @param {String} methodName\r
776 * @private\r
777 */\r
778 invokeTriggers: function(methodName, args) {\r
779 var me = this,\r
780 triggers = me.getTriggers(),\r
781 id, trigger;\r
782\r
783 if (triggers) {\r
784 for (id in triggers) {\r
785 if (triggers.hasOwnProperty(id)) {\r
786 trigger = triggers[id];\r
787 // IE8 needs "|| []" if args is undefined\r
788 trigger[methodName].apply(trigger, args || []);\r
789 }\r
790 }\r
791 }\r
792 },\r
793\r
794 /**\r
795 * Returns the trigger with the given id\r
796 * @param {String} id\r
797 * @return {Ext.form.trigger.Trigger}\r
798 */\r
799 getTrigger: function(id) {\r
800 return this.getTriggers()[id];\r
801 },\r
802\r
803 updateHideTrigger: function(hideTrigger) {\r
804 this.invokeTriggers(hideTrigger ? 'hide' : 'show');\r
805 },\r
806\r
807 updateEditable: function(editable, oldEditable) {\r
808 this.setReadOnlyAttr(!editable || this.readOnly);\r
809 },\r
810\r
811 /**\r
812 * Sets the read-only state of this field.\r
813 * @param {Boolean} readOnly True to prevent the user changing the field and explicitly\r
814 * hide the trigger(s). Setting this to true will supersede settings editable and\r
815 * hideTrigger. Setting this to false will defer back to {@link #editable editable} and {@link #hideTrigger hideTrigger}.\r
816 */\r
817 setReadOnly: function(readOnly) {\r
818 var me = this,\r
819 triggers = me.getTriggers(),\r
820 hideTriggers = me.getHideTrigger(),\r
821 trigger,\r
822 id;\r
823\r
824 readOnly = !!readOnly;\r
825\r
826 me.callParent([readOnly]);\r
827 if (me.rendered) {\r
828 me.setReadOnlyAttr(readOnly || !me.editable);\r
829 }\r
830\r
831 if (triggers) {\r
832 for (id in triggers) {\r
833 trigger = triggers[id];\r
834 /*\r
835 * Controlled trigger visibility state is only managed fully when 'hideOnReadOnly' is falsy.\r
836 * Truth table:\r
837 * - If the trigger is configured/defaulted as 'hideOnReadOnly : true', it's readOnly-visibility\r
838 * is determined solely by readOnly state of the Field.\r
839 * - If 'hideOnReadOnly : false/undefined', the Fields.{link #hideTrigger hideTrigger} is honored.\r
840 */\r
841 if (trigger.hideOnReadOnly === true || (trigger.hideOnReadOnly !== false && !hideTriggers)) {\r
842 trigger.setVisible(!readOnly);\r
843 }\r
844 }\r
845 }\r
846 },\r
847\r
848 /**\r
849 * @private\r
850 * Sets the readonly attribute of the input element\r
851 */\r
852 setReadOnlyAttr: function(readOnly) {\r
853 var me = this,\r
854 readOnlyName = 'readonly',\r
855 inputEl = me.inputEl.dom;\r
856\r
857 if (readOnly) {\r
858 inputEl.setAttribute(readOnlyName, readOnlyName);\r
859 } else {\r
860 inputEl.removeAttribute(readOnlyName);\r
861 }\r
862 \r
863 if (me.ariaRole) {\r
864 me.ariaEl.dom.setAttribute('aria-readonly', !!readOnly);\r
865 }\r
866 },\r
867\r
868 /**\r
869 * Performs any necessary manipulation of a raw String value to prepare it for conversion and/or\r
870 * {@link #validate validation}. For text fields this applies the configured {@link #stripCharsRe}\r
871 * to the raw value.\r
872 * @param {String} value The unprocessed string value\r
873 * @return {String} The processed string value\r
874 */\r
875 processRawValue: function(value) {\r
876 var me = this,\r
877 stripRe = me.stripCharsRe,\r
878 mod, newValue;\r
879\r
880 if (stripRe) {\r
881 // This will force all instances that match stripRe to be removed\r
882 // in case the user tries to add it with copy and paste EXTJS-18621\r
883 if (!stripRe.global) {\r
884 mod = 'g';\r
885 mod += (stripRe.ignoreCase) ? 'i' : '';\r
886 mod += (stripRe.multiline) ? 'm' : '';\r
887 stripRe = new RegExp(stripRe.source, mod);\r
888 }\r
889\r
890 newValue = value.replace(stripRe, '');\r
891 if (newValue !== value) {\r
892 me.setRawValue(newValue);\r
893 value = newValue;\r
894 }\r
895 }\r
896 return value;\r
897 },\r
898\r
899 onDisable: function(){\r
900 this.callParent();\r
901 if (Ext.isIE) {\r
902 this.inputEl.dom.unselectable = 'on';\r
903 }\r
904 },\r
905\r
906 onEnable: function(){\r
907 this.callParent();\r
908 if (Ext.isIE) {\r
909 this.inputEl.dom.unselectable = '';\r
910 }\r
911 },\r
912\r
913 onKeyDown: function(e) {\r
914 this.fireEvent('keydown', this, e);\r
915 },\r
916\r
917 onKeyUp: function(e) {\r
918 this.fireEvent('keyup', this, e);\r
919 },\r
920\r
921 onKeyPress: function(e) {\r
922 this.fireEvent('keypress', this, e);\r
923 },\r
924\r
925 /**\r
926 * Resets the current field value to the originally-loaded value and clears any validation messages.\r
927 * Also adds **{@link #emptyText}** and **{@link #emptyCls}** if the original value was blank.\r
928 */\r
929 reset : function(){\r
930 this.callParent();\r
931 this.applyEmptyText();\r
932 },\r
933\r
934 applyEmptyText: function(){\r
935 var me = this,\r
936 emptyText = me.emptyText,\r
937 isEmpty;\r
938\r
939 if (me.rendered && emptyText) {\r
940 isEmpty = me.getRawValue().length < 1 && !me.hasFocus;\r
941\r
942 if (Ext.supports.Placeholder) {\r
943 me.inputEl.dom.placeholder = emptyText;\r
944 } else if (isEmpty) {\r
945 me.setRawValue(emptyText);\r
946 me.valueContainsPlaceholder = true;\r
947 }\r
948\r
949 //all browsers need this because of a styling issue with chrome + placeholders.\r
950 //the text isnt vertically aligned when empty (and using the placeholder)\r
951 if (isEmpty) {\r
952 me.inputEl.addCls(me.emptyUICls);\r
953 }\r
954 else {\r
955 me.inputEl.removeCls(me.emptyUICls);\r
956 }\r
957\r
958 me.autoSize();\r
959 }\r
960 },\r
961 /**\r
962 * Returns the value of this field's {@link #cfg-emptyText}\r
963 * @return {String} The value of this field's emptyText\r
964 */\r
965 getEmptyText : function () {\r
966 return this.emptyText;\r
967 },\r
968 \r
969 /**\r
970 * Sets the default text to place into an empty field\r
971 * @param {String} value The {@link #cfg-emptyText} value for this field\r
972 * @return {Ext.form.field.Text} this\r
973 */\r
974 setEmptyText: function(value) {\r
975 var me = this,\r
976 inputEl = me.inputEl,\r
977 inputDom = inputEl && inputEl.dom,\r
978 emptyText = value || '';\r
979\r
980 if (value) {\r
981 me.emptyText = emptyText;\r
982 me.applyEmptyText();\r
983 } else if (inputDom) {\r
984 if (Ext.supports.Placeholder) {\r
985 inputDom.removeAttribute('placeholder');\r
986 } else {\r
987 if (inputDom.value !== me.getRawValue()) {\r
988 // only way these are !== is if emptyText is in the dom.value\r
989 inputDom.value = '';\r
990 inputEl.removeCls(me.emptyUICls);\r
991 }\r
992 }\r
993 // value is null so it cannot be the input value:\r
994 me.valueContainsPlaceholder = false;\r
995 }\r
996 // This has to be added at the end because getRawValue depends on\r
997 // the emptyText value to return an empty string or not in legacy browsers.\r
998 me.emptyText = emptyText;\r
999 return me;\r
1000 },\r
1001\r
1002 afterFirstLayout: function() {\r
1003 this.callParent();\r
1004 if (Ext.isIE && this.disabled) {\r
1005 var el = this.inputEl;\r
1006 if (el) {\r
1007 el.dom.unselectable = 'on';\r
1008 }\r
1009 }\r
1010 },\r
1011\r
1012 /**\r
1013 * @private\r
1014 */\r
1015 toggleInvalidCls: function(hasError) {\r
1016 var method = hasError ? 'addCls' : 'removeCls';\r
1017\r
1018 this.callParent();\r
1019\r
1020 this.triggerWrap[method](this.triggerWrapInvalidCls);\r
1021 this.inputWrap[method](this.inputWrapInvalidCls);\r
1022 },\r
1023\r
1024 beforeFocus: function(){\r
1025 var me = this,\r
1026 inputEl = me.inputEl,\r
1027 emptyText = me.emptyText,\r
1028 isEmpty;\r
1029\r
1030 me.callParent(arguments);\r
1031 if ((emptyText && !Ext.supports.Placeholder) && (inputEl.dom.value === me.emptyText && me.valueContainsPlaceholder)) {\r
1032 me.setRawValue('');\r
1033 isEmpty = true;\r
1034 inputEl.removeCls(me.emptyUICls);\r
1035 me.valueContainsPlaceholder = false;\r
1036 } else if (Ext.supports.Placeholder) {\r
1037 inputEl.removeCls(me.emptyUICls);\r
1038 }\r
1039 },\r
1040\r
1041 onFocus: function(e) {\r
1042 var me = this;\r
1043\r
1044 me.callParent(arguments);\r
1045 if (me.selectOnFocus) {\r
1046 me.inputEl.dom.select();\r
1047 }\r
1048\r
1049 if (me.emptyText) {\r
1050 me.autoSize();\r
1051 }\r
1052\r
1053 me.addCls(me.fieldFocusCls);\r
1054 me.triggerWrap.addCls(me.triggerWrapFocusCls);\r
1055 me.inputWrap.addCls(me.inputWrapFocusCls);\r
1056 me.invokeTriggers('onFieldFocus', [e]);\r
1057 },\r
1058\r
1059 /**\r
1060 * @private\r
1061 */\r
1062 onBlur: function(e) {\r
1063 var me = this;\r
1064\r
1065 me.callParent(arguments);\r
1066\r
1067 me.removeCls(me.fieldFocusCls);\r
1068 me.triggerWrap.removeCls(me.triggerWrapFocusCls);\r
1069 me.inputWrap.removeCls(me.inputWrapFocusCls);\r
1070 me.invokeTriggers('onFieldBlur', [e]);\r
1071 },\r
1072\r
1073 completeEdit: function(e) {\r
1074 this.callParent([e]);\r
1075 this.applyEmptyText();\r
1076 },\r
1077\r
1078 /**\r
1079 * @private\r
1080 */\r
1081 filterKeys : function(e){\r
1082 /*\r
1083 * Current only FF will fire keypress events for special keys.\r
1084 * \r
1085 * On European keyboards, the right alt key, Alt Gr, is used to type certain special characters.\r
1086 * JS detects a keypress of this as ctrlKey & altKey. As such, we check that alt isn't pressed\r
1087 * so we can still process these special characters.\r
1088 */\r
1089 if ((e.ctrlKey && !e.altKey) || e.isSpecialKey()) {\r
1090 return;\r
1091 }\r
1092 var charCode = String.fromCharCode(e.getCharCode());\r
1093 if (!this.maskRe.test(charCode)) {\r
1094 e.stopEvent();\r
1095 }\r
1096 },\r
1097\r
1098 getState: function() {\r
1099 return this.addPropertyToState(this.callParent(), 'value');\r
1100 },\r
1101\r
1102 applyState: function(state) {\r
1103 this.callParent(arguments);\r
1104 if(state.hasOwnProperty('value')) {\r
1105 this.setValue(state.value);\r
1106 }\r
1107 },\r
1108\r
1109 /**\r
1110 * Returns the raw String value of the field, without performing any normalization, conversion, or validation. Gets\r
1111 * the current value of the input element if the field has been rendered, ignoring the value if it is the\r
1112 * {@link #emptyText}. To get a normalized and converted value see {@link #getValue}.\r
1113 * @return {String} The raw String value of the field\r
1114 */\r
1115 getRawValue: function() {\r
1116 var me = this,\r
1117 v = me.callParent();\r
1118 if (v === me.emptyText && me.valueContainsPlaceholder) {\r
1119 v = '';\r
1120 }\r
1121 return v;\r
1122 },\r
1123\r
1124 /**\r
1125 * Sets a data value into the field and runs the change detection and validation. Also applies any configured\r
1126 * {@link #emptyText} for text fields. To set the value directly without these inspections see {@link #setRawValue}.\r
1127 * @param {Object} value The value to set\r
1128 * @return {Ext.form.field.Text} this\r
1129 */\r
1130 setValue: function(value) {\r
1131 var me = this,\r
1132 inputEl = me.inputEl;\r
1133\r
1134 if (inputEl && me.emptyText && !Ext.isEmpty(value)) {\r
1135 inputEl.removeCls(me.emptyUICls);\r
1136 me.valueContainsPlaceholder = false;\r
1137 }\r
1138\r
1139 me.callParent(arguments);\r
1140\r
1141 me.applyEmptyText();\r
1142 return me;\r
1143 },\r
1144\r
1145 /**\r
1146 * Validates a value according to the field's validation rules and returns an array of errors\r
1147 * for any failing validations. Validation rules are processed in the following order:\r
1148 *\r
1149 * 1. **Field specific validator**\r
1150 *\r
1151 * A validator offers a way to customize and reuse a validation specification.\r
1152 * If a field is configured with a `{@link #validator}`\r
1153 * function, it will be passed the current field value. The `{@link #validator}`\r
1154 * function is expected to return either:\r
1155 *\r
1156 * - Boolean `true` if the value is valid (validation continues).\r
1157 * - a String to represent the invalid message if invalid (validation halts).\r
1158 *\r
1159 * 2. **Basic Validation**\r
1160 *\r
1161 * If the `{@link #validator}` has not halted validation,\r
1162 * basic validation proceeds as follows:\r
1163 *\r
1164 * - `{@link #allowBlank}` : (Invalid message = `{@link #blankText}`)\r
1165 *\r
1166 * Depending on the configuration of `{@link #allowBlank}`, a\r
1167 * blank field will cause validation to halt at this step and return\r
1168 * Boolean true or false accordingly.\r
1169 *\r
1170 * - `{@link #minLength}` : (Invalid message = `{@link #minLengthText}`)\r
1171 *\r
1172 * If the passed value does not satisfy the `{@link #minLength}`\r
1173 * specified, validation halts.\r
1174 *\r
1175 * - `{@link #maxLength}` : (Invalid message = `{@link #maxLengthText}`)\r
1176 *\r
1177 * If the passed value does not satisfy the `{@link #maxLength}`\r
1178 * specified, validation halts.\r
1179 *\r
1180 * 3. **Preconfigured Validation Types (VTypes)**\r
1181 *\r
1182 * If none of the prior validation steps halts validation, a field\r
1183 * configured with a `{@link #vtype}` will utilize the\r
1184 * corresponding {@link Ext.form.field.VTypes VTypes} validation function.\r
1185 * If invalid, either the field's `{@link #vtypeText}` or\r
1186 * the VTypes vtype Text property will be used for the invalid message.\r
1187 * Keystrokes on the field will be filtered according to the VTypes\r
1188 * vtype Mask property.\r
1189 *\r
1190 * 4. **Field specific regex test**\r
1191 *\r
1192 * If none of the prior validation steps halts validation, a field's\r
1193 * configured `{@link #regex}` test will be processed.\r
1194 * The invalid message for this test is configured with `{@link #regexText}`\r
1195 *\r
1196 * @param {Object} value The value to validate. The processed raw value will be used if nothing is passed.\r
1197 * @return {String[]} Array of any validation errors\r
1198 */\r
1199 getErrors: function(value) {\r
1200 value = arguments.length ? (value == null ? '' : value) : this.processRawValue(this.getRawValue());\r
1201\r
1202 var me = this,\r
1203 errors = me.callParent([value]),\r
1204 validator = me.validator,\r
1205 vtype = me.vtype,\r
1206 vtypes = Ext.form.field.VTypes,\r
1207 regex = me.regex,\r
1208 format = Ext.String.format,\r
1209 msg, trimmed, isBlank;\r
1210\r
1211 if (Ext.isFunction(validator)) {\r
1212 msg = validator.call(me, value);\r
1213 if (msg !== true) {\r
1214 errors.push(msg);\r
1215 }\r
1216 }\r
1217\r
1218 trimmed = me.allowOnlyWhitespace ? value : Ext.String.trim(value);\r
1219\r
1220 if (trimmed.length < 1 || (value === me.emptyText && me.valueContainsPlaceholder)) {\r
1221 if (!me.allowBlank) {\r
1222 errors.push(me.blankText);\r
1223 }\r
1224 // If we are not configured to validate blank values, there cannot be any additional errors\r
1225 if (!me.validateBlank) {\r
1226 return errors;\r
1227 }\r
1228 isBlank = true;\r
1229 }\r
1230\r
1231 // If a blank value has been allowed through, then exempt it from the minLength check.\r
1232 // It must be allowed to hit the vtype validation.\r
1233 if (!isBlank && value.length < me.minLength) {\r
1234 errors.push(format(me.minLengthText, me.minLength));\r
1235 }\r
1236\r
1237 if (value.length > me.maxLength) {\r
1238 errors.push(format(me.maxLengthText, me.maxLength));\r
1239 }\r
1240\r
1241 if (vtype) {\r
1242 if (!vtypes[vtype](value, me)) {\r
1243 errors.push(me.vtypeText || vtypes[vtype +'Text']);\r
1244 }\r
1245 }\r
1246\r
1247 if (regex && !regex.test(value)) {\r
1248 errors.push(me.regexText || me.invalidText);\r
1249 }\r
1250\r
1251 return errors;\r
1252 },\r
1253\r
1254 /**\r
1255 * Selects text in this field\r
1256 * @param {Number} [start=0] The index where the selection should start\r
1257 * @param {Number} [end] The index where the selection should end (defaults to the text length)\r
1258 */\r
1259 selectText: function (start, end) {\r
1260 var me = this,\r
1261 v = me.getRawValue(),\r
1262 len = v.length,\r
1263 el = me.inputEl.dom,\r
1264 range;\r
1265\r
1266 if (len > 0) {\r
1267 start = start === undefined ? 0 : Math.min(start, len);\r
1268 end = end === undefined ? len : Math.min(end, len);\r
1269\r
1270 if (el.setSelectionRange) {\r
1271 el.setSelectionRange(start, end);\r
1272 } else if (el.createTextRange) {\r
1273 range = el.createTextRange();\r
1274 range.moveStart('character', start);\r
1275 range.moveEnd('character', end - len);\r
1276 range.select();\r
1277 }\r
1278 }\r
1279\r
1280 // TODO: Reinvestigate FF and Opera.\r
1281 },\r
1282\r
1283 // Template method, override in Combobox.\r
1284 getGrowWidth: function () {\r
1285 return this.inputEl.dom.value;\r
1286 },\r
1287\r
1288 /**\r
1289 * Automatically grows the field to accommodate the width of the text up to the maximum\r
1290 * field width allowed. This only takes effect if {@link #grow} = true, and fires the\r
1291 * {@link #autosize} event if the width changes.\r
1292 */\r
1293 autoSize: function() {\r
1294 var me = this,\r
1295 triggers, triggerId, triggerWidth, inputEl, width, value;\r
1296\r
1297 if (me.grow && me.rendered && me.getSizeModel().width.auto) {\r
1298 inputEl = me.inputEl;\r
1299 triggers = me.getTriggers();\r
1300 triggerWidth = 0;\r
1301\r
1302 value = Ext.util.Format.htmlEncode(\r
1303 me.getGrowWidth() || (me.hasFocus ? '' : me.emptyText) || ''\r
1304 );\r
1305 value += me.growAppend;\r
1306\r
1307 for (triggerId in triggers) {\r
1308 triggerWidth += triggers[triggerId].el.getWidth();\r
1309 }\r
1310\r
1311 width = inputEl.getTextWidth(value) + triggerWidth +\r
1312 // The element that has the border depends on theme - inputWrap (classic)\r
1313 // or triggerWrap (neptune)\r
1314 me.inputWrap.getBorderWidth('lr') + me.triggerWrap.getBorderWidth('lr');\r
1315\r
1316 width = Math.min(Math.max(width, me.growMin), me.growMax);\r
1317\r
1318 me.bodyEl.setWidth(width);\r
1319\r
1320 me.updateLayout();\r
1321\r
1322 me.fireEvent('autosize', me, width);\r
1323 }\r
1324 },\r
1325\r
1326 onDestroy: function(){\r
1327 var me = this;\r
1328\r
1329 me.invokeTriggers('destroy');\r
1330 Ext.destroy(me.triggerRepeater);\r
1331\r
1332 me.callParent();\r
1333 },\r
1334\r
1335 onTriggerClick: Ext.emptyFn,\r
1336\r
1337 privates: {\r
1338 /**\r
1339 * @private\r
1340 */\r
1341 getTdType: function () {\r
1342 return 'textfield';\r
1343 }\r
1344 },\r
1345\r
1346 deprecated: {\r
1347 5: {\r
1348 methods: {\r
1349 /**\r
1350 * Get the total width of the trigger button area.\r
1351 * @return {Number} The total trigger width\r
1352 * @deprecated 5.0\r
1353 */\r
1354 getTriggerWidth: function() {\r
1355 var triggers = this.getTriggers(),\r
1356 width = 0,\r
1357 id;\r
1358 if (triggers && this.rendered) {\r
1359 for (id in triggers) {\r
1360 if (triggers.hasOwnProperty(id)) {\r
1361 width += triggers[id].el.getWidth();\r
1362 }\r
1363 }\r
1364 }\r
1365\r
1366 return width;\r
1367 }\r
1368 }\r
1369 }\r
1370 }\r
1371\r
1372});\r