]>
Commit | Line | Data |
---|---|---|
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 | |
106 | Ext.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 |