3 * Copyright(c) 2006-2009 Ext JS, LLC
5 * http://www.extjs.com/license
8 * @class Ext.form.Field
9 * @extends Ext.BoxComponent
10 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
13 * @param {Object} config Configuration options
16 Ext.form.Field = Ext.extend(Ext.BoxComponent, {
18 * <p>The label Element associated with this Field. <b>Only available after this Field has been rendered by a
19 * {@link form Ext.layout.FormLayout} layout manager.</b></p>
24 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults
25 * to 'text'). The types 'file' and 'password' must be used to render those field types currently -- there are
26 * no separate Ext components for those. Note that if you use <tt>inputType:'file'</tt>, {@link #emptyText}
27 * is not supported and should be avoided.
30 * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
31 * not those which are built via applyTo (defaults to undefined).
34 * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
37 * @cfg {String} name The field's HTML name attribute (defaults to '').
38 * <b>Note</b>: this property must be set if this field is to be automatically included with
39 * {@link Ext.form.BasicForm#submit form submit()}.
42 * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to '').
46 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to 'x-form-invalid')
48 invalidClass : 'x-form-invalid',
50 * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided
51 * (defaults to 'The value in this field is invalid')
53 invalidText : 'The value in this field is invalid',
55 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to 'x-form-focus')
57 focusClass : 'x-form-focus',
59 * @cfg {Boolean} preventMark
60 * <tt>true</tt> to disable {@link #markInvalid marking the field invalid}.
61 * Defaults to <tt>false</tt>.
64 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
65 automatic validation (defaults to 'keyup').
67 validationEvent : 'keyup',
69 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
71 validateOnBlur : true,
73 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation
74 * is initiated (defaults to 250)
76 validationDelay : 250,
78 * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
79 * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
80 * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details. Defaults to:</p>
81 * <pre><code>{tag: 'input', type: 'text', size: '20', autocomplete: 'off'}</code></pre>
83 defaultAutoCreate : {tag: 'input', type: 'text', size: '20', autocomplete: 'off'},
85 * @cfg {String} fieldClass The default CSS class for the field (defaults to 'x-form-field')
87 fieldClass : 'x-form-field',
89 * @cfg {String} msgTarget<p>The location where the message text set through {@link #markInvalid} should display.
90 * Must be one of the following values:</p>
91 * <div class="mdetail-params"><ul>
92 * <li><code>qtip</code> Display a quick tip containing the message when the user hovers over the field. This is the default.
93 * <div class="subdesc"><b>{@link Ext.QuickTips#init Ext.QuickTips.init} must have been called for this setting to work.</b></div</li>
94 * <li><code>title</code> Display the message in a default browser title attribute popup.</li>
95 * <li><code>under</code> Add a block div beneath the field containing the error message.</li>
96 * <li><code>side</code> Add an error icon to the right of the field, displaying the message in a popup on hover.</li>
97 * <li><code>[element id]</code> Add the error message directly to the innerHTML of the specified element.</li>
102 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field
103 * (defaults to 'normal').
107 * @cfg {Boolean} readOnly <tt>true</tt> to mark the field as readOnly in HTML
108 * (defaults to <tt>false</tt>).
109 * <br><p><b>Note</b>: this only sets the element's readOnly DOM attribute.
110 * Setting <code>readOnly=true</code>, for example, will not disable triggering a
111 * ComboBox or DateField; it gives you the option of forcing the user to choose
112 * via the trigger without typing in the text box. To hide the trigger use
113 * <code>{@link Ext.form.TriggerField#hideTrigger hideTrigger}</code>.</p>
117 * @cfg {Boolean} disabled True to disable the field (defaults to false).
118 * <p>Be aware that conformant with the <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.12.1">HTML specification</a>,
119 * disabled Fields will not be {@link Ext.form.BasicForm#submit submitted}.</p>
123 * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
124 * Defaults to <tt>true</tt>.
138 initComponent : function(){
139 Ext.form.Field.superclass.initComponent.call(this);
143 * Fires when this field receives input focus.
144 * @param {Ext.form.Field} this
149 * Fires when this field loses input focus.
150 * @param {Ext.form.Field} this
155 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.
156 * To handle other keys see {@link Ext.Panel#keys} or {@link Ext.KeyMap}.
157 * You can check {@link Ext.EventObject#getKey} to determine which key was pressed.
158 * For example: <pre><code>
159 var form = new Ext.form.FormPanel({
162 fieldLabel: 'Field 1',
166 fieldLabel: 'Field 2',
169 specialkey: function(field, e){
170 // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
171 // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
172 if (e.{@link Ext.EventObject#getKey getKey()} == e.ENTER) {
173 var form = field.ownerCt.getForm();
183 * @param {Ext.form.Field} this
184 * @param {Ext.EventObject} e The event object
189 * Fires just before the field blurs if the field value has changed.
190 * @param {Ext.form.Field} this
191 * @param {Mixed} newValue The new value
192 * @param {Mixed} oldValue The original value
197 * Fires after the field has been marked as invalid.
198 * @param {Ext.form.Field} this
199 * @param {String} msg The validation message
204 * Fires after the field has been validated with no errors.
205 * @param {Ext.form.Field} this
212 * Returns the {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
213 * attribute of the field if available.
214 * @return {String} name The field {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
216 getName : function(){
217 return this.rendered && this.el.dom.name ? this.el.dom.name : this.name || this.id || '';
221 onRender : function(ct, position){
223 var cfg = this.getAutoCreate();
226 cfg.name = this.name || this.id;
229 cfg.type = this.inputType;
233 Ext.form.Field.superclass.onRender.call(this, ct, position);
234 if(this.submitValue === false){
235 this.el.dom.removeAttribute('name');
237 var type = this.el.dom.type;
239 if(type == 'password'){
242 this.el.addClass('x-form-'+type);
245 this.setReadOnly(true);
247 if(this.tabIndex !== undefined){
248 this.el.dom.setAttribute('tabIndex', this.tabIndex);
251 this.el.addClass([this.fieldClass, this.cls]);
255 getItemCt : function(){
260 initValue : function(){
261 if(this.value !== undefined){
262 this.setValue(this.value);
263 }else if(!Ext.isEmpty(this.el.dom.value) && this.el.dom.value != this.emptyText){
264 this.setValue(this.el.dom.value);
267 * The original value of the field as configured in the {@link #value} configuration, or
268 * as loaded by the last form load operation if the form's {@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
269 * setting is <code>true</code>.
271 * @property originalValue
273 this.originalValue = this.getValue();
277 * <p>Returns true if the value of this Field has been changed from its original value.
278 * Will return false if the field is disabled or has not been rendered yet.</p>
279 * <p>Note that if the owning {@link Ext.form.BasicForm form} was configured with
280 * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
281 * then the <i>original value</i> is updated when the values are loaded by
282 * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#setValues setValues}.</p>
283 * @return {Boolean} True if this field has been changed from its original value (and
284 * is not disabled), false otherwise.
286 isDirty : function() {
287 if(this.disabled || !this.rendered) {
290 return String(this.getValue()) !== String(this.originalValue);
294 * Sets the read only state of this field.
295 * @param {Boolean} readOnly Whether the field should be read only.
297 setReadOnly : function(readOnly){
299 this.el.dom.readOnly = readOnly;
301 this.readOnly = readOnly;
305 afterRender : function(){
306 Ext.form.Field.superclass.afterRender.call(this);
312 fireKey : function(e){
313 if(e.isSpecialKey()){
314 this.fireEvent('specialkey', this, e);
319 * Resets the current field value to the originally loaded value and clears any validation messages.
320 * See {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
323 this.setValue(this.originalValue);
328 initEvents : function(){
329 this.mon(this.el, Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.fireKey, this);
330 this.mon(this.el, 'focus', this.onFocus, this);
332 // standardise buffer across all browsers + OS-es for consistent event order.
333 // (the 10ms buffer for Editors fixes a weird FF/Win editor issue when changing OS window focus)
334 this.mon(this.el, 'blur', this.onBlur, this, this.inEditor ? {buffer:10} : null);
338 preFocus: Ext.emptyFn,
341 onFocus : function(){
344 this.el.addClass(this.focusClass);
347 this.hasFocus = true;
349 * <p>The value that the Field had at the time it was last focused. This is the value that is passed
350 * to the {@link #change} event which is fired if the value has been changed when the Field is blurred.</p>
351 * <p><b>This will be undefined until the Field has been visited.</b> Compare {@link #originalValue}.</p>
353 * @property startValue
355 this.startValue = this.getValue();
356 this.fireEvent('focus', this);
361 beforeBlur : Ext.emptyFn,
367 this.el.removeClass(this.focusClass);
369 this.hasFocus = false;
370 if(this.validationEvent !== false && (this.validateOnBlur || this.validationEvent == 'blur')){
373 var v = this.getValue();
374 if(String(v) !== String(this.startValue)){
375 this.fireEvent('change', this, v, this.startValue);
377 this.fireEvent('blur', this);
382 postBlur : Ext.emptyFn,
385 * Returns whether or not the field value is currently valid by
386 * {@link #validateValue validating} the {@link #processValue processed value}
387 * of the field. <b>Note</b>: {@link #disabled} fields are ignored.
388 * @param {Boolean} preventMark True to disable marking the field invalid
389 * @return {Boolean} True if the value is valid, else false
391 isValid : function(preventMark){
395 var restore = this.preventMark;
396 this.preventMark = preventMark === true;
397 var v = this.validateValue(this.processValue(this.getRawValue()));
398 this.preventMark = restore;
403 * Validates the field value
404 * @return {Boolean} True if the value is valid, else false
406 validate : function(){
407 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
415 * This method should only be overridden if necessary to prepare raw values
416 * for validation (see {@link #validate} and {@link #isValid}). This method
417 * is expected to return the processed value for the field which will
418 * be used for validation (see validateValue method).
419 * @param {Mixed} value
421 processValue : function(value){
427 * Subclasses should provide the validation implementation by overriding this
428 * @param {Mixed} value
430 validateValue : function(value){
435 * Gets the active error message for this field.
436 * @return {String} Returns the active error message on the field, if there is no error, an empty string is returned.
438 getActiveError : function(){
439 return this.activeError || '';
443 * <p>Display an error message associated with this field, using {@link #msgTarget} to determine how to
444 * display the message and applying {@link #invalidClass} to the field's UI element.</p>
445 * <p><b>Note</b>: this method does not cause the Field's {@link #validate} method to return <code>false</code>
446 * if the value does <i>pass</i> validation. So simply marking a Field as invalid will not prevent
447 * submission of forms submitted with the {@link Ext.form.Action.Submit#clientValidation} option set.</p>
448 * {@link #isValid invalid}.
449 * @param {String} msg (optional) The validation message (defaults to {@link #invalidText})
451 markInvalid : function(msg){
452 if(!this.rendered || this.preventMark){ // not rendered
455 msg = msg || this.invalidText;
457 var mt = this.getMessageHandler();
460 }else if(this.msgTarget){
461 this.el.addClass(this.invalidClass);
462 var t = Ext.getDom(this.msgTarget);
465 t.style.display = this.msgDisplay;
468 this.activeError = msg;
469 this.fireEvent('invalid', this, msg);
473 * Clear any invalid styles/messages for this field
475 clearInvalid : function(){
476 if(!this.rendered || this.preventMark){ // not rendered
479 this.el.removeClass(this.invalidClass);
480 var mt = this.getMessageHandler();
483 }else if(this.msgTarget){
484 this.el.removeClass(this.invalidClass);
485 var t = Ext.getDom(this.msgTarget);
488 t.style.display = 'none';
491 delete this.activeError;
492 this.fireEvent('valid', this);
496 getMessageHandler : function(){
497 return Ext.form.MessageTargets[this.msgTarget];
501 getErrorCt : function(){
502 return this.el.findParent('.x-form-element', 5, true) || // use form element wrap if available
503 this.el.findParent('.x-form-field-wrap', 5, true); // else direct field wrap
507 alignErrorIcon : function(){
508 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
512 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
513 * @return {Mixed} value The field value
515 getRawValue : function(){
516 var v = this.rendered ? this.el.getValue() : Ext.value(this.value, '');
517 if(v === this.emptyText){
524 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
525 * @return {Mixed} value The field value
527 getValue : function(){
531 var v = this.el.getValue();
532 if(v === this.emptyText || v === undefined){
539 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
540 * @param {Mixed} value The value to set
541 * @return {Mixed} value The field value that is set
543 setRawValue : function(v){
544 return this.rendered ? (this.el.dom.value = (Ext.isEmpty(v) ? '' : v)) : '';
548 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
549 * @param {Mixed} value The value to set
550 * @return {Ext.form.Field} this
552 setValue : function(v){
555 this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
561 // private, does not work for all fields
562 append : function(v){
563 this.setValue([this.getValue(), v].join(''));
567 * @cfg {Boolean} autoWidth @hide
570 * @cfg {Boolean} autoHeight @hide
574 * @cfg {String} autoEl @hide
579 Ext.form.MessageTargets = {
581 mark: function(field, msg){
582 field.el.addClass(field.invalidClass);
583 field.el.dom.qtip = msg;
584 field.el.dom.qclass = 'x-form-invalid-tip';
585 if(Ext.QuickTips){ // fix for floating editors interacting with DND
586 Ext.QuickTips.enable();
589 clear: function(field){
590 field.el.removeClass(field.invalidClass);
591 field.el.dom.qtip = '';
595 mark: function(field, msg){
596 field.el.addClass(field.invalidClass);
597 field.el.dom.title = msg;
599 clear: function(field){
600 field.el.dom.title = '';
604 mark: function(field, msg){
605 field.el.addClass(field.invalidClass);
607 var elp = field.getErrorCt();
608 if(!elp){ // field has no container el
609 field.el.dom.title = msg;
612 field.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
613 field.errorEl.setWidth(elp.getWidth(true)-20);
615 field.errorEl.update(msg);
616 Ext.form.Field.msgFx[field.msgFx].show(field.errorEl, field);
618 clear: function(field){
619 field.el.removeClass(field.invalidClass);
621 Ext.form.Field.msgFx[field.msgFx].hide(field.errorEl, field);
623 field.el.dom.title = '';
628 mark: function(field, msg){
629 field.el.addClass(field.invalidClass);
630 if(!field.errorIcon){
631 var elp = field.getErrorCt();
632 if(!elp){ // field has no container el
633 field.el.dom.title = msg;
636 field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
638 field.alignErrorIcon();
639 field.errorIcon.dom.qtip = msg;
640 field.errorIcon.dom.qclass = 'x-form-invalid-tip';
641 field.errorIcon.show();
642 field.on('resize', field.alignErrorIcon, field);
644 clear: function(field){
645 field.el.removeClass(field.invalidClass);
647 field.errorIcon.dom.qtip = '';
648 field.errorIcon.hide();
649 field.un('resize', field.alignErrorIcon, field);
651 field.el.dom.title = '';
657 // anything other than normal should be considered experimental
658 Ext.form.Field.msgFx = {
660 show: function(msgEl, f){
661 msgEl.setDisplayed('block');
664 hide : function(msgEl, f){
665 msgEl.setDisplayed(false).update('');
670 show: function(msgEl, f){
671 msgEl.slideIn('t', {stopFx:true});
674 hide : function(msgEl, f){
675 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
680 show: function(msgEl, f){
682 msgEl.alignTo(f.el, 'tl-tr');
683 msgEl.slideIn('l', {stopFx:true});
686 hide : function(msgEl, f){
687 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
691 Ext.reg('field', Ext.form.Field);
693 * @class Ext.form.TextField
694 * @extends Ext.form.Field
695 * <p>Basic text field. Can be used as a direct replacement for traditional text inputs,
696 * or as the base class for more sophisticated input controls (like {@link Ext.form.TextArea}
697 * and {@link Ext.form.ComboBox}).</p>
698 * <p><b><u>Validation</u></b></p>
699 * <p>The validation procedure is described in the documentation for {@link #validateValue}.</p>
700 * <p><b><u>Alter Validation Behavior</u></b></p>
701 * <p>Validation behavior for each field can be configured:</p>
702 * <div class="mdetail-params"><ul>
703 * <li><code>{@link Ext.form.TextField#invalidText invalidText}</code> : the default validation message to
704 * show if any validation step above does not provide a message when invalid</li>
705 * <li><code>{@link Ext.form.TextField#maskRe maskRe}</code> : filter out keystrokes before any validation occurs</li>
706 * <li><code>{@link Ext.form.TextField#stripCharsRe stripCharsRe}</code> : filter characters after being typed in,
707 * but before being validated</li>
708 * <li><code>{@link Ext.form.Field#invalidClass invalidClass}</code> : alternate style when invalid</li>
709 * <li><code>{@link Ext.form.Field#validateOnBlur validateOnBlur}</code>,
710 * <code>{@link Ext.form.Field#validationDelay validationDelay}</code>, and
711 * <code>{@link Ext.form.Field#validationEvent validationEvent}</code> : modify how/when validation is triggered</li>
714 * @constructor Creates a new TextField
715 * @param {Object} config Configuration options
719 Ext.form.TextField = Ext.extend(Ext.form.Field, {
721 * @cfg {String} vtypeText A custom error message to display in place of the default message provided
722 * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>''</tt>). <b>Note</b>:
723 * only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
726 * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
727 * before validation (defaults to <tt>null</tt>).
730 * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
731 * (defaults to <tt>false</tt>)
735 * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
740 * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
745 * @cfg {String} vtype A validation type name as defined in {@link Ext.form.VTypes} (defaults to <tt>null</tt>)
749 * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
750 * not match (defaults to <tt>null</tt>)
754 * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
757 disableKeyFilter : false,
759 * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
764 * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
768 * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
769 * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
770 * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
771 * entered into the field use <tt><b>{@link Ext.form.Field#autoCreate autoCreate}</b></tt> to add
772 * any attributes you want to a field, for example:<pre><code>
773 var myField = new Ext.form.NumberField({
776 fieldLabel: 'Mobile',
777 maxLength: 16, // for validation
778 autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete: 'off', maxlength: '10'}
782 maxLength : Number.MAX_VALUE,
784 * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
785 * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
787 minLengthText : 'The minimum length for this field is {0}',
789 * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
790 * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
792 maxLengthText : 'The maximum length for this field is {0}',
794 * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
795 * receives input focus (defaults to <tt>false</tt>)
797 selectOnFocus : false,
799 * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
800 * fails (defaults to <tt>'This field is required'</tt>)
802 blankText : 'This field is required',
804 * @cfg {Function} validator
805 * <p>A custom validation function to be called during field validation ({@link #validateValue})
806 * (defaults to <tt>null</tt>). If specified, this function will be called first, allowing the
807 * developer to override the default validation process.</p>
808 * <br><p>This function will be passed the following Parameters:</p>
809 * <div class="mdetail-params"><ul>
810 * <li><code>value</code>: <i>Mixed</i>
811 * <div class="sub-desc">The current field value</div></li>
813 * <br><p>This function is to Return:</p>
814 * <div class="mdetail-params"><ul>
815 * <li><code>true</code>: <i>Boolean</i>
816 * <div class="sub-desc"><code>true</code> if the value is valid</div></li>
817 * <li><code>msg</code>: <i>String</i>
818 * <div class="sub-desc">An error message if the value is invalid</div></li>
823 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
824 * (defaults to <tt>null</tt>). If the test fails, the field will be marked invalid using
825 * <b><tt>{@link #regexText}</tt></b>.
829 * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
830 * test fails during validation (defaults to <tt>''</tt>)
834 * @cfg {String} emptyText The default text to place into an empty field (defaults to <tt>null</tt>).
835 * <b>Note</b>: that this value will be submitted to the server if this field is enabled and configured
836 * with a {@link #name}.
840 * @cfg {String} emptyClass The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
841 * (defaults to <tt>'x-form-empty-field'</tt>). This class is automatically added and removed as needed
842 * depending on the current field value.
844 emptyClass : 'x-form-empty-field',
847 * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input
848 * field (defaults to <tt>false</tt>)
851 initComponent : function(){
852 Ext.form.TextField.superclass.initComponent.call(this);
856 * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered. The field may or
857 * may not have actually changed size according to the default logic, but this event provides
858 * a hook for the developer to apply additional logic at runtime to resize the field if needed.
859 * @param {Ext.form.Field} this This text field
860 * @param {Number} width The new field width
866 * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
868 * @param {Ext.form.TextField} this This text field
869 * @param {Ext.EventObject} e
874 * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
876 * @param {Ext.form.TextField} this This text field
877 * @param {Ext.EventObject} e
882 * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
884 * @param {Ext.form.TextField} this This text field
885 * @param {Ext.EventObject} e
892 initEvents : function(){
893 Ext.form.TextField.superclass.initEvents.call(this);
894 if(this.validationEvent == 'keyup'){
895 this.validationTask = new Ext.util.DelayedTask(this.validate, this);
896 this.mon(this.el, 'keyup', this.filterValidation, this);
898 else if(this.validationEvent !== false && this.validationEvent != 'blur'){
899 this.mon(this.el, this.validationEvent, this.validate, this, {buffer: this.validationDelay});
901 if(this.selectOnFocus || this.emptyText){
902 this.mon(this.el, 'mousedown', this.onMouseDown, this);
905 this.applyEmptyText();
908 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Ext.form.VTypes[this.vtype+'Mask']))){
909 this.mon(this.el, 'keypress', this.filterKeys, this);
912 this.mon(this.el, 'keyup', this.onKeyUpBuffered, this, {buffer: 50});
913 this.mon(this.el, 'click', this.autoSize, this);
915 if(this.enableKeyEvents){
919 keydown: this.onKeyDown,
920 keypress: this.onKeyPress
925 onMouseDown: function(e){
927 this.mon(this.el, 'mouseup', Ext.emptyFn, this, { single: true, preventDefault: true });
931 processValue : function(value){
932 if(this.stripCharsRe){
933 var newValue = value.replace(this.stripCharsRe, '');
934 if(newValue !== value){
935 this.setRawValue(newValue);
942 filterValidation : function(e){
943 if(!e.isNavKeyPress()){
944 this.validationTask.delay(this.validationDelay);
949 onDisable: function(){
950 Ext.form.TextField.superclass.onDisable.call(this);
952 this.el.dom.unselectable = 'on';
957 onEnable: function(){
958 Ext.form.TextField.superclass.onEnable.call(this);
960 this.el.dom.unselectable = '';
965 onKeyUpBuffered : function(e){
966 if(this.doAutoSize(e)){
972 doAutoSize : function(e){
973 return !e.isNavKeyPress();
977 onKeyUp : function(e){
978 this.fireEvent('keyup', this, e);
982 onKeyDown : function(e){
983 this.fireEvent('keydown', this, e);
987 onKeyPress : function(e){
988 this.fireEvent('keypress', this, e);
992 * Resets the current field value to the originally-loaded value and clears any validation messages.
993 * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyClass}</b></tt> if the
994 * original value was blank.
997 Ext.form.TextField.superclass.reset.call(this);
998 this.applyEmptyText();
1001 applyEmptyText : function(){
1002 if(this.rendered && this.emptyText && this.getRawValue().length < 1 && !this.hasFocus){
1003 this.setRawValue(this.emptyText);
1004 this.el.addClass(this.emptyClass);
1009 preFocus : function(){
1012 if(el.dom.value == this.emptyText){
1013 this.setRawValue('');
1015 el.removeClass(this.emptyClass);
1017 if(this.selectOnFocus){
1023 postBlur : function(){
1024 this.applyEmptyText();
1028 filterKeys : function(e){
1033 if(Ext.isGecko && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
1036 var cc = String.fromCharCode(e.getCharCode());
1037 if(!Ext.isGecko && e.isSpecialKey() && !cc){
1040 if(!this.maskRe.test(cc)){
1045 setValue : function(v){
1046 if(this.emptyText && this.el && !Ext.isEmpty(v)){
1047 this.el.removeClass(this.emptyClass);
1049 Ext.form.TextField.superclass.setValue.apply(this, arguments);
1050 this.applyEmptyText();
1056 * <p>Validates a value according to the field's validation rules and marks the field as invalid
1057 * if the validation fails. Validation rules are processed in the following order:</p>
1058 * <div class="mdetail-params"><ul>
1060 * <li><b>1. Field specific validator</b>
1061 * <div class="sub-desc">
1062 * <p>A validator offers a way to customize and reuse a validation specification.
1063 * If a field is configured with a <code>{@link #validator}</code>
1064 * function, it will be passed the current field value. The <code>{@link #validator}</code>
1065 * function is expected to return either:
1066 * <div class="mdetail-params"><ul>
1067 * <li>Boolean <tt>true</tt> if the value is valid (validation continues).</li>
1068 * <li>a String to represent the invalid message if invalid (validation halts).</li>
1072 * <li><b>2. Basic Validation</b>
1073 * <div class="sub-desc">
1074 * <p>If the <code>{@link #validator}</code> has not halted validation,
1075 * basic validation proceeds as follows:</p>
1077 * <div class="mdetail-params"><ul>
1079 * <li><code>{@link #allowBlank}</code> : (Invalid message =
1080 * <code>{@link #emptyText}</code>)<div class="sub-desc">
1081 * Depending on the configuration of <code>{@link #allowBlank}</code>, a
1082 * blank field will cause validation to halt at this step and return
1083 * Boolean true or false accordingly.
1086 * <li><code>{@link #minLength}</code> : (Invalid message =
1087 * <code>{@link #minLengthText}</code>)<div class="sub-desc">
1088 * If the passed value does not satisfy the <code>{@link #minLength}</code>
1089 * specified, validation halts.
1092 * <li><code>{@link #maxLength}</code> : (Invalid message =
1093 * <code>{@link #maxLengthText}</code>)<div class="sub-desc">
1094 * If the passed value does not satisfy the <code>{@link #maxLength}</code>
1095 * specified, validation halts.
1101 * <li><b>3. Preconfigured Validation Types (VTypes)</b>
1102 * <div class="sub-desc">
1103 * <p>If none of the prior validation steps halts validation, a field
1104 * configured with a <code>{@link #vtype}</code> will utilize the
1105 * corresponding {@link Ext.form.VTypes VTypes} validation function.
1106 * If invalid, either the field's <code>{@link #vtypeText}</code> or
1107 * the VTypes vtype Text property will be used for the invalid message.
1108 * Keystrokes on the field will be filtered according to the VTypes
1109 * vtype Mask property.</p>
1112 * <li><b>4. Field specific regex test</b>
1113 * <div class="sub-desc">
1114 * <p>If none of the prior validation steps halts validation, a field's
1115 * configured <code>{@link #regex}</code> test will be processed.
1116 * The invalid message for this test is configured with
1117 * <code>{@link #regexText}</code>.</p>
1120 * @param {Mixed} value The value to validate
1121 * @return {Boolean} True if the value is valid, else false
1123 validateValue : function(value){
1124 if(Ext.isFunction(this.validator)){
1125 var msg = this.validator(value);
1127 this.markInvalid(msg);
1131 if(value.length < 1 || value === this.emptyText){ // if it's blank
1132 if(this.allowBlank){
1133 this.clearInvalid();
1136 this.markInvalid(this.blankText);
1140 if(value.length < this.minLength){
1141 this.markInvalid(String.format(this.minLengthText, this.minLength));
1144 if(value.length > this.maxLength){
1145 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
1149 var vt = Ext.form.VTypes;
1150 if(!vt[this.vtype](value, this)){
1151 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
1155 if(this.regex && !this.regex.test(value)){
1156 this.markInvalid(this.regexText);
1163 * Selects text in this field
1164 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
1165 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
1167 selectText : function(start, end){
1168 var v = this.getRawValue();
1169 var doFocus = false;
1171 start = start === undefined ? 0 : start;
1172 end = end === undefined ? v.length : end;
1173 var d = this.el.dom;
1174 if(d.setSelectionRange){
1175 d.setSelectionRange(start, end);
1176 }else if(d.createTextRange){
1177 var range = d.createTextRange();
1178 range.moveStart('character', start);
1179 range.moveEnd('character', end-v.length);
1182 doFocus = Ext.isGecko || Ext.isOpera;
1192 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
1193 * This only takes effect if <tt><b>{@link #grow}</b> = true</tt>, and fires the {@link #autosize} event.
1195 autoSize : function(){
1196 if(!this.grow || !this.rendered){
1200 this.metrics = Ext.util.TextMetrics.createInstance(this.el);
1203 var v = el.dom.value;
1204 var d = document.createElement('div');
1205 d.appendChild(document.createTextNode(v));
1210 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
1211 this.el.setWidth(w);
1212 this.fireEvent('autosize', this, w);
1215 onDestroy: function(){
1216 if(this.validationTask){
1217 this.validationTask.cancel();
1218 this.validationTask = null;
1220 Ext.form.TextField.superclass.onDestroy.call(this);
1223 Ext.reg('textfield', Ext.form.TextField);
1225 * @class Ext.form.TriggerField
1226 * @extends Ext.form.TextField
1227 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
1228 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
1229 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
1230 * for which you can provide a custom implementation. For example:
1232 var trigger = new Ext.form.TriggerField();
1233 trigger.onTriggerClick = myTriggerFn;
1234 trigger.applyToMarkup('my-field');
1237 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
1238 * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
1241 * Create a new TriggerField.
1242 * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
1243 * to the base TextField)
1246 Ext.form.TriggerField = Ext.extend(Ext.form.TextField, {
1248 * @cfg {String} triggerClass
1249 * An additional CSS class used to style the trigger button. The trigger will always get the
1250 * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
1253 * @cfg {Mixed} triggerConfig
1254 * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the
1255 * trigger element for this Field. (Optional).</p>
1256 * <p>Specify this when you need a customized element to act as the trigger button for a TriggerField.</p>
1257 * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning
1258 * and appearance of the trigger. Defaults to:</p>
1259 * <pre><code>{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}</code></pre>
1262 * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
1263 * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
1264 * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details. Defaults to:</p>
1265 * <pre><code>{tag: "input", type: "text", size: "16", autocomplete: "off"}</code></pre>
1267 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
1269 * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base
1270 * text field (defaults to <tt>false</tt>)
1274 * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field,
1275 * the field will only respond to a click on the trigger to set the value. (defaults to <tt>true</tt>).
1279 * @cfg {Boolean} readOnly <tt>true</tt> to prevent the user from changing the field, and
1280 * hides the trigger. Superceeds the editable and hideTrigger options if the value is true.
1281 * (defaults to <tt>false</tt>)
1285 * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
1286 * <tt>x-trigger-wrap-focus</tt>.
1288 wrapFocusClass: 'x-trigger-wrap-focus',
1293 autoSize: Ext.emptyFn,
1303 removeMode: 'container',
1305 defaultTriggerWidth: 17,
1308 onResize : function(w, h){
1309 Ext.form.TriggerField.superclass.onResize.call(this, w, h);
1310 var tw = this.getTriggerWidth();
1311 if(Ext.isNumber(w)){
1312 this.el.setWidth(w - tw);
1314 this.wrap.setWidth(this.el.getWidth() + tw);
1317 getTriggerWidth: function(){
1318 var tw = this.trigger.getWidth();
1319 if(!this.hideTrigger && tw === 0){
1320 tw = this.defaultTriggerWidth;
1326 alignErrorIcon : function(){
1328 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
1333 onRender : function(ct, position){
1334 this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
1335 Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
1337 this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
1338 this.trigger = this.wrap.createChild(this.triggerConfig ||
1339 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
1342 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
1344 this.resizeEl = this.positionEl = this.wrap;
1345 this.updateEditState();
1348 updateEditState: function(){
1350 if (this.readOnly) {
1351 this.el.dom.readOnly = true;
1352 this.el.addClass('x-trigger-noedit');
1353 this.mun(this.el, 'click', this.onTriggerClick, this);
1354 this.trigger.setDisplayed(false);
1356 if (!this.editable) {
1357 this.el.dom.readOnly = true;
1358 this.el.addClass('x-trigger-noedit');
1359 this.mon(this.el, 'click', this.onTriggerClick, this);
1361 this.el.dom.readOnly = false;
1362 this.el.removeClass('x-trigger-noedit');
1363 this.mun(this.el, 'click', this.onTriggerClick, this);
1365 this.trigger.setDisplayed(!this.hideTrigger);
1367 this.onResize(this.width || this.wrap.getWidth());
1371 setHideTrigger: function(hideTrigger){
1372 if(hideTrigger != this.hideTrigger){
1373 this.hideTrigger = hideTrigger;
1374 this.updateEditState();
1379 * @param {Boolean} value True to allow the user to directly edit the field text
1380 * Allow or prevent the user from directly editing the field text. If false is passed,
1381 * the user will only be able to modify the field using the trigger. Will also add
1382 * a click event to the text field which will call the trigger. This method
1383 * is the runtime equivalent of setting the 'editable' config option at config time.
1385 setEditable: function(editable){
1386 if(editable != this.editable){
1387 this.editable = editable;
1388 this.updateEditState();
1393 * @param {Boolean} value True to prevent the user changing the field and explicitly
1395 * Setting this to true will superceed settings editable and hideTrigger.
1396 * Setting this to false will defer back to editable and hideTrigger. This method
1397 * is the runtime equivalent of setting the 'readOnly' config option at config time.
1399 setReadOnly: function(readOnly){
1400 if(readOnly != this.readOnly){
1401 this.readOnly = readOnly;
1402 this.updateEditState();
1406 afterRender : function(){
1407 Ext.form.TriggerField.superclass.afterRender.call(this);
1411 initTrigger : function(){
1412 this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
1413 this.trigger.addClassOnOver('x-form-trigger-over');
1414 this.trigger.addClassOnClick('x-form-trigger-click');
1418 onDestroy : function(){
1419 Ext.destroy(this.trigger, this.wrap);
1421 this.doc.un('mousedown', this.mimicBlur, this);
1424 Ext.form.TriggerField.superclass.onDestroy.call(this);
1428 onFocus : function(){
1429 Ext.form.TriggerField.superclass.onFocus.call(this);
1431 this.wrap.addClass(this.wrapFocusClass);
1432 this.mimicing = true;
1433 this.doc.on('mousedown', this.mimicBlur, this, {delay: 10});
1434 if(this.monitorTab){
1435 this.on('specialkey', this.checkTab, this);
1441 checkTab : function(me, e){
1442 if(e.getKey() == e.TAB){
1448 onBlur : Ext.emptyFn,
1451 mimicBlur : function(e){
1452 if(!this.isDestroyed && !this.wrap.contains(e.target) && this.validateBlur(e)){
1458 triggerBlur : function(){
1459 this.mimicing = false;
1460 this.doc.un('mousedown', this.mimicBlur, this);
1461 if(this.monitorTab && this.el){
1462 this.un('specialkey', this.checkTab, this);
1464 Ext.form.TriggerField.superclass.onBlur.call(this);
1466 this.wrap.removeClass(this.wrapFocusClass);
1470 beforeBlur : Ext.emptyFn,
1473 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
1474 validateBlur : function(e){
1479 * The function that should handle the trigger's click event. This method does nothing by default
1480 * until overridden by an implementing function. See Ext.form.ComboBox and Ext.form.DateField for
1481 * sample implementations.
1483 * @param {EventObject} e
1485 onTriggerClick : Ext.emptyFn
1488 * @cfg {Boolean} grow @hide
1491 * @cfg {Number} growMin @hide
1494 * @cfg {Number} growMax @hide
1499 * @class Ext.form.TwinTriggerField
1500 * @extends Ext.form.TriggerField
1501 * TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
1502 * to be extended by an implementing class. For an example of implementing this class, see the custom
1503 * SearchField implementation here:
1504 * <a href="http://extjs.com/deploy/ext/examples/form/custom.html">http://extjs.com/deploy/ext/examples/form/custom.html</a>
1506 Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
1508 * @cfg {Mixed} triggerConfig
1509 * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements
1510 * for this Field. (Optional).</p>
1511 * <p>Specify this when you need a customized element to contain the two trigger elements for this Field.
1512 * Each trigger element must be marked by the CSS class <tt>x-form-trigger</tt> (also see
1513 * <tt>{@link #trigger1Class}</tt> and <tt>{@link #trigger2Class}</tt>).</p>
1514 * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing,
1515 * positioning and appearance of the triggers.</p>
1518 * @cfg {String} trigger1Class
1519 * An additional CSS class used to style the trigger button. The trigger will always get the
1520 * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
1523 * @cfg {String} trigger2Class
1524 * An additional CSS class used to style the trigger button. The trigger will always get the
1525 * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
1528 initComponent : function(){
1529 Ext.form.TwinTriggerField.superclass.initComponent.call(this);
1531 this.triggerConfig = {
1532 tag:'span', cls:'x-form-twin-triggers', cn:[
1533 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
1534 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
1538 getTrigger : function(index){
1539 return this.triggers[index];
1542 initTrigger : function(){
1543 var ts = this.trigger.select('.x-form-trigger', true);
1544 var triggerField = this;
1545 ts.each(function(t, all, index){
1546 var triggerIndex = 'Trigger'+(index+1);
1547 t.hide = function(){
1548 var w = triggerField.wrap.getWidth();
1549 this.dom.style.display = 'none';
1550 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
1551 this['hidden' + triggerIndex] = true;
1553 t.show = function(){
1554 var w = triggerField.wrap.getWidth();
1555 this.dom.style.display = '';
1556 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
1557 this['hidden' + triggerIndex] = false;
1560 if(this['hide'+triggerIndex]){
1561 t.dom.style.display = 'none';
1562 this['hidden' + triggerIndex] = true;
1564 this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true});
1565 t.addClassOnOver('x-form-trigger-over');
1566 t.addClassOnClick('x-form-trigger-click');
1568 this.triggers = ts.elements;
1571 getTriggerWidth: function(){
1573 Ext.each(this.triggers, function(t, index){
1574 var triggerIndex = 'Trigger' + (index + 1),
1576 if(w === 0 && !this['hidden' + triggerIndex]){
1577 tw += this.defaultTriggerWidth;
1586 onDestroy : function() {
1587 Ext.destroy(this.triggers);
1588 Ext.form.TwinTriggerField.superclass.onDestroy.call(this);
1592 * The function that should handle the trigger's click event. This method does nothing by default
1593 * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
1594 * for additional information.
1596 * @param {EventObject} e
1598 onTrigger1Click : Ext.emptyFn,
1600 * The function that should handle the trigger's click event. This method does nothing by default
1601 * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
1602 * for additional information.
1604 * @param {EventObject} e
1606 onTrigger2Click : Ext.emptyFn
1608 Ext.reg('trigger', Ext.form.TriggerField);
1610 * @class Ext.form.TextArea
1611 * @extends Ext.form.TextField
1612 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
1613 * support for auto-sizing.
1615 * Creates a new TextArea
1616 * @param {Object} config Configuration options
1619 Ext.form.TextArea = Ext.extend(Ext.form.TextField, {
1621 * @cfg {Number} growMin The minimum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
1622 * (defaults to <tt>60</tt>)
1626 * @cfg {Number} growMax The maximum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
1627 * (defaults to <tt>1000</tt>)
1630 growAppend : ' \n ',
1632 enterIsSpecial : false,
1635 * @cfg {Boolean} preventScrollbars <tt>true</tt> to prevent scrollbars from appearing regardless of how much text is
1636 * in the field. This option is only relevant when {@link #grow} is <tt>true</tt>. Equivalent to setting overflow: hidden, defaults to
1639 preventScrollbars: false,
1641 * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
1642 * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
1643 * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details. Defaults to:</p>
1644 * <pre><code>{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}</code></pre>
1648 onRender : function(ct, position){
1650 this.defaultAutoCreate = {
1652 style:"width:100px;height:60px;",
1656 Ext.form.TextArea.superclass.onRender.call(this, ct, position);
1658 this.textSizeEl = Ext.DomHelper.append(document.body, {
1659 tag: "pre", cls: "x-form-grow-sizer"
1661 if(this.preventScrollbars){
1662 this.el.setStyle("overflow", "hidden");
1664 this.el.setHeight(this.growMin);
1668 onDestroy : function(){
1669 Ext.removeNode(this.textSizeEl);
1670 Ext.form.TextArea.superclass.onDestroy.call(this);
1673 fireKey : function(e){
1674 if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
1675 this.fireEvent("specialkey", this, e);
1680 doAutoSize : function(e){
1681 return !e.isNavKeyPress() || e.getKey() == e.ENTER;
1685 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
1686 * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
1688 autoSize: function(){
1689 if(!this.grow || !this.textSizeEl){
1693 v = Ext.util.Format.htmlEncode(el.dom.value),
1694 ts = this.textSizeEl,
1697 Ext.fly(ts).setWidth(this.el.getWidth());
1701 v += this.growAppend;
1703 v = v.replace(/\n/g, ' <br />');
1707 h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
1708 if(h != this.lastHeight){
1709 this.lastHeight = h;
1710 this.el.setHeight(h);
1711 this.fireEvent("autosize", this, h);
1715 Ext.reg('textarea', Ext.form.TextArea);/**
1716 * @class Ext.form.NumberField
1717 * @extends Ext.form.TextField
1718 * Numeric text field that provides automatic keystroke filtering and numeric validation.
1720 * Creates a new NumberField
1721 * @param {Object} config Configuration options
1722 * @xtype numberfield
1724 Ext.form.NumberField = Ext.extend(Ext.form.TextField, {
1726 * @cfg {RegExp} stripCharsRe @hide
1729 * @cfg {RegExp} maskRe @hide
1732 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
1734 fieldClass: "x-form-field x-form-num-field",
1736 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
1738 allowDecimals : true,
1740 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
1742 decimalSeparator : ".",
1744 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
1746 decimalPrecision : 2,
1748 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
1750 allowNegative : true,
1752 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
1754 minValue : Number.NEGATIVE_INFINITY,
1756 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
1758 maxValue : Number.MAX_VALUE,
1760 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
1762 minText : "The minimum value for this field is {0}",
1764 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
1766 maxText : "The maximum value for this field is {0}",
1768 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
1769 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
1771 nanText : "{0} is not a valid number",
1773 * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
1775 baseChars : "0123456789",
1778 initEvents : function(){
1779 var allowed = this.baseChars + '';
1780 if (this.allowDecimals) {
1781 allowed += this.decimalSeparator;
1783 if (this.allowNegative) {
1786 this.maskRe = new RegExp('[' + Ext.escapeRe(allowed) + ']');
1787 Ext.form.NumberField.superclass.initEvents.call(this);
1791 validateValue : function(value){
1792 if(!Ext.form.NumberField.superclass.validateValue.call(this, value)){
1795 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
1798 value = String(value).replace(this.decimalSeparator, ".");
1800 this.markInvalid(String.format(this.nanText, value));
1803 var num = this.parseValue(value);
1804 if(num < this.minValue){
1805 this.markInvalid(String.format(this.minText, this.minValue));
1808 if(num > this.maxValue){
1809 this.markInvalid(String.format(this.maxText, this.maxValue));
1815 getValue : function(){
1816 return this.fixPrecision(this.parseValue(Ext.form.NumberField.superclass.getValue.call(this)));
1819 setValue : function(v){
1820 v = Ext.isNumber(v) ? v : parseFloat(String(v).replace(this.decimalSeparator, "."));
1821 v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
1822 return Ext.form.NumberField.superclass.setValue.call(this, v);
1826 * Replaces any existing {@link #minValue} with the new value.
1827 * @param {Number} value The minimum value
1829 setMinValue : function(value){
1830 this.minValue = Ext.num(value, Number.NEGATIVE_INFINITY);
1834 * Replaces any existing {@link #maxValue} with the new value.
1835 * @param {Number} value The maximum value
1837 setMaxValue : function(value){
1838 this.maxValue = Ext.num(value, Number.MAX_VALUE);
1842 parseValue : function(value){
1843 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
1844 return isNaN(value) ? '' : value;
1848 fixPrecision : function(value){
1849 var nan = isNaN(value);
1850 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
1851 return nan ? '' : value;
1853 return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
1856 beforeBlur : function(){
1857 var v = this.parseValue(this.getRawValue());
1858 if(!Ext.isEmpty(v)){
1859 this.setValue(this.fixPrecision(v));
1863 Ext.reg('numberfield', Ext.form.NumberField);/**
1864 * @class Ext.form.DateField
1865 * @extends Ext.form.TriggerField
1866 * Provides a date input field with a {@link Ext.DatePicker} dropdown and automatic date validation.
1868 * Create a new DateField
1869 * @param {Object} config
1872 Ext.form.DateField = Ext.extend(Ext.form.TriggerField, {
1874 * @cfg {String} format
1875 * The default date format string which can be overriden for localization support. The format must be
1876 * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/Y'</tt>).
1880 * @cfg {String} altFormats
1881 * Multiple date formats separated by "<tt>|</tt>" to try when parsing a user input value and it
1882 * does not match the defined format (defaults to
1883 * <tt>'m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d'</tt>).
1885 altFormats : "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d",
1887 * @cfg {String} disabledDaysText
1888 * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)
1890 disabledDaysText : "Disabled",
1892 * @cfg {String} disabledDatesText
1893 * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)
1895 disabledDatesText : "Disabled",
1897 * @cfg {String} minText
1898 * The error text to display when the date in the cell is before <tt>{@link #minValue}</tt> (defaults to
1899 * <tt>'The date in this field must be after {minValue}'</tt>).
1901 minText : "The date in this field must be equal to or after {0}",
1903 * @cfg {String} maxText
1904 * The error text to display when the date in the cell is after <tt>{@link #maxValue}</tt> (defaults to
1905 * <tt>'The date in this field must be before {maxValue}'</tt>).
1907 maxText : "The date in this field must be equal to or before {0}",
1909 * @cfg {String} invalidText
1910 * The error text to display when the date in the field is invalid (defaults to
1911 * <tt>'{value} is not a valid date - it must be in the format {format}'</tt>).
1913 invalidText : "{0} is not a valid date - it must be in the format {1}",
1915 * @cfg {String} triggerClass
1916 * An additional CSS class used to style the trigger button. The trigger will always get the
1917 * class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
1918 * (defaults to <tt>'x-form-date-trigger'</tt> which displays a calendar icon).
1920 triggerClass : 'x-form-date-trigger',
1922 * @cfg {Boolean} showToday
1923 * <tt>false</tt> to hide the footer area of the DatePicker containing the Today button and disable
1924 * the keyboard handler for spacebar that selects the current date (defaults to <tt>true</tt>).
1928 * @cfg {Date/String} minValue
1929 * The minimum allowed date. Can be either a Javascript date object or a string date in a
1930 * valid format (defaults to null).
1933 * @cfg {Date/String} maxValue
1934 * The maximum allowed date. Can be either a Javascript date object or a string date in a
1935 * valid format (defaults to null).
1938 * @cfg {Array} disabledDays
1939 * An array of days to disable, 0 based (defaults to null). Some examples:<pre><code>
1940 // disable Sunday and Saturday:
1941 disabledDays: [0, 6]
1942 // disable weekdays:
1943 disabledDays: [1,2,3,4,5]
1947 * @cfg {Array} disabledDates
1948 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
1949 * expression so they are very powerful. Some examples:<pre><code>
1950 // disable these exact dates:
1951 disabledDates: ["03/08/2003", "09/16/2003"]
1952 // disable these days for every year:
1953 disabledDates: ["03/08", "09/16"]
1954 // only match the beginning (useful if you are using short years):
1955 disabledDates: ["^03/08"]
1956 // disable every day in March 2006:
1957 disabledDates: ["03/../2006"]
1958 // disable every day in every March:
1959 disabledDates: ["^03"]
1961 * Note that the format of the dates included in the array should exactly match the {@link #format} config.
1962 * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
1963 * it, you will have to escape the dot when restricting dates. For example: <tt>["03\\.08\\.03"]</tt>.
1966 * @cfg {String/Object} autoCreate
1967 * A {@link Ext.DomHelper DomHelper element specification object}, or <tt>true</tt> for the default element
1968 * specification object:<pre><code>
1969 * autoCreate: {tag: "input", type: "text", size: "10", autocomplete: "off"}
1974 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
1976 initComponent : function(){
1977 Ext.form.DateField.superclass.initComponent.call(this);
1982 * Fires when a date is selected via the date picker.
1983 * @param {Ext.form.DateField} this
1984 * @param {Date} date The date that was selected
1989 if(Ext.isString(this.minValue)){
1990 this.minValue = this.parseDate(this.minValue);
1992 if(Ext.isString(this.maxValue)){
1993 this.maxValue = this.parseDate(this.maxValue);
1995 this.disabledDatesRE = null;
1996 this.initDisabledDays();
1999 initEvents: function() {
2000 Ext.form.DateField.superclass.initEvents.call(this);
2001 this.keyNav = new Ext.KeyNav(this.el, {
2002 "down": function(e) {
2003 this.onTriggerClick();
2012 initDisabledDays : function(){
2013 if(this.disabledDates){
2014 var dd = this.disabledDates,
2015 len = dd.length - 1,
2018 Ext.each(dd, function(d, i){
2019 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
2024 this.disabledDatesRE = new RegExp(re + ')');
2029 * Replaces any existing disabled dates with new values and refreshes the DatePicker.
2030 * @param {Array} disabledDates An array of date strings (see the <tt>{@link #disabledDates}</tt> config
2031 * for details on supported values) used to disable a pattern of dates.
2033 setDisabledDates : function(dd){
2034 this.disabledDates = dd;
2035 this.initDisabledDays();
2037 this.menu.picker.setDisabledDates(this.disabledDatesRE);
2042 * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
2043 * @param {Array} disabledDays An array of disabled day indexes. See the <tt>{@link #disabledDays}</tt>
2044 * config for details on supported values.
2046 setDisabledDays : function(dd){
2047 this.disabledDays = dd;
2049 this.menu.picker.setDisabledDays(dd);
2054 * Replaces any existing <tt>{@link #minValue}</tt> with the new value and refreshes the DatePicker.
2055 * @param {Date} value The minimum date that can be selected
2057 setMinValue : function(dt){
2058 this.minValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
2060 this.menu.picker.setMinDate(this.minValue);
2065 * Replaces any existing <tt>{@link #maxValue}</tt> with the new value and refreshes the DatePicker.
2066 * @param {Date} value The maximum date that can be selected
2068 setMaxValue : function(dt){
2069 this.maxValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
2071 this.menu.picker.setMaxDate(this.maxValue);
2076 validateValue : function(value){
2077 value = this.formatDate(value);
2078 if(!Ext.form.DateField.superclass.validateValue.call(this, value)){
2081 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
2085 value = this.parseDate(value);
2087 this.markInvalid(String.format(this.invalidText, svalue, this.format));
2090 var time = value.getTime();
2091 if(this.minValue && time < this.minValue.getTime()){
2092 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
2095 if(this.maxValue && time > this.maxValue.getTime()){
2096 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
2099 if(this.disabledDays){
2100 var day = value.getDay();
2101 for(var i = 0; i < this.disabledDays.length; i++) {
2102 if(day === this.disabledDays[i]){
2103 this.markInvalid(this.disabledDaysText);
2108 var fvalue = this.formatDate(value);
2109 if(this.disabledDatesRE && this.disabledDatesRE.test(fvalue)){
2110 this.markInvalid(String.format(this.disabledDatesText, fvalue));
2117 // Provides logic to override the default TriggerField.validateBlur which just returns true
2118 validateBlur : function(){
2119 return !this.menu || !this.menu.isVisible();
2123 * Returns the current date value of the date field.
2124 * @return {Date} The date value
2126 getValue : function(){
2127 return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";
2131 * Sets the value of the date field. You can pass a date object or any string that can be
2132 * parsed into a valid date, using <tt>{@link #format}</tt> as the date format, according
2133 * to the same rules as {@link Date#parseDate} (the default format used is <tt>"m/d/Y"</tt>).
2136 //All of these calls set the same date value (May 4, 2006)
2138 //Pass a date object:
2139 var dt = new Date('5/4/2006');
2140 dateField.setValue(dt);
2142 //Pass a date string (default format):
2143 dateField.setValue('05/04/2006');
2145 //Pass a date string (custom format):
2146 dateField.format = 'Y-m-d';
2147 dateField.setValue('2006-05-04');
2149 * @param {String/Date} date The date or valid date string
2150 * @return {Ext.form.Field} this
2152 setValue : function(date){
2153 return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
2157 parseDate : function(value){
2158 if(!value || Ext.isDate(value)){
2161 var v = Date.parseDate(value, this.format);
2162 if(!v && this.altFormats){
2163 if(!this.altFormatsArray){
2164 this.altFormatsArray = this.altFormats.split("|");
2166 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
2167 v = Date.parseDate(value, this.altFormatsArray[i]);
2174 onDestroy : function(){
2175 Ext.destroy(this.menu, this.keyNav);
2176 Ext.form.DateField.superclass.onDestroy.call(this);
2180 formatDate : function(date){
2181 return Ext.isDate(date) ? date.dateFormat(this.format) : date;
2185 * @method onTriggerClick
2189 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
2190 onTriggerClick : function(){
2194 if(this.menu == null){
2195 this.menu = new Ext.menu.DateMenu({
2197 focusOnSelect: false
2201 Ext.apply(this.menu.picker, {
2202 minDate : this.minValue,
2203 maxDate : this.maxValue,
2204 disabledDatesRE : this.disabledDatesRE,
2205 disabledDatesText : this.disabledDatesText,
2206 disabledDays : this.disabledDays,
2207 disabledDaysText : this.disabledDaysText,
2208 format : this.format,
2209 showToday : this.showToday,
2210 minText : String.format(this.minText, this.formatDate(this.minValue)),
2211 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
2213 this.menu.picker.setValue(this.getValue() || new Date());
2214 this.menu.show(this.el, "tl-bl?");
2215 this.menuEvents('on');
2219 menuEvents: function(method){
2220 this.menu[method]('select', this.onSelect, this);
2221 this.menu[method]('hide', this.onMenuHide, this);
2222 this.menu[method]('show', this.onFocus, this);
2225 onSelect: function(m, d){
2227 this.fireEvent('select', this, d);
2231 onMenuHide: function(){
2232 this.focus(false, 60);
2233 this.menuEvents('un');
2237 beforeBlur : function(){
2238 var v = this.parseDate(this.getRawValue());
2245 * @cfg {Boolean} grow @hide
2248 * @cfg {Number} growMin @hide
2251 * @cfg {Number} growMax @hide
2258 Ext.reg('datefield', Ext.form.DateField);/**
\r
2259 * @class Ext.form.DisplayField
\r
2260 * @extends Ext.form.Field
\r
2261 * A display-only text field which is not validated and not submitted.
\r
2263 * Creates a new DisplayField.
\r
2264 * @param {Object} config Configuration options
\r
2265 * @xtype displayfield
\r
2267 Ext.form.DisplayField = Ext.extend(Ext.form.Field, {
\r
2268 validationEvent : false,
\r
2269 validateOnBlur : false,
\r
2270 defaultAutoCreate : {tag: "div"},
\r
2272 * @cfg {String} fieldClass The default CSS class for the field (defaults to <tt>"x-form-display-field"</tt>)
\r
2274 fieldClass : "x-form-display-field",
\r
2276 * @cfg {Boolean} htmlEncode <tt>false</tt> to skip HTML-encoding the text when rendering it (defaults to
\r
2277 * <tt>false</tt>). This might be useful if you want to include tags in the field's innerHTML rather than
\r
2278 * rendering them as string literals per the default logic.
\r
2280 htmlEncode: false,
\r
2283 initEvents : Ext.emptyFn,
\r
2285 isValid : function(){
\r
2289 validate : function(){
\r
2293 getRawValue : function(){
\r
2294 var v = this.rendered ? this.el.dom.innerHTML : Ext.value(this.value, '');
\r
2295 if(v === this.emptyText){
\r
2298 if(this.htmlEncode){
\r
2299 v = Ext.util.Format.htmlDecode(v);
\r
2304 getValue : function(){
\r
2305 return this.getRawValue();
\r
2308 getName: function() {
\r
2312 setRawValue : function(v){
\r
2313 if(this.htmlEncode){
\r
2314 v = Ext.util.Format.htmlEncode(v);
\r
2316 return this.rendered ? (this.el.dom.innerHTML = (Ext.isEmpty(v) ? '' : v)) : (this.value = v);
\r
2319 setValue : function(v){
\r
2320 this.setRawValue(v);
\r
2324 * @cfg {String} inputType
\r
2328 * @cfg {Boolean} disabled
\r
2332 * @cfg {Boolean} readOnly
\r
2336 * @cfg {Boolean} validateOnBlur
\r
2340 * @cfg {Number} validationDelay
\r
2344 * @cfg {String/Boolean} validationEvent
\r
2349 Ext.reg('displayfield', Ext.form.DisplayField);
\r
2351 * @class Ext.form.ComboBox
2352 * @extends Ext.form.TriggerField
2353 * <p>A combobox control with support for autocomplete, remote-loading, paging and many other features.</p>
2354 * <p>A ComboBox works in a similar manner to a traditional HTML <select> field. The difference is
2355 * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input
2356 * field to hold the value of the valueField. The <i>{@link #displayField}</i> is shown in the text field
2357 * which is named according to the {@link #name}.</p>
2358 * <p><b><u>Events</u></b></p>
2359 * <p>To do something when something in ComboBox is selected, configure the select event:<pre><code>
2360 var cb = new Ext.form.ComboBox({
2361 // all of your config options
2364 'select': yourFunction
2368 // Alternatively, you can assign events after the object is created:
2369 var cb = new Ext.form.ComboBox(yourOptions);
2370 cb.on('select', yourFunction, yourScope);
2373 * <p><b><u>ComboBox in Grid</u></b></p>
2374 * <p>If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer}
2375 * will be needed to show the displayField when the editor is not active. Set up the renderer manually, or implement
2376 * a reusable render, for example:<pre><code>
2377 // create reusable renderer
2378 Ext.util.Format.comboRenderer = function(combo){
2379 return function(value){
2380 var record = combo.findRecord(combo.{@link #valueField}, value);
2381 return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
2385 // create the combo instance
2386 var combo = new Ext.form.ComboBox({
2387 {@link #typeAhead}: true,
2388 {@link #triggerAction}: 'all',
2389 {@link #lazyRender}:true,
2390 {@link #mode}: 'local',
2391 {@link #store}: new Ext.data.ArrayStore({
2397 data: [[1, 'item1'], [2, 'item2']]
2399 {@link #valueField}: 'myId',
2400 {@link #displayField}: 'displayText'
2403 // snippet of column model used within grid
2404 var cm = new Ext.grid.ColumnModel([{
2407 header: "Some Header",
2408 dataIndex: 'whatever',
2410 editor: combo, // specify reference to combo instance
2411 renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
2417 * <p><b><u>Filtering</u></b></p>
2418 * <p>A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox
2419 * store manually see <tt>{@link #lastQuery}</tt>.</p>
2421 * Create a new ComboBox.
2422 * @param {Object} config Configuration options
2425 Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, {
2427 * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox.
2428 * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or
2429 * {@link Ext.form.FormPanel}, you must also set <tt>{@link #lazyRender} = true</tt>.
2432 * @cfg {Boolean} lazyRender <tt>true</tt> to prevent the ComboBox from rendering until requested
2433 * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}),
2434 * defaults to <tt>false</tt>).
2437 * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or <tt>true</tt> for a default
2438 * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
2439 * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details. Defaults to:</p>
2440 * <pre><code>{tag: "input", type: "text", size: "24", autocomplete: "off"}</code></pre>
2443 * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).
2444 * Acceptable values for this property are:
2445 * <div class="mdetail-params"><ul>
2446 * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
2447 * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally,
2448 * automatically generating {@link Ext.data.Field#name field names} to work with all data components.
2449 * <div class="mdetail-params"><ul>
2450 * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
2451 * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
2452 * {@link #valueField} and {@link #displayField})</div></li>
2453 * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
2454 * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
2455 * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
2456 * </div></li></ul></div></li></ul></div>
2457 * <p>See also <tt>{@link #mode}</tt>.</p>
2460 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
2461 * the dropdown list (defaults to undefined, with no header element)
2465 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
2467 * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown
2468 * list (defaults to the width of the ComboBox field). See also <tt>{@link #minListWidth}
2471 * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
2472 * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field1'</tt> if
2473 * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
2474 * the store configuration}).
2475 * <p>See also <tt>{@link #valueField}</tt>.</p>
2476 * <p><b>Note</b>: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a
2477 * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not
2481 * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this
2482 * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field2'</tt> if
2483 * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
2484 * the store configuration}).
2485 * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be
2486 * mapped. See also <tt>{@link #hiddenName}</tt>, <tt>{@link #hiddenValue}</tt>, and <tt>{@link #displayField}</tt>.</p>
2489 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
2490 * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically
2491 * post during a form submission. See also {@link #valueField}.
2492 * <p><b>Note</b>: the hidden field's id will also default to this name if {@link #hiddenId} is not specified.
2493 * The ComboBox {@link Ext.Component#id id} and the <tt>{@link #hiddenId}</tt> <b>should be different</b>, since
2494 * no two DOM nodes should share the same id. So, if the ComboBox <tt>{@link Ext.form.Field#name name}</tt> and
2495 * <tt>hiddenName</tt> are the same, you should specify a unique <tt>{@link #hiddenId}</tt>.</p>
2498 * @cfg {String} hiddenId If <tt>{@link #hiddenName}</tt> is specified, <tt>hiddenId</tt> can also be provided
2499 * to give the hidden field a unique id (defaults to the <tt>{@link #hiddenName}</tt>). The <tt>hiddenId</tt>
2500 * and combo {@link Ext.Component#id id} should be different, since no two DOM
2501 * nodes should share the same id.
2504 * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is
2505 * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured
2506 * <tt>{@link Ext.form.Field#value value}</tt>.
2509 * @cfg {String} listClass The CSS class to add to the predefined <tt>'x-combo-list'</tt> class
2510 * applied the dropdown list element (defaults to '').
2514 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list
2515 * (defaults to <tt>'x-combo-selected'</tt>)
2517 selectedClass : 'x-combo-selected',
2519 * @cfg {String} listEmptyText The empty text to display in the data view if no items are found.
2524 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always
2525 * get the class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
2526 * (defaults to <tt>'x-form-arrow-trigger'</tt> which displays a downward arrow icon).
2528 triggerClass : 'x-form-arrow-trigger',
2530 * @cfg {Boolean/String} shadow <tt>true</tt> or <tt>"sides"</tt> for the default effect, <tt>"frame"</tt> for
2531 * 4-way shadow, and <tt>"drop"</tt> for bottom-right
2535 * @cfg {String} listAlign A valid anchor position value. See <tt>{@link Ext.Element#alignTo}</tt> for details
2536 * on supported anchor positions (defaults to <tt>'tl-bl?'</tt>)
2538 listAlign : 'tl-bl?',
2540 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown
2541 * (defaults to <tt>300</tt>)
2545 * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its
2546 * distance to the viewport edges (defaults to <tt>90</tt>)
2550 * @cfg {String} triggerAction The action to execute when the trigger is clicked.
2551 * <div class="mdetail-params"><ul>
2552 * <li><b><tt>'query'</tt></b> : <b>Default</b>
2553 * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.</p></li>
2554 * <li><b><tt>'all'</tt></b> :
2555 * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>
2557 * <p>See also <code>{@link #queryParam}</code>.</p>
2559 triggerAction : 'query',
2561 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
2562 * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #mode} = 'remote'</tt> or <tt>0</tt> if
2563 * <tt>{@link #mode} = 'local'</tt>, does not apply if
2564 * <tt>{@link Ext.form.TriggerField#editable editable} = false</tt>).
2568 * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being
2569 * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
2570 * to <tt>false</tt>)
2574 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
2575 * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #mode} = 'remote'</tt>
2576 * or <tt>10</tt> if <tt>{@link #mode} = 'local'</tt>)
2580 * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.PagingToolbar} is displayed in the
2581 * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
2582 * {@link Ext.PagingToolbar#pageSize limit} parameters. Only applies when <tt>{@link #mode} = 'remote'</tt>
2583 * (defaults to <tt>0</tt>).
2587 * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.
2588 * Only applies when <tt>{@link Ext.form.TriggerField#editable editable} = true</tt> (defaults to
2591 selectOnFocus : false,
2593 * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)
2594 * as it will be passed on the querystring (defaults to <tt>'query'</tt>)
2596 queryParam : 'query',
2598 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
2599 * when <tt>{@link #mode} = 'remote'</tt> (defaults to <tt>'Loading...'</tt>)
2601 loadingText : 'Loading...',
2603 * @cfg {Boolean} resizable <tt>true</tt> to add a resize handle to the bottom of the dropdown list
2604 * (creates an {@link Ext.Resizable} with 'se' {@link Ext.Resizable#pinned pinned} handles).
2605 * Defaults to <tt>false</tt>.
2609 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if
2610 * <tt>{@link #resizable} = true</tt> (defaults to <tt>8</tt>)
2614 * @cfg {String} allQuery The text query to send to the server to return all records for the list
2615 * with no filtering (defaults to '')
2619 * @cfg {String} mode Acceptable values are:
2620 * <div class="mdetail-params"><ul>
2621 * <li><b><tt>'remote'</tt></b> : <b>Default</b>
2622 * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger
2623 * is clicked. If you do not want the store to be automatically loaded the first time the trigger is
2624 * clicked, set to <tt>'local'</tt> and manually load the store. To force a requery of the store
2625 * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>
2626 * <li><b><tt>'local'</tt></b> :
2627 * <p class="sub-desc">ComboBox loads local data</p>
2629 var combo = new Ext.form.ComboBox({
2630 renderTo: document.body,
2632 store: new Ext.data.ArrayStore({
2635 'myId', // numeric value is the key
2638 data: [[1, 'item1'], [2, 'item2']] // data is local
2641 displayField: 'displayText',
2642 triggerAction: 'all'
2644 * </code></pre></li>
2649 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to <tt>70</tt>, will
2650 * be ignored if <tt>{@link #listWidth}</tt> has a higher value)
2654 * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,
2655 * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)
2657 forceSelection : false,
2659 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
2660 * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)
2662 typeAheadDelay : 250,
2664 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
2665 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
2666 * default text is used, it means there is no value set and no validation will occur on this field.
2670 * @cfg {Boolean} lazyInit <tt>true</tt> to not initialize the list for this combo until the field is focused
2671 * (defaults to <tt>true</tt>)
2676 * @cfg {Boolean} clearFilterOnReset <tt>true</tt> to clear any filters on the store (when in local mode) when reset is called
2677 * (defaults to <tt>true</tt>)
2679 clearFilterOnReset : true,
2682 * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
2683 * If a hiddenName is specified, setting this to true will cause both the hidden field and the element to be submitted.
2684 * Defaults to <tt>undefined</tt>.
2686 submitValue: undefined,
2689 * The value of the match string used to filter the store. Delete this property to force a requery.
2692 var combo = new Ext.form.ComboBox({
2697 // delete the previous query in the beforequery event or set
2698 // combo.lastQuery = null (this will reload the store the next time it expands)
2699 beforequery: function(qe){
2700 delete qe.combo.lastQuery;
2705 * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
2706 * configure the combo with <tt>lastQuery=''</tt>. Example use:
2708 var combo = new Ext.form.ComboBox({
2711 triggerAction: 'all',
2715 * @property lastQuery
2720 initComponent : function(){
2721 Ext.form.ComboBox.superclass.initComponent.call(this);
2725 * Fires when the dropdown list is expanded
2726 * @param {Ext.form.ComboBox} combo This combo box
2731 * Fires when the dropdown list is collapsed
2732 * @param {Ext.form.ComboBox} combo This combo box
2736 * @event beforeselect
2737 * Fires before a list item is selected. Return false to cancel the selection.
2738 * @param {Ext.form.ComboBox} combo This combo box
2739 * @param {Ext.data.Record} record The data record returned from the underlying store
2740 * @param {Number} index The index of the selected item in the dropdown list
2745 * Fires when a list item is selected
2746 * @param {Ext.form.ComboBox} combo This combo box
2747 * @param {Ext.data.Record} record The data record returned from the underlying store
2748 * @param {Number} index The index of the selected item in the dropdown list
2752 * @event beforequery
2753 * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
2754 * cancel property to true.
2755 * @param {Object} queryEvent An object that has these properties:<ul>
2756 * <li><code>combo</code> : Ext.form.ComboBox <div class="sub-desc">This combo box</div></li>
2757 * <li><code>query</code> : String <div class="sub-desc">The query</div></li>
2758 * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>
2759 * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>
2765 var s = Ext.getDom(this.transform);
2766 if(!this.hiddenName){
2767 this.hiddenName = s.name;
2770 this.mode = 'local';
2771 var d = [], opts = s.options;
2772 for(var i = 0, len = opts.length;i < len; i++){
2774 value = (o.hasAttribute ? o.hasAttribute('value') : o.getAttributeNode('value').specified) ? o.value : o.text;
2775 if(o.selected && Ext.isEmpty(this.value, true)) {
2778 d.push([value, o.text]);
2780 this.store = new Ext.data.ArrayStore({
2782 fields: ['value', 'text'],
2786 this.valueField = 'value';
2787 this.displayField = 'text';
2789 s.name = Ext.id(); // wipe out the name in case somewhere else they have a reference
2790 if(!this.lazyRender){
2792 this.el = Ext.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
2793 this.render(this.el.parentNode, s);
2797 //auto-configure store from local array data
2798 else if(this.store){
2799 this.store = Ext.StoreMgr.lookup(this.store);
2800 if(this.store.autoCreated){
2801 this.displayField = this.valueField = 'field1';
2802 if(!this.store.expandData){
2803 this.displayField = 'field2';
2805 this.mode = 'local';
2809 this.selectedIndex = -1;
2810 if(this.mode == 'local'){
2811 if(!Ext.isDefined(this.initialConfig.queryDelay)){
2812 this.queryDelay = 10;
2814 if(!Ext.isDefined(this.initialConfig.minChars)){
2821 onRender : function(ct, position){
2822 if(this.hiddenName && !Ext.isDefined(this.submitValue)){
2823 this.submitValue = false;
2825 Ext.form.ComboBox.superclass.onRender.call(this, ct, position);
2826 if(this.hiddenName){
2827 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,
2828 id: (this.hiddenId||this.hiddenName)}, 'before', true);
2832 this.el.dom.setAttribute('autocomplete', 'off');
2838 this.on('focus', this.initList, this, {single: true});
2843 initValue : function(){
2844 Ext.form.ComboBox.superclass.initValue.call(this);
2845 if(this.hiddenField){
2846 this.hiddenField.value =
2847 Ext.isDefined(this.hiddenValue) ? this.hiddenValue :
2848 Ext.isDefined(this.value) ? this.value : '';
2853 initList : function(){
2855 var cls = 'x-combo-list';
2857 this.list = new Ext.Layer({
2858 parentEl: this.getListParent(),
2859 shadow: this.shadow,
2860 cls: [cls, this.listClass].join(' '),
2865 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
2866 this.list.setSize(lw, 0);
2867 this.list.swallowEvent('mousewheel');
2868 this.assetHeight = 0;
2869 if(this.syncFont !== false){
2870 this.list.setStyle('font-size', this.el.getStyle('font-size'));
2873 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
2874 this.assetHeight += this.header.getHeight();
2877 this.innerList = this.list.createChild({cls:cls+'-inner'});
2878 this.mon(this.innerList, 'mouseover', this.onViewOver, this);
2879 this.mon(this.innerList, 'mousemove', this.onViewMove, this);
2880 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
2883 this.footer = this.list.createChild({cls:cls+'-ft'});
2884 this.pageTb = new Ext.PagingToolbar({
2886 pageSize: this.pageSize,
2887 renderTo:this.footer
2889 this.assetHeight += this.footer.getHeight();
2894 * @cfg {String/Ext.XTemplate} tpl <p>The template string, or {@link Ext.XTemplate} instance to
2895 * use to display each item in the dropdown list. The dropdown list is displayed in a
2896 * DataView. See {@link #view}.</p>
2897 * <p>The default template string is:</p><pre><code>
2898 '<tpl for="."><div class="x-combo-list-item">{' + this.displayField + '}</div></tpl>'
2900 * <p>Override the default value to create custom UI layouts for items in the list.
2901 * For example:</p><pre><code>
2902 '<tpl for="."><div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}</div></tpl>'
2904 * <p>The template <b>must</b> contain one or more substitution parameters using field
2905 * names from the Combo's</b> {@link #store Store}. In the example above an
2906 * <pre>ext:qtip</pre> attribute is added to display other fields from the Store.</p>
2907 * <p>To preserve the default visual look of list items, add the CSS class name
2908 * <pre>x-combo-list-item</pre> to the template's container element.</p>
2909 * <p>Also see {@link #itemSelector} for additional details.</p>
2911 this.tpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
2913 * @cfg {String} itemSelector
2914 * <p>A simple CSS selector (e.g. div.some-class or span:first-child) that will be
2915 * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown
2916 * display will be working with.</p>
2917 * <p><b>Note</b>: this setting is <b>required</b> if a custom XTemplate has been
2918 * specified in {@link #tpl} which assigns a class other than <pre>'x-combo-list-item'</pre>
2919 * to dropdown list items</b>
2924 * The {@link Ext.DataView DataView} used to display the ComboBox's options.
2925 * @type Ext.DataView
2927 this.view = new Ext.DataView({
2928 applyTo: this.innerList,
2931 selectedClass: this.selectedClass,
2932 itemSelector: this.itemSelector || '.' + cls + '-item',
2933 emptyText: this.listEmptyText
2936 this.mon(this.view, 'click', this.onViewClick, this);
2938 this.bindStore(this.store, true);
2941 this.resizer = new Ext.Resizable(this.list, {
2942 pinned:true, handles:'se'
2944 this.mon(this.resizer, 'resize', function(r, w, h){
2945 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
2947 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
2948 this.restrictHeight();
2951 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
2957 * <p>Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.</p>
2958 * A custom implementation may be provided as a configuration option if the floating list needs to be rendered
2959 * to a different Element. An example might be rendering the list inside a Menu so that clicking
2960 * the list does not hide the Menu:<pre><code>
2961 var store = new Ext.data.ArrayStore({
2963 fields: ['initials', 'fullname'],
2965 ['FF', 'Fred Flintstone'],
2966 ['BR', 'Barney Rubble']
2970 var combo = new Ext.form.ComboBox({
2972 displayField: 'fullname',
2973 emptyText: 'Select a name...',
2974 forceSelection: true,
2975 getListParent: function() {
2976 return this.el.up('.x-menu');
2978 iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
2980 selectOnFocus: true,
2981 triggerAction: 'all',
2986 var menu = new Ext.menu.Menu({
2989 combo // A Field in a Menu
2994 getListParent : function() {
2995 return document.body;
2999 * Returns the store associated with this combo.
3000 * @return {Ext.data.Store} The store
3002 getStore : function(){
3007 bindStore : function(store, initial){
3008 if(this.store && !initial){
3009 if(this.store !== store && this.store.autoDestroy){
3010 this.store.destroy();
3012 this.store.un('beforeload', this.onBeforeLoad, this);
3013 this.store.un('load', this.onLoad, this);
3014 this.store.un('exception', this.collapse, this);
3019 this.view.bindStore(null);
3022 this.pageTb.bindStore(null);
3028 this.lastQuery = null;
3030 this.pageTb.bindStore(store);
3034 this.store = Ext.StoreMgr.lookup(store);
3037 beforeload: this.onBeforeLoad,
3039 exception: this.collapse
3043 this.view.bindStore(store);
3049 Ext.form.ComboBox.superclass.reset.call(this);
3050 if(this.clearFilterOnReset && this.mode == 'local'){
3051 this.store.clearFilter();
3056 initEvents : function(){
3057 Ext.form.ComboBox.superclass.initEvents.call(this);
3059 this.keyNav = new Ext.KeyNav(this.el, {
3061 this.inKeyMode = true;
3065 "down" : function(e){
3066 if(!this.isExpanded()){
3067 this.onTriggerClick();
3069 this.inKeyMode = true;
3074 "enter" : function(e){
3078 "esc" : function(e){
3082 "tab" : function(e){
3083 this.onViewClick(false);
3089 doRelay : function(e, h, hname){
3090 if(hname == 'down' || this.scope.isExpanded()){
3091 // this MUST be called before ComboBox#fireKey()
3092 var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
3093 if(!Ext.isIE && Ext.EventManager.useKeydown){
3094 // call Combo#fireKey() for browsers which use keydown event (except IE)
3095 this.scope.fireKey(e);
3102 forceKeyDown : true,
3103 defaultEventAction: 'stopEvent'
3105 this.queryDelay = Math.max(this.queryDelay || 10,
3106 this.mode == 'local' ? 10 : 250);
3107 this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
3109 this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
3111 if(!this.enableKeyEvents){
3112 this.mon(this.el, 'keyup', this.onKeyUp, this);
3117 onDestroy : function(){
3119 this.dqTask.cancel();
3122 this.bindStore(null);
3129 Ext.destroyMembers(this, 'hiddenField');
3130 Ext.form.ComboBox.superclass.onDestroy.call(this);
3134 fireKey : function(e){
3135 if (!this.isExpanded()) {
3136 Ext.form.ComboBox.superclass.fireKey.call(this, e);
3141 onResize : function(w, h){
3142 Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
3143 if(this.isVisible() && this.list){
3146 this.bufferSize = w;
3150 doResize: function(w){
3151 if(!Ext.isDefined(this.listWidth)){
3152 var lw = Math.max(w, this.minListWidth);
3153 this.list.setWidth(lw);
3154 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
3159 onEnable : function(){
3160 Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
3161 if(this.hiddenField){
3162 this.hiddenField.disabled = false;
3167 onDisable : function(){
3168 Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
3169 if(this.hiddenField){
3170 this.hiddenField.disabled = true;
3175 onBeforeLoad : function(){
3179 this.innerList.update(this.loadingText ?
3180 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
3181 this.restrictHeight();
3182 this.selectedIndex = -1;
3186 onLoad : function(){
3190 if(this.store.getCount() > 0 || this.listEmptyText){
3192 this.restrictHeight();
3193 if(this.lastQuery == this.allQuery){
3195 this.el.dom.select();
3197 if(!this.selectByValue(this.value, true)){
3198 this.select(0, true);
3202 if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){
3203 this.taTask.delay(this.typeAheadDelay);
3207 this.onEmptyResults();
3213 onTypeAhead : function(){
3214 if(this.store.getCount() > 0){
3215 var r = this.store.getAt(0);
3216 var newValue = r.data[this.displayField];
3217 var len = newValue.length;
3218 var selStart = this.getRawValue().length;
3219 if(selStart != len){
3220 this.setRawValue(newValue);
3221 this.selectText(selStart, newValue.length);
3227 onSelect : function(record, index){
3228 if(this.fireEvent('beforeselect', this, record, index) !== false){
3229 this.setValue(record.data[this.valueField || this.displayField]);
3231 this.fireEvent('select', this, record, index);
3236 getName: function(){
3237 var hf = this.hiddenField;
3238 return hf && hf.name ? hf.name : this.hiddenName || Ext.form.ComboBox.superclass.getName.call(this);
3242 * Returns the currently selected field value or empty string if no value is set.
3243 * @return {String} value The selected value
3245 getValue : function(){
3246 if(this.valueField){
3247 return Ext.isDefined(this.value) ? this.value : '';
3249 return Ext.form.ComboBox.superclass.getValue.call(this);
3254 * Clears any text/value currently set in the field
3256 clearValue : function(){
3257 if(this.hiddenField){
3258 this.hiddenField.value = '';
3260 this.setRawValue('');
3261 this.lastSelectionText = '';
3262 this.applyEmptyText();
3267 * Sets the specified value into the field. If the value finds a match, the corresponding record text
3268 * will be displayed in the field. If the value does not match the data value of an existing item,
3269 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
3270 * Otherwise the field will be blank (although the value will still be set).
3271 * @param {String} value The value to match
3272 * @return {Ext.form.Field} this
3274 setValue : function(v){
3276 if(this.valueField){
3277 var r = this.findRecord(this.valueField, v);
3279 text = r.data[this.displayField];
3280 }else if(Ext.isDefined(this.valueNotFoundText)){
3281 text = this.valueNotFoundText;
3284 this.lastSelectionText = text;
3285 if(this.hiddenField){
3286 this.hiddenField.value = v;
3288 Ext.form.ComboBox.superclass.setValue.call(this, text);
3294 findRecord : function(prop, value){
3296 if(this.store.getCount() > 0){
3297 this.store.each(function(r){
3298 if(r.data[prop] == value){
3308 onViewMove : function(e, t){
3309 this.inKeyMode = false;
3313 onViewOver : function(e, t){
3314 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
3317 var item = this.view.findItemFromChild(t);
3319 var index = this.view.indexOf(item);
3320 this.select(index, false);
3325 onViewClick : function(doFocus){
3326 var index = this.view.getSelectedIndexes()[0],
3330 this.onSelect(r, index);
3331 }else if(s.getCount() === 0){
3332 this.onEmptyResults();
3334 if(doFocus !== false){
3340 restrictHeight : function(){
3341 this.innerList.dom.style.height = '';
3342 var inner = this.innerList.dom,
3343 pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,
3344 h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
3345 ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
3346 hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
3347 space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;
3349 h = Math.min(h, space, this.maxHeight);
3351 this.innerList.setHeight(h);
3352 this.list.beginUpdate();
3353 this.list.setHeight(h+pad);
3354 this.list.alignTo(this.wrap, this.listAlign);
3355 this.list.endUpdate();
3359 onEmptyResults : function(){
3364 * Returns true if the dropdown list is expanded, else false.
3366 isExpanded : function(){
3367 return this.list && this.list.isVisible();
3371 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
3372 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
3373 * @param {String} value The data value of the item to select
3374 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
3375 * selected item if it is not currently in view (defaults to true)
3376 * @return {Boolean} True if the value matched an item in the list, else false
3378 selectByValue : function(v, scrollIntoView){
3379 if(!Ext.isEmpty(v, true)){
3380 var r = this.findRecord(this.valueField || this.displayField, v);
3382 this.select(this.store.indexOf(r), scrollIntoView);
3390 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
3391 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
3392 * @param {Number} index The zero-based index of the list item to select
3393 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
3394 * selected item if it is not currently in view (defaults to true)
3396 select : function(index, scrollIntoView){
3397 this.selectedIndex = index;
3398 this.view.select(index);
3399 if(scrollIntoView !== false){
3400 var el = this.view.getNode(index);
3402 this.innerList.scrollChildIntoView(el, false);
3408 selectNext : function(){
3409 var ct = this.store.getCount();
3411 if(this.selectedIndex == -1){
3413 }else if(this.selectedIndex < ct-1){
3414 this.select(this.selectedIndex+1);
3420 selectPrev : function(){
3421 var ct = this.store.getCount();
3423 if(this.selectedIndex == -1){
3425 }else if(this.selectedIndex !== 0){
3426 this.select(this.selectedIndex-1);
3432 onKeyUp : function(e){
3434 if(this.editable !== false && this.readOnly !== true && (k == e.BACKSPACE || !e.isSpecialKey())){
3436 this.dqTask.delay(this.queryDelay);
3438 Ext.form.ComboBox.superclass.onKeyUp.call(this, e);
3442 validateBlur : function(){
3443 return !this.list || !this.list.isVisible();
3447 initQuery : function(){
3448 this.doQuery(this.getRawValue());
3452 beforeBlur : function(){
3453 var val = this.getRawValue(),
3454 rec = this.findRecord(this.displayField, val);
3455 if(!rec && this.forceSelection){
3456 if(val.length > 0 && val != this.emptyText){
3457 this.el.dom.value = Ext.isEmpty(this.lastSelectionText) ? '' : this.lastSelectionText;
3458 this.applyEmptyText();
3464 val = rec.get(this.valueField || this.displayField);
3471 * Execute a query to filter the dropdown list. Fires the {@link #beforequery} event prior to performing the
3472 * query allowing the query action to be canceled if needed.
3473 * @param {String} query The SQL query to execute
3474 * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer
3475 * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option. It
3476 * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)
3478 doQuery : function(q, forceAll){
3479 q = Ext.isEmpty(q) ? '' : q;
3486 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
3490 forceAll = qe.forceAll;
3491 if(forceAll === true || (q.length >= this.minChars)){
3492 if(this.lastQuery !== q){
3494 if(this.mode == 'local'){
3495 this.selectedIndex = -1;
3497 this.store.clearFilter();
3499 this.store.filter(this.displayField, q);
3503 this.store.baseParams[this.queryParam] = q;
3505 params: this.getParams(q)
3510 this.selectedIndex = -1;
3517 getParams : function(q){
3519 //p[this.queryParam] = q;
3522 p.limit = this.pageSize;
3528 * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.
3530 collapse : function(){
3531 if(!this.isExpanded()){
3535 Ext.getDoc().un('mousewheel', this.collapseIf, this);
3536 Ext.getDoc().un('mousedown', this.collapseIf, this);
3537 this.fireEvent('collapse', this);
3541 collapseIf : function(e){
3542 if(!e.within(this.wrap) && !e.within(this.list)){
3548 * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.
3550 expand : function(){
3551 if(this.isExpanded() || !this.hasFocus){
3554 if(this.bufferSize){
3555 this.doResize(this.bufferSize);
3556 delete this.bufferSize;
3558 this.list.alignTo(this.wrap, this.listAlign);
3561 this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
3563 this.mon(Ext.getDoc(), {
3565 mousewheel: this.collapseIf,
3566 mousedown: this.collapseIf
3568 this.fireEvent('expand', this);
3572 * @method onTriggerClick
3576 // Implements the default empty TriggerField.onTriggerClick function
3577 onTriggerClick : function(){
3578 if(this.readOnly || this.disabled){
3581 if(this.isExpanded()){
3586 if(this.triggerAction == 'all') {
3587 this.doQuery(this.allQuery, true);
3589 this.doQuery(this.getRawValue());
3600 * @cfg {Boolean} grow @hide
3603 * @cfg {Number} growMin @hide
3606 * @cfg {Number} growMax @hide
3610 Ext.reg('combo', Ext.form.ComboBox);
3612 * @class Ext.form.Checkbox
3613 * @extends Ext.form.Field
3614 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
3616 * Creates a new Checkbox
3617 * @param {Object} config Configuration options
3620 Ext.form.Checkbox = Ext.extend(Ext.form.Field, {
3622 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
3624 focusClass : undefined,
3626 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to 'x-form-field')
3628 fieldClass : 'x-form-field',
3630 * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
3634 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
3635 * {tag: 'input', type: 'checkbox', autocomplete: 'off'})
3637 defaultAutoCreate : { tag: 'input', type: 'checkbox', autocomplete: 'off'},
3639 * @cfg {String} boxLabel The text that appears beside the checkbox
3642 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
3645 * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of
3646 * handling the check event). The handler is passed the following parameters:
3647 * <div class="mdetail-params"><ul>
3648 * <li><b>checkbox</b> : Ext.form.Checkbox<div class="sub-desc">The Checkbox being toggled.</div></li>
3649 * <li><b>checked</b> : Boolean<div class="sub-desc">The new checked state of the checkbox.</div></li>
3653 * @cfg {Object} scope An object to use as the scope ('this' reference) of the {@link #handler} function
3654 * (defaults to this Checkbox).
3658 actionMode : 'wrap',
3661 initComponent : function(){
3662 Ext.form.Checkbox.superclass.initComponent.call(this);
3666 * Fires when the checkbox is checked or unchecked.
3667 * @param {Ext.form.Checkbox} this This checkbox
3668 * @param {Boolean} checked The new checked value
3675 onResize : function(){
3676 Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
3677 if(!this.boxLabel && !this.fieldLabel){
3678 this.el.alignTo(this.wrap, 'c-c');
3683 initEvents : function(){
3684 Ext.form.Checkbox.superclass.initEvents.call(this);
3687 click: this.onClick,
3688 change: this.onClick
3694 * Overridden and disabled. The editor element does not support standard valid/invalid marking.
3697 markInvalid : Ext.emptyFn,
3700 * Overridden and disabled. The editor element does not support standard valid/invalid marking.
3703 clearInvalid : Ext.emptyFn,
3706 onRender : function(ct, position){
3707 Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
3708 if(this.inputValue !== undefined){
3709 this.el.dom.value = this.inputValue;
3711 this.wrap = this.el.wrap({cls: 'x-form-check-wrap'});
3713 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
3716 this.setValue(true);
3718 this.checked = this.el.dom.checked;
3720 // Need to repaint for IE, otherwise positioning is broken
3722 this.wrap.repaint();
3724 this.resizeEl = this.positionEl = this.wrap;
3728 onDestroy : function(){
3729 Ext.destroy(this.wrap);
3730 Ext.form.Checkbox.superclass.onDestroy.call(this);
3734 initValue : function() {
3735 this.originalValue = this.getValue();
3739 * Returns the checked state of the checkbox.
3740 * @return {Boolean} True if checked, else false
3742 getValue : function(){
3744 return this.el.dom.checked;
3746 return this.checked;
3750 onClick : function(){
3751 if(this.el.dom.checked != this.checked){
3752 this.setValue(this.el.dom.checked);
3757 * Sets the checked state of the checkbox, fires the 'check' event, and calls a
3758 * <code>{@link #handler}</code> (if configured).
3759 * @param {Boolean/String} checked The following values will check the checkbox:
3760 * <code>true, 'true', '1', or 'on'</code>. Any other value will uncheck the checkbox.
3761 * @return {Ext.form.Field} this
3763 setValue : function(v){
3764 var checked = this.checked ;
3765 this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
3767 this.el.dom.checked = this.checked;
3768 this.el.dom.defaultChecked = this.checked;
3770 if(checked != this.checked){
3771 this.fireEvent('check', this, this.checked);
3773 this.handler.call(this.scope || this, this, this.checked);
3779 Ext.reg('checkbox', Ext.form.Checkbox);
3781 * @class Ext.form.CheckboxGroup
3782 * @extends Ext.form.Field
3783 * <p>A grouping container for {@link Ext.form.Checkbox} controls.</p>
3784 * <p>Sample usage:</p>
3786 var myCheckboxGroup = new Ext.form.CheckboxGroup({
3788 xtype: 'checkboxgroup',
3789 fieldLabel: 'Single Column',
3790 itemCls: 'x-check-group-alt',
3791 // Put all controls in a single column with width 100%
3794 {boxLabel: 'Item 1', name: 'cb-col-1'},
3795 {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
3796 {boxLabel: 'Item 3', name: 'cb-col-3'}
3801 * Creates a new CheckboxGroup
3802 * @param {Object} config Configuration options
3803 * @xtype checkboxgroup
3805 Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {
3807 * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects
3808 * to arrange in the group.
3811 * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
3812 * checkbox/radio controls using automatic layout. This config can take several types of values:
3813 * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width
3814 * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>
3815 * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be
3816 * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>
3817 * <li><b>Array</b> : Object<p class="sub-desc">You can also specify an array of column widths, mixing integer
3818 * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will
3819 * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float
3820 * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field
3821 * container you should do so.</p></li></ul>
3825 * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column
3826 * top to bottom before starting on the next column. The number of controls in each column will be automatically
3827 * calculated to keep columns as even as possible. The default value is false, so that controls will be added
3828 * to columns one at a time, completely filling each row left to right before starting on the next row.
3832 * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).
3833 * If no items are selected at validation time, {@link @blankText} will be used as the error text.
3837 * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must
3838 * select at least one item in this group")
3840 blankText : "You must select at least one item in this group",
3843 defaultType : 'checkbox',
3846 groupCls : 'x-form-check-group',
3849 initComponent: function(){
3853 * Fires when the state of a child checkbox changes.
3854 * @param {Ext.form.CheckboxGroup} this
3855 * @param {Array} checked An array containing the checked boxes.
3859 this.on('change', this.validate, this);
3860 Ext.form.CheckboxGroup.superclass.initComponent.call(this);
3864 onRender : function(ct, position){
3873 bufferResize: false // Default this to false, since it doesn't really have a proper ownerCt.
3877 defaultType: this.defaultType,
3885 if(this.items[0].items){
3887 // The container has standard ColumnLayout configs, so pass them in directly
3889 Ext.apply(panelCfg, {
3890 layoutConfig: {columns: this.items.length},
3891 defaults: this.defaults,
3894 for(var i=0, len=this.items.length; i<len; i++){
3895 Ext.applyIf(this.items[i], colCfg);
3900 // The container has field item configs, so we have to generate the column
3901 // panels first then move the items into the columns as needed.
3903 var numCols, cols = [];
3905 if(typeof this.columns == 'string'){ // 'auto' so create a col per item
3906 this.columns = this.items.length;
3908 if(!Ext.isArray(this.columns)){
3910 for(var i=0; i<this.columns; i++){
3911 cs.push((100/this.columns)*.01); // distribute by even %
3916 numCols = this.columns.length;
3918 // Generate the column configs with the correct width setting
3919 for(var i=0; i<numCols; i++){
3920 var cc = Ext.apply({items:[]}, colCfg);
3921 cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = this.columns[i];
3923 cc.defaults = Ext.apply(cc.defaults || {}, this.defaults)
3928 // Distribute the original items into the columns
3930 var rows = Math.ceil(this.items.length / numCols), ri = 0;
3931 for(var i=0, len=this.items.length; i<len; i++){
3932 if(i>0 && i%rows==0){
3935 if(this.items[i].fieldLabel){
3936 this.items[i].hideLabel = false;
3938 cols[ri].items.push(this.items[i]);
3941 for(var i=0, len=this.items.length; i<len; i++){
3942 var ci = i % numCols;
3943 if(this.items[i].fieldLabel){
3944 this.items[i].hideLabel = false;
3946 cols[ci].items.push(this.items[i]);
3950 Ext.apply(panelCfg, {
3951 layoutConfig: {columns: numCols},
3956 this.panel = new Ext.Container(panelCfg);
3957 this.panel.ownerCt = this;
3958 this.el = this.panel.getEl();
3960 if(this.forId && this.itemCls){
3961 var l = this.el.up(this.itemCls).child('label', true);
3963 l.setAttribute('htmlFor', this.forId);
3967 var fields = this.panel.findBy(function(c){
3968 return c.isFormField;
3971 this.items = new Ext.util.MixedCollection();
3972 this.items.addAll(fields);
3974 Ext.form.CheckboxGroup.superclass.onRender.call(this, ct, position);
3977 initValue : function(){
3979 this.setValue.apply(this, this.buffered ? this.value : [this.value]);
3980 delete this.buffered;
3985 afterRender : function(){
3986 Ext.form.CheckboxGroup.superclass.afterRender.call(this);
3987 this.eachItem(function(item){
3988 item.on('check', this.fireChecked, this);
3989 item.inGroup = true;
3994 doLayout: function(){
3995 //ugly method required to layout hidden items
3997 this.panel.forceLayout = this.ownerCt.forceLayout;
3998 this.panel.doLayout();
4003 fireChecked: function(){
4005 this.eachItem(function(item){
4010 this.fireEvent('change', this, arr);
4014 validateValue : function(value){
4015 if(!this.allowBlank){
4017 this.eachItem(function(f){
4019 return (blank = false);
4023 this.markInvalid(this.blankText);
4031 isDirty: function(){
4032 //override the behaviour to check sub items.
4033 if (this.disabled || !this.rendered) {
4038 this.eachItem(function(item){
4048 onDisable : function(){
4049 this.eachItem(function(item){
4055 onEnable : function(){
4056 this.eachItem(function(item){
4062 doLayout: function(){
4064 this.panel.forceLayout = this.ownerCt.forceLayout;
4065 this.panel.doLayout();
4070 onResize : function(w, h){
4071 this.panel.setSize(w, h);
4072 this.panel.doLayout();
4075 // inherit docs from Field
4077 this.eachItem(function(c){
4082 // Defer the clearInvalid so if BaseForm's collection is being iterated it will be called AFTER it is complete.
4083 // Important because reset is being called on both the group and the individual items.
4085 this.clearInvalid();
4090 * {@link Ext.form.Checkbox#setValue Set the value(s)} of an item or items
4091 * in the group. Examples illustrating how this method may be called:
4093 // call with name and value
4094 myCheckboxGroup.setValue('cb-col-1', true);
4095 // call with an array of boolean values
4096 myCheckboxGroup.setValue([true, false, false]);
4097 // call with an object literal specifying item:value pairs
4098 myCheckboxGroup.setValue({
4102 // use comma separated string to set items with name to true (checked)
4103 myCheckboxGroup.setValue('cb-col-1,cb-col-3');
4105 * See {@link Ext.form.Checkbox#setValue} for additional information.
4106 * @param {Mixed} id The checkbox to check, or as described by example shown.
4107 * @param {Boolean} value (optional) The value to set the item.
4108 * @return {Ext.form.CheckboxGroup} this
4110 setValue: function(){
4112 this.onSetValue.apply(this, arguments);
4114 this.buffered = true;
4115 this.value = arguments;
4120 onSetValue: function(id, value){
4121 if(arguments.length == 1){
4122 if(Ext.isArray(id)){
4123 // an array of boolean values
4124 Ext.each(id, function(val, idx){
4125 var item = this.items.itemAt(idx);
4130 }else if(Ext.isObject(id)){
4131 // set of name/value pairs
4133 var f = this.getBox(i);
4139 this.setValueForItem(id);
4142 var f = this.getBox(id);
4150 beforeDestroy: function(){
4151 Ext.destroy(this.panel);
4152 Ext.form.CheckboxGroup.superclass.beforeDestroy.call(this);
4156 setValueForItem : function(val){
4157 val = String(val).split(',');
4158 this.eachItem(function(item){
4159 if(val.indexOf(item.inputValue)> -1){
4160 item.setValue(true);
4166 getBox : function(id){
4168 this.eachItem(function(f){
4169 if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
4178 * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
4179 * @return {Array} An array of the selected checkboxes.
4181 getValue : function(){
4183 this.eachItem(function(item){
4192 eachItem: function(fn){
4193 if(this.items && this.items.each){
4194 this.items.each(fn, this);
4199 * @cfg {String} name
4204 * @method getRawValue
4207 getRawValue : Ext.emptyFn,
4210 * @method setRawValue
4213 setRawValue : Ext.emptyFn
4217 Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
4219 * @class Ext.form.Radio
4220 * @extends Ext.form.Checkbox
4221 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
4222 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
4224 * Creates a new Radio
4225 * @param {Object} config Configuration options
4228 Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
4232 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
4235 markInvalid : Ext.emptyFn,
4237 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
4240 clearInvalid : Ext.emptyFn,
4243 * If this radio is part of a group, it will return the selected value
4246 getGroupValue : function(){
4247 var p = this.el.up('form') || Ext.getBody();
4248 var c = p.child('input[name='+this.el.dom.name+']:checked', true);
4249 return c ? c.value : null;
4253 onClick : function(){
4254 if(this.el.dom.checked != this.checked){
4255 var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
4256 els.each(function(el){
4257 if(el.dom.id == this.id){
4258 this.setValue(true);
4260 Ext.getCmp(el.dom.id).setValue(false);
4267 * Sets either the checked/unchecked status of this Radio, or, if a string value
4268 * is passed, checks a sibling Radio of the same name whose value is the value specified.
4269 * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
4270 * @return {Ext.form.Field} this
4272 setValue : function(v){
4273 if (typeof v == 'boolean') {
4274 Ext.form.Radio.superclass.setValue.call(this, v);
4276 var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
4278 Ext.getCmp(r.id).setValue(true);
4285 getCheckEl: function(){
4287 return this.el.up('.x-form-radio-group')
4289 return this.el.up('form') || Ext.getBody();
4292 Ext.reg('radio', Ext.form.Radio);
4294 * @class Ext.form.RadioGroup
4295 * @extends Ext.form.CheckboxGroup
4296 * A grouping container for {@link Ext.form.Radio} controls.
4298 * Creates a new RadioGroup
4299 * @param {Object} config Configuration options
4302 Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {
4304 * @cfg {Array} items An Array of {@link Ext.form.Radio Radio}s or Radio config objects
4305 * to arrange in the group.
4308 * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
4309 * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
4310 * be used as the error text.
4314 * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
4315 * (defaults to 'You must select one item in this group')
4317 blankText : 'You must select one item in this group',
4320 defaultType : 'radio',
4323 groupCls : 'x-form-radio-group',
4327 * Fires when the state of a child radio changes.
4328 * @param {Ext.form.RadioGroup} this
4329 * @param {Ext.form.Radio} checked The checked radio
4333 * Gets the selected {@link Ext.form.Radio} in the group, if it exists.
4334 * @return {Ext.form.Radio} The selected radio.
4336 getValue : function(){
4338 this.eachItem(function(item){
4348 * Sets the checked radio in the group.
4349 * @param {String/Ext.form.Radio} id The radio to check.
4350 * @param {Boolean} value The value to set the radio.
4351 * @return {Ext.form.RadioGroup} this
4353 onSetValue : function(id, value){
4354 if(arguments.length > 1){
4355 var f = this.getBox(id);
4359 this.eachItem(function(item){
4361 item.setValue(false);
4367 this.setValueForItem(id);
4371 setValueForItem : function(val){
4372 val = String(val).split(',')[0];
4373 this.eachItem(function(item){
4374 item.setValue(val == item.inputValue);
4379 fireChecked : function(){
4380 if(!this.checkTask){
4381 this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);
4383 this.checkTask.delay(10);
4387 bufferChecked : function(){
4389 this.eachItem(function(item){
4395 this.fireEvent('change', this, out);
4398 onDestroy : function(){
4400 this.checkTask.cancel();
4401 this.checkTask = null;
4403 Ext.form.RadioGroup.superclass.onDestroy.call(this);
4408 Ext.reg('radiogroup', Ext.form.RadioGroup);
4410 * @class Ext.form.Hidden
\r
4411 * @extends Ext.form.Field
\r
4412 * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.
\r
4414 * Create a new Hidden field.
\r
4415 * @param {Object} config Configuration options
\r
4418 Ext.form.Hidden = Ext.extend(Ext.form.Field, {
\r
4420 inputType : 'hidden',
\r
4423 onRender : function(){
\r
4424 Ext.form.Hidden.superclass.onRender.apply(this, arguments);
\r
4428 initEvents : function(){
\r
4429 this.originalValue = this.getValue();
\r
4432 // These are all private overrides
\r
4433 setSize : Ext.emptyFn,
\r
4434 setWidth : Ext.emptyFn,
\r
4435 setHeight : Ext.emptyFn,
\r
4436 setPosition : Ext.emptyFn,
\r
4437 setPagePosition : Ext.emptyFn,
\r
4438 markInvalid : Ext.emptyFn,
\r
4439 clearInvalid : Ext.emptyFn
\r
4441 Ext.reg('hidden', Ext.form.Hidden);/**
4442 * @class Ext.form.BasicForm
4443 * @extends Ext.util.Observable
4444 * <p>Encapsulates the DOM <form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides
4445 * input field management, validation, submission, and form loading services.</p>
4446 * <p>By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}.
4447 * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.</p>
4448 * <p><b><u>File Uploads</u></b></p>
4449 * <p>{@link #fileUpload File uploads} are not performed using Ajax submission, that
4450 * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
4451 * manner with the DOM <tt><form></tt> element temporarily modified to have its
4452 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
4453 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
4454 * but removed after the return data has been gathered.</p>
4455 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
4456 * server is using JSON to send the return object, then the
4457 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
4458 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
4459 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
4460 * "<" as "&lt;", "&" as "&amp;" etc.</p>
4461 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
4462 * is created containing a <tt>responseText</tt> property in order to conform to the
4463 * requirements of event handlers and callbacks.</p>
4464 * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
4465 * and some server technologies (notably JEE) may require some custom processing in order to
4466 * retrieve parameter names and parameter values from the packet content.</p>
4468 * @param {Mixed} el The form element or its id
4469 * @param {Object} config Configuration options
4471 Ext.form.BasicForm = function(el, config){
4472 Ext.apply(this, config);
4473 if(Ext.isString(this.paramOrder)){
4474 this.paramOrder = this.paramOrder.split(/[\s,|]/);
4477 * A {@link Ext.util.MixedCollection MixedCollection} containing all the Ext.form.Fields in this form.
4478 * @type MixedCollection
4481 this.items = new Ext.util.MixedCollection(false, function(o){
4482 return o.getItemId();
4486 * @event beforeaction
4487 * Fires before any action is performed. Return false to cancel the action.
4488 * @param {Form} this
4489 * @param {Action} action The {@link Ext.form.Action} to be performed
4493 * @event actionfailed
4494 * Fires when an action fails.
4495 * @param {Form} this
4496 * @param {Action} action The {@link Ext.form.Action} that failed
4500 * @event actioncomplete
4501 * Fires when an action is completed.
4502 * @param {Form} this
4503 * @param {Action} action The {@link Ext.form.Action} that completed
4511 Ext.form.BasicForm.superclass.constructor.call(this);
4514 Ext.extend(Ext.form.BasicForm, Ext.util.Observable, {
4516 * @cfg {String} method
4517 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4520 * @cfg {DataReader} reader
4521 * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read
4522 * data when executing 'load' actions. This is optional as there is built-in
4523 * support for processing JSON. For additional information on using an XMLReader
4524 * see the example provided in examples/form/xml-form.html.
4527 * @cfg {DataReader} errorReader
4528 * <p>An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to
4529 * read field error messages returned from 'submit' actions. This is optional
4530 * as there is built-in support for processing JSON.</p>
4531 * <p>The Records which provide messages for the invalid Fields must use the
4532 * Field name (or id) as the Record ID, and must contain a field called 'msg'
4533 * which contains the error message.</p>
4534 * <p>The errorReader does not have to be a full-blown implementation of a
4535 * DataReader. It simply needs to implement a <tt>read(xhr)</tt> function
4536 * which returns an Array of Records in an object with the following
4537 * structure:</p><pre><code>
4539 records: recordArray
4545 * The URL to use for form actions if one isn't supplied in the
4546 * <code>{@link #doAction doAction} options</code>.
4549 * @cfg {Boolean} fileUpload
4550 * Set to true if this form is a file upload.
4551 * <p>File uploads are not performed using normal 'Ajax' techniques, that is they are <b>not</b>
4552 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
4553 * DOM <tt><form></tt> element temporarily modified to have its
4554 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
4555 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
4556 * but removed after the return data has been gathered.</p>
4557 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
4558 * server is using JSON to send the return object, then the
4559 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
4560 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
4561 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
4562 * "<" as "&lt;", "&" as "&amp;" etc.</p>
4563 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
4564 * is created containing a <tt>responseText</tt> property in order to conform to the
4565 * requirements of event handlers and callbacks.</p>
4566 * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
4567 * and some server technologies (notably JEE) may require some custom processing in order to
4568 * retrieve parameter names and parameter values from the packet content.</p>
4571 * @cfg {Object} baseParams
4572 * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
4573 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
4576 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4581 * @cfg {Object} api (Optional) If specified load and submit actions will be handled
4582 * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}.
4583 * Methods which have been imported by Ext.Direct can be specified here to load and submit
4585 * Such as the following:<pre><code>
4587 load: App.ss.MyProfile.load,
4588 submit: App.ss.MyProfile.submit
4591 * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
4592 * to customize how the load method is invoked.
4593 * Submit actions will always use a standard form submit. The formHandler configuration must
4594 * be set on the associated server-side method which has been imported by Ext.Direct</p>
4598 * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
4599 * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
4600 * <code>load</code> configuration.</p>
4601 * <br><p>Specify the params in the order in which they must be executed on the
4602 * server-side as either (1) an Array of String values, or (2) a String of params
4603 * delimited by either whitespace, comma, or pipe. For example,
4604 * any of the following would be acceptable:</p><pre><code>
4605 paramOrder: ['param1','param2','param3']
4606 paramOrder: 'param1 param2 param3'
4607 paramOrder: 'param1,param2,param3'
4608 paramOrder: 'param1|param2|param'
4611 paramOrder: undefined,
4614 * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
4615 * <code>load</code> configuration. Send parameters as a collection of named
4616 * arguments (defaults to <tt>false</tt>). Providing a
4617 * <tt>{@link #paramOrder}</tt> nullifies this configuration.
4619 paramsAsHash: false,
4622 * @cfg {String} waitTitle
4623 * The default title to show for the waiting message box (defaults to <tt>'Please Wait...'</tt>)
4625 waitTitle: 'Please Wait...',
4628 activeAction : null,
4631 * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
4632 * or {@link #setValues}() data instead of when the form was first created. Defaults to <tt>false</tt>.
4634 trackResetOnLoad : false,
4637 * @cfg {Boolean} standardSubmit
4638 * <p>If set to <tt>true</tt>, standard HTML form submits are used instead
4639 * of XHR (Ajax) style form submissions. Defaults to <tt>false</tt>.</p>
4640 * <br><p><b>Note:</b> When using <code>standardSubmit</code>, the
4641 * <code>options</code> to <code>{@link #submit}</code> are ignored because
4642 * Ext's Ajax infrastracture is bypassed. To pass extra parameters (e.g.
4643 * <code>baseParams</code> and <code>params</code>), utilize hidden fields
4644 * to submit extra data, for example:</p>
4647 standardSubmit: true,
4651 {@link url}: 'myProcess.php',
4658 handler: function(){
4659 var fp = this.ownerCt.ownerCt,
4660 form = fp.getForm();
4661 if (form.isValid()) {
4662 // check if there are baseParams and if
4663 // hiddent items have been added already
4664 if (fp.baseParams && !fp.paramsAdded) {
4665 // add hidden items for all baseParams
4666 for (i in fp.baseParams) {
4670 value: fp.baseParams[i]
4674 // set a custom flag to prevent re-adding
4675 fp.paramsAdded = true;
4677 form.{@link #submit}();
4685 * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
4686 * element by passing it or its id or mask the form itself by passing in true.
4688 * @property waitMsgTarget
4692 initEl : function(el){
4693 this.el = Ext.get(el);
4694 this.id = this.el.id || Ext.id();
4695 if(!this.standardSubmit){
4696 this.el.on('submit', this.onSubmit, this);
4698 this.el.addClass('x-form');
4702 * Get the HTML form Element
4703 * @return Ext.Element
4710 onSubmit : function(e){
4715 destroy: function() {
4716 this.items.each(function(f){
4720 this.el.removeAllListeners();
4723 this.purgeListeners();
4727 * Returns true if client-side validation on the form is successful.
4730 isValid : function(){
4732 this.items.each(function(f){
4741 * <p>Returns true if any fields in this form have changed from their original values.</p>
4742 * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
4743 * Fields' <i>original values</i> are updated when the values are loaded by {@link #setValues}
4744 * or {@link #loadRecord}.</p>
4747 isDirty : function(){
4749 this.items.each(function(f){
4759 * Performs a predefined action ({@link Ext.form.Action.Submit} or
4760 * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action}
4761 * to perform application-specific processing.
4762 * @param {String/Object} actionName The name of the predefined action type,
4763 * or instance of {@link Ext.form.Action} to perform.
4764 * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}.
4765 * All of the config options listed below are supported by both the
4766 * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
4767 * actions unless otherwise noted (custom actions could also accept
4768 * other config options):<ul>
4770 * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
4771 * to the form's {@link #url}.)</div></li>
4773 * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
4774 * to the form's method, or POST if not defined)</div></li>
4776 * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
4777 * (defaults to the form's baseParams, or none if not defined)</p>
4778 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
4780 * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action
4781 * (defaults to the form's default headers)</div></li>
4783 * <li><b>success</b> : Function<div class="sub-desc">The callback that will
4784 * be invoked after a successful response (see top of
4785 * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
4786 * for a description of what constitutes a successful response).
4787 * The function is passed the following parameters:<ul>
4788 * <li><tt>form</tt> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
4789 * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
4790 * <div class="sub-desc">The action object contains these properties of interest:<ul>
4791 * <li><tt>{@link Ext.form.Action#response response}</tt></li>
4792 * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
4793 * <li><tt>{@link Ext.form.Action#type type}</tt></li>
4794 * </ul></div></li></ul></div></li>
4796 * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
4797 * failed transaction attempt. The function is passed the following parameters:<ul>
4798 * <li><tt>form</tt> : The {@link Ext.form.BasicForm} that requested the action.</li>
4799 * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
4800 * <div class="sub-desc">The action object contains these properties of interest:<ul>
4801 * <li><tt>{@link Ext.form.Action#failureType failureType}</tt></li>
4802 * <li><tt>{@link Ext.form.Action#response response}</tt></li>
4803 * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
4804 * <li><tt>{@link Ext.form.Action#type type}</tt></li>
4805 * </ul></div></li></ul></div></li>
4807 * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
4808 * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
4810 * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
4811 * Determines whether a Form's fields are validated in a final call to
4812 * {@link Ext.form.BasicForm#isValid isValid} prior to submission. Set to <tt>false</tt>
4813 * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
4815 * @return {BasicForm} this
4817 doAction : function(action, options){
4818 if(Ext.isString(action)){
4819 action = new Ext.form.Action.ACTION_TYPES[action](this, options);
4821 if(this.fireEvent('beforeaction', this, action) !== false){
4822 this.beforeAction(action);
4823 action.run.defer(100, action);
4829 * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Submit submit action}.
4830 * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
4831 * <p><b>Note:</b> this is ignored when using the {@link #standardSubmit} option.</p>
4832 * <p>The following code:</p><pre><code>
4833 myFormPanel.getForm().submit({
4834 clientValidation: true,
4835 url: 'updateConsignment.php',
4837 newStatus: 'delivered'
4839 success: function(form, action) {
4840 Ext.Msg.alert('Success', action.result.msg);
4842 failure: function(form, action) {
4843 switch (action.failureType) {
4844 case Ext.form.Action.CLIENT_INVALID:
4845 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
4847 case Ext.form.Action.CONNECT_FAILURE:
4848 Ext.Msg.alert('Failure', 'Ajax communication failed');
4850 case Ext.form.Action.SERVER_INVALID:
4851 Ext.Msg.alert('Failure', action.result.msg);
4856 * would process the following server response for a successful submission:<pre><code>
4858 "success":true, // note this is Boolean, not string
4859 "msg":"Consignment updated"
4862 * and the following server response for a failed submission:<pre><code>
4864 "success":false, // note this is Boolean, not string
4865 "msg":"You do not have permission to perform this operation"
4868 * @return {BasicForm} this
4870 submit : function(options){
4871 if(this.standardSubmit){
4872 var v = this.isValid();
4874 var el = this.el.dom;
4875 if(this.url && Ext.isEmpty(el.action)){
4876 el.action = this.url;
4882 var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
4883 this.doAction(submitAction, options);
4888 * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
4889 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
4890 * @return {BasicForm} this
4892 load : function(options){
4893 var loadAction = String.format('{0}load', this.api ? 'direct' : '');
4894 this.doAction(loadAction, options);
4899 * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
4900 * @param {Record} record The record to edit
4901 * @return {BasicForm} this
4903 updateRecord : function(record){
4905 var fs = record.fields;
4906 fs.each(function(f){
4907 var field = this.findField(f.name);
4909 record.set(f.name, field.getValue());
4917 * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
4918 * {@link Ext.data.Record#data record data}.
4919 * See also {@link #trackResetOnLoad}.
4920 * @param {Record} record The record to load
4921 * @return {BasicForm} this
4923 loadRecord : function(record){
4924 this.setValues(record.data);
4929 beforeAction : function(action){
4930 var o = action.options;
4932 if(this.waitMsgTarget === true){
4933 this.el.mask(o.waitMsg, 'x-mask-loading');
4934 }else if(this.waitMsgTarget){
4935 this.waitMsgTarget = Ext.get(this.waitMsgTarget);
4936 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
4938 Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
4944 afterAction : function(action, success){
4945 this.activeAction = null;
4946 var o = action.options;
4948 if(this.waitMsgTarget === true){
4950 }else if(this.waitMsgTarget){
4951 this.waitMsgTarget.unmask();
4953 Ext.MessageBox.updateProgress(1);
4954 Ext.MessageBox.hide();
4961 Ext.callback(o.success, o.scope, [this, action]);
4962 this.fireEvent('actioncomplete', this, action);
4964 Ext.callback(o.failure, o.scope, [this, action]);
4965 this.fireEvent('actionfailed', this, action);
4970 * Find a {@link Ext.form.Field} in this form.
4971 * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
4972 * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
4975 findField : function(id){
4976 var field = this.items.get(id);
4977 if(!Ext.isObject(field)){
4978 this.items.each(function(f){
4979 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4985 return field || null;
4990 * Mark fields in this form invalid in bulk.
4991 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4992 * @return {BasicForm} this
4994 markInvalid : function(errors){
4995 if(Ext.isArray(errors)){
4996 for(var i = 0, len = errors.length; i < len; i++){
4997 var fieldError = errors[i];
4998 var f = this.findField(fieldError.id);
5000 f.markInvalid(fieldError.msg);
5006 if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
5007 field.markInvalid(errors[id]);
5015 * Set values for fields in this form in bulk.
5016 * @param {Array/Object} values Either an array in the form:<pre><code>
5017 [{id:'clientName', value:'Fred. Olsen Lines'},
5018 {id:'portOfLoading', value:'FXT'},
5019 {id:'portOfDischarge', value:'OSL'} ]</code></pre>
5020 * or an object hash of the form:<pre><code>
5022 clientName: 'Fred. Olsen Lines',
5023 portOfLoading: 'FXT',
5024 portOfDischarge: 'OSL'
5026 * @return {BasicForm} this
5028 setValues : function(values){
5029 if(Ext.isArray(values)){ // array of objects
5030 for(var i = 0, len = values.length; i < len; i++){
5032 var f = this.findField(v.id);
5034 f.setValue(v.value);
5035 if(this.trackResetOnLoad){
5036 f.originalValue = f.getValue();
5040 }else{ // object hash
5043 if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
5044 field.setValue(values[id]);
5045 if(this.trackResetOnLoad){
5046 field.originalValue = field.getValue();
5055 * <p>Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit.
5056 * If multiple fields exist with the same name they are returned as an array.</p>
5057 * <p><b>Note:</b> The values are collected from all enabled HTML input elements within the form, <u>not</u> from
5058 * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the
5059 * value can potentially be the emptyText of a field.</p>
5060 * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object)
5061 * @return {String/Object}
5063 getValues : function(asString){
5064 var fs = Ext.lib.Ajax.serializeForm(this.el.dom);
5065 if(asString === true){
5068 return Ext.urlDecode(fs);
5072 * Retrieves the fields in the form as a set of key/value pairs, using the {@link Ext.form.Field#getValue getValue()} method.
5073 * If multiple fields exist with the same name they are returned as an array.
5074 * @param {Boolean} dirtyOnly (optional) True to return only fields that are dirty.
5075 * @return {Object} The values in the form
5077 getFieldValues : function(dirtyOnly){
5082 this.items.each(function(f){
5083 if(dirtyOnly !== true || f.isDirty()){
5088 if(Ext.isDefined(key)){
5089 if(Ext.isArray(key)){
5103 * Clears all invalid messages in this form.
5104 * @return {BasicForm} this
5106 clearInvalid : function(){
5107 this.items.each(function(f){
5115 * @return {BasicForm} this
5118 this.items.each(function(f){
5125 * Add Ext.form Components to this form's Collection. This does not result in rendering of
5126 * the passed Component, it just enables the form to validate Fields, and distribute values to
5128 * <p><b>You will not usually call this function. In order to be rendered, a Field must be added
5129 * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}.
5130 * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's
5131 * collection.</b></p>
5132 * @param {Field} field1
5133 * @param {Field} field2 (optional)
5134 * @param {Field} etc (optional)
5135 * @return {BasicForm} this
5138 this.items.addAll(Array.prototype.slice.call(arguments, 0));
5144 * Removes a field from the items collection (does NOT remove its markup).
5145 * @param {Field} field
5146 * @return {BasicForm} this
5148 remove : function(field){
5149 this.items.remove(field);
5154 * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm,
5155 * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id.
5156 * @return {BasicForm} this
5158 render : function(){
5159 this.items.each(function(f){
5160 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
5161 f.applyToMarkup(f.id);
5168 * Calls {@link Ext#apply} for all fields in this form with the passed object.
5169 * @param {Object} values
5170 * @return {BasicForm} this
5172 applyToFields : function(o){
5173 this.items.each(function(f){
5180 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
5181 * @param {Object} values
5182 * @return {BasicForm} this
5184 applyIfToFields : function(o){
5185 this.items.each(function(f){
5191 callFieldMethod : function(fnName, args){
5193 this.items.each(function(f){
5194 if(Ext.isFunction(f[fnName])){
5195 f[fnName].apply(f, args);
5203 Ext.BasicForm = Ext.form.BasicForm;/**
5204 * @class Ext.form.FormPanel
5205 * @extends Ext.Panel
5206 * <p>Standard form container.</p>
5208 * <p><b><u>Layout</u></b></p>
5209 * <p>By default, FormPanel is configured with <tt>layout:'form'</tt> to use an {@link Ext.layout.FormLayout}
5210 * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers
5211 * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the
5212 * {@link Ext.layout.FormLayout} layout manager.</p>
5214 * <p><b><u>BasicForm</u></b></p>
5215 * <p>Although <b>not listed</b> as configuration options of FormPanel, the FormPanel class accepts all
5216 * of the config options required to configure its internal {@link Ext.form.BasicForm} for:
5217 * <div class="mdetail-params"><ul>
5218 * <li>{@link Ext.form.BasicForm#fileUpload file uploads}</li>
5219 * <li>functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form</li>
5222 * <p><b>Note</b>: If subclassing FormPanel, any configuration options for the BasicForm must be applied to
5223 * the <tt><b>initialConfig</b></tt> property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm}
5224 * configuration settings to <b><tt>this</tt></b> will <b>not</b> affect the BasicForm's configuration.</p>
5226 * <p><b><u>Form Validation</u></b></p>
5227 * <p>For information on form validation see the following:</p>
5228 * <div class="mdetail-params"><ul>
5229 * <li>{@link Ext.form.TextField}</li>
5230 * <li>{@link Ext.form.VTypes}</li>
5231 * <li>{@link Ext.form.BasicForm#doAction BasicForm.doAction <b>clientValidation</b> notes}</li>
5232 * <li><tt>{@link Ext.form.FormPanel#monitorValid monitorValid}</tt></li>
5235 * <p><b><u>Form Submission</u></b></p>
5236 * <p>By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser
5237 * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the
5238 * <tt><b>{@link Ext.form.BasicForm#standardSubmit standardSubmit}</b></tt> option.</p>
5241 * @param {Object} config Configuration options
5244 Ext.FormPanel = Ext.extend(Ext.Panel, {
5246 * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).
5249 * @cfg {Boolean} hideLabels
5250 * <p><tt>true</tt> to hide field labels by default (sets <tt>display:none</tt>). Defaults to
5251 * <tt>false</tt>.</p>
5252 * <p>Also see {@link Ext.Component}.<tt>{@link Ext.Component#hideLabel hideLabel}</tt>.
5255 * @cfg {Number} labelPad
5256 * The default padding in pixels for field labels (defaults to <tt>5</tt>). <tt>labelPad</tt> only
5257 * applies if <tt>{@link #labelWidth}</tt> is also specified, otherwise it will be ignored.
5260 * @cfg {String} labelSeparator
5261 * See {@link Ext.Component}.<tt>{@link Ext.Component#labelSeparator labelSeparator}</tt>
5264 * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
5265 * and can be overridden on any child container (e.g., a fieldset can specify a different <tt>labelWidth</tt>
5266 * for its fields) (defaults to <tt>100</tt>).
5269 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
5272 * @cfg {Array} buttons
5273 * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.<br>
5274 * <p>Buttons in the footer of a FormPanel may be configured with the option <tt>formBind: true</tt>. This causes
5275 * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on
5276 * the form's valid/invalid state.</p>
5281 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to <tt>75</tt>).
5283 minButtonWidth : 75,
5286 * @cfg {String} labelAlign The label alignment value used for the <tt>text-align</tt> specification
5287 * for the <b>container</b>. Valid values are <tt>"left</tt>", <tt>"top"</tt> or <tt>"right"</tt>
5288 * (defaults to <tt>"left"</tt>). This property cascades to child <b>containers</b> and can be
5289 * overridden on any child <b>container</b> (e.g., a fieldset can specify a different <tt>labelAlign</tt>
5292 labelAlign : 'left',
5295 * @cfg {Boolean} monitorValid If <tt>true</tt>, the form monitors its valid state <b>client-side</b> and
5296 * regularly fires the {@link #clientvalidation} event passing that state.<br>
5297 * <p>When monitoring valid state, the FormPanel enables/disables any of its configured
5298 * {@link #buttons} which have been configured with <code>formBind: true</code> depending
5299 * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to <tt>false</tt></p>
5301 monitorValid : false,
5304 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
5309 * @cfg {String} layout Defaults to <tt>'form'</tt>. Normally this configuration property should not be altered.
5310 * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}.
5315 initComponent : function(){
5316 this.form = this.createForm();
5317 Ext.FormPanel.superclass.initComponent.call(this);
5321 cls: this.baseCls + '-body',
5322 method : this.method || 'POST',
5323 id : this.formId || Ext.id()
5325 if(this.fileUpload) {
5326 this.bodyCfg.enctype = 'multipart/form-data';
5332 * @event clientvalidation
5333 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5334 * @param {Ext.form.FormPanel} this
5335 * @param {Boolean} valid true if the form has passed client-side validation
5340 this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']);
5344 createForm : function(){
5345 var config = Ext.applyIf({listeners: {}}, this.initialConfig);
5346 return new Ext.form.BasicForm(null, config);
5350 initFields : function(){
5352 var formPanel = this;
5353 var fn = function(c){
5354 if(formPanel.isField(c)){
5356 }else if(c.findBy && c != formPanel){
5357 formPanel.applySettings(c);
5358 //each check required for check/radio groups.
5359 if(c.items && c.items.each){
5360 c.items.each(fn, this);
5364 this.items.each(fn, this);
5368 applySettings: function(c){
5371 labelAlign: ct.labelAlign,
5372 labelWidth: ct.labelWidth,
5378 getLayoutTarget : function(){
5379 return this.form.el;
5383 * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains.
5384 * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains.
5386 getForm : function(){
5391 onRender : function(ct, position){
5393 Ext.FormPanel.superclass.onRender.call(this, ct, position);
5394 this.form.initEl(this.body);
5398 beforeDestroy : function(){
5399 this.stopMonitoring();
5401 * Don't move this behaviour to BasicForm because it can be used
5404 Ext.destroy(this.form);
5405 this.form.items.clear();
5406 Ext.FormPanel.superclass.beforeDestroy.call(this);
5409 // Determine if a Component is usable as a form Field.
5410 isField : function(c) {
5411 return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
5415 initEvents : function(){
5416 Ext.FormPanel.superclass.initEvents.call(this);
5417 // Listeners are required here to catch bubbling events from children.
5420 add: this.onAddEvent,
5421 remove: this.onRemoveEvent
5423 if(this.monitorValid){ // initialize after render
5424 this.startMonitoring();
5430 Ext.FormPanel.superclass.onAdd.call(this, c);
5435 onAddEvent: function(ct, c){
5442 processAdd : function(c){
5443 // If a single form Field, add it
5444 if(this.isField(c)){
5446 // If a Container, add any Fields it might contain
5448 this.applySettings(c);
5449 this.form.add.apply(this.form, c.findBy(this.isField));
5454 onRemove: function(c){
5455 Ext.FormPanel.superclass.onRemove.call(this, c);
5456 this.processRemove(c);
5459 onRemoveEvent: function(ct, c){
5461 this.processRemove(c);
5466 processRemove : function(c){
5467 // If a single form Field, remove it
5468 if(this.isField(c)){
5469 this.form.remove(c);
5470 // If a Container, its already destroyed by the time it gets here. Remove any references to destroyed fields.
5472 var isDestroyed = function(o) {
5473 return !!o.isDestroyed;
5475 this.form.items.filterBy(isDestroyed, this.form).each(this.form.remove, this.form);
5480 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
5481 * option "monitorValid"
5483 startMonitoring : function(){
5484 if(!this.validTask){
5485 this.validTask = new Ext.util.TaskRunner();
5486 this.validTask.start({
5487 run : this.bindHandler,
5488 interval : this.monitorPoll || 200,
5495 * Stops monitoring of the valid state of this form
5497 stopMonitoring : function(){
5499 this.validTask.stopAll();
5500 this.validTask = null;
5505 * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.
5506 * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)
5509 this.form.load.apply(this.form, arguments);
5513 onDisable : function(){
5514 Ext.FormPanel.superclass.onDisable.call(this);
5516 this.form.items.each(function(){
5523 onEnable : function(){
5524 Ext.FormPanel.superclass.onEnable.call(this);
5526 this.form.items.each(function(){
5533 bindHandler : function(){
5535 this.form.items.each(function(f){
5536 if(!f.isValid(true)){
5542 var fitems = this.fbar.items.items;
5543 for(var i = 0, len = fitems.length; i < len; i++){
5544 var btn = fitems[i];
5545 if(btn.formBind === true && btn.disabled === valid){
5546 btn.setDisabled(!valid);
5550 this.fireEvent('clientvalidation', this, valid);
5553 Ext.reg('form', Ext.FormPanel);
5555 Ext.form.FormPanel = Ext.FormPanel;
5557 * @class Ext.form.FieldSet
\r
5558 * @extends Ext.Panel
\r
5559 * Standard container used for grouping items within a {@link Ext.form.FormPanel form}.
\r
5561 var form = new Ext.FormPanel({
\r
5562 title: 'Simple Form with FieldSets',
\r
5563 labelWidth: 75, // label settings here cascade unless overridden
\r
5564 url: 'save-form.php',
\r
5566 bodyStyle:'padding:5px 5px 0',
\r
5568 renderTo: document.body,
\r
5569 layout:'column', // arrange items in columns
\r
5570 defaults: { // defaults applied to items
\r
5573 bodyStyle: 'padding:4px'
\r
5576 // Fieldset in Column 1
\r
5579 title: 'Fieldset 1',
\r
5580 collapsible: true,
\r
5583 anchor: '-20' // leave room for error icon
\r
5585 defaultType: 'textfield',
\r
5587 fieldLabel: 'Field 1'
\r
5589 fieldLabel: 'Field 2'
\r
5591 fieldLabel: 'Field 3'
\r
5595 // Fieldset in Column 2 - Panel inside
\r
5597 title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header
\r
5600 checkboxToggle: true,
\r
5601 collapsed: true, // fieldset initially collapsed
\r
5606 title: 'Panel inside a fieldset',
\r
5614 * @param {Object} config Configuration options
\r
5617 Ext.form.FieldSet = Ext.extend(Ext.Panel, {
\r
5619 * @cfg {Mixed} checkboxToggle <tt>true</tt> to render a checkbox into the fieldset frame just
\r
5620 * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults
\r
5621 * to <tt>false</tt>).
\r
5622 * <p>A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox.
\r
5623 * If <tt>true</tt> is specified, the default DomHelper config object used to create the element
\r
5624 * is:</p><pre><code>
\r
5625 * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}
\r
5629 * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if <tt>{@link #checkboxToggle} = true</tt>
\r
5630 * (defaults to <tt>'[checkbox id]-checkbox'</tt>).
\r
5633 * @cfg {Boolean} collapsible
\r
5634 * <tt>true</tt> to make the fieldset collapsible and have the expand/collapse toggle button automatically
\r
5635 * rendered into the legend element, <tt>false</tt> to keep the fieldset statically sized with no collapse
\r
5636 * button (defaults to <tt>false</tt>). Another option is to configure <tt>{@link #checkboxToggle}</tt>.
\r
5639 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
\r
5642 * @cfg {String} itemCls A css class to apply to the <tt>x-form-item</tt> of fields (see
\r
5643 * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).
\r
5644 * This property cascades to child containers.
\r
5647 * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to <tt>'x-fieldset'</tt>).
\r
5649 baseCls : 'x-fieldset',
\r
5651 * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to <tt>'form'</tt>).
\r
5655 * @cfg {Boolean} animCollapse
\r
5656 * <tt>true</tt> to animate the transition when the panel is collapsed, <tt>false</tt> to skip the
\r
5657 * animation (defaults to <tt>false</tt>).
\r
5659 animCollapse : false,
\r
5662 onRender : function(ct, position){
\r
5664 this.el = document.createElement('fieldset');
\r
5665 this.el.id = this.id;
\r
5666 if (this.title || this.header || this.checkboxToggle) {
\r
5667 this.el.appendChild(document.createElement('legend')).className = 'x-fieldset-header';
\r
5671 Ext.form.FieldSet.superclass.onRender.call(this, ct, position);
\r
5673 if(this.checkboxToggle){
\r
5674 var o = typeof this.checkboxToggle == 'object' ?
\r
5675 this.checkboxToggle :
\r
5676 {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};
\r
5677 this.checkbox = this.header.insertFirst(o);
\r
5678 this.checkbox.dom.checked = !this.collapsed;
\r
5679 this.mon(this.checkbox, 'click', this.onCheckClick, this);
\r
5684 onCollapse : function(doAnim, animArg){
\r
5685 if(this.checkbox){
\r
5686 this.checkbox.dom.checked = false;
\r
5688 Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);
\r
5693 onExpand : function(doAnim, animArg){
\r
5694 if(this.checkbox){
\r
5695 this.checkbox.dom.checked = true;
\r
5697 Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);
\r
5701 * This function is called by the fieldset's checkbox when it is toggled (only applies when
\r
5702 * checkboxToggle = true). This method should never be called externally, but can be
\r
5703 * overridden to provide custom behavior when the checkbox is toggled if needed.
\r
5705 onCheckClick : function(){
\r
5706 this[this.checkbox.dom.checked ? 'expand' : 'collapse']();
\r
5710 * @cfg {String/Number} activeItem
\r
5714 * @cfg {Mixed} applyTo
\r
5718 * @cfg {Boolean} bodyBorder
\r
5722 * @cfg {Boolean} border
\r
5726 * @cfg {Boolean/Number} bufferResize
\r
5730 * @cfg {Boolean} collapseFirst
\r
5734 * @cfg {String} defaultType
\r
5738 * @cfg {String} disabledClass
\r
5742 * @cfg {String} elements
\r
5746 * @cfg {Boolean} floating
\r
5750 * @cfg {Boolean} footer
\r
5754 * @cfg {Boolean} frame
\r
5758 * @cfg {Boolean} header
\r
5762 * @cfg {Boolean} headerAsText
\r
5766 * @cfg {Boolean} hideCollapseTool
\r
5770 * @cfg {String} iconCls
\r
5774 * @cfg {Boolean/String} shadow
\r
5778 * @cfg {Number} shadowOffset
\r
5782 * @cfg {Boolean} shim
\r
5786 * @cfg {Object/Array} tbar
\r
5790 * @cfg {Array} tools
\r
5794 * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
\r
5798 * @cfg {String} xtype
\r
5802 * @property header
\r
5806 * @property footer
\r
5814 * @method getBottomToolbar
\r
5818 * @method getTopToolbar
\r
5822 * @method setIconClass
\r
5830 * @event beforeclose
\r
5834 * @event bodyresize
\r
5842 * @event deactivate
\r
5846 Ext.reg('fieldset', Ext.form.FieldSet);
\r
5848 * @class Ext.form.HtmlEditor
5849 * @extends Ext.form.Field
5850 * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
5851 * automatically hidden when needed. These are noted in the config options where appropriate.
5852 * <br><br>The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
5853 * enabled by default unless the global {@link Ext.QuickTips} singleton is {@link Ext.QuickTips#init initialized}.
5854 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
5855 * supported by this editor.</b>
5856 * <br><br>An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
5857 * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.
5858 * <br><br>Example usage:
5860 // Simple example rendered with default options:
5861 Ext.QuickTips.init(); // enable tooltips
5862 new Ext.form.HtmlEditor({
5863 renderTo: Ext.getBody(),
5868 // Passed via xtype into a container and with custom options:
5869 Ext.QuickTips.init(); // enable tooltips
5871 title: 'HTML Editor',
5872 renderTo: Ext.getBody(),
5878 xtype: 'htmleditor',
5879 enableColors: false,
5880 enableAlignments: false
5885 * Create a new HtmlEditor
5886 * @param {Object} config
5890 Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
5892 * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
5894 enableFormat : true,
5896 * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
5898 enableFontSize : true,
5900 * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
5902 enableColors : true,
5904 * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
5906 enableAlignments : true,
5908 * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
5912 * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
5914 enableSourceEdit : true,
5916 * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
5920 * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
5924 * @cfg {String} createLinkText The default text for the create link prompt
5926 createLinkText : 'Please enter the URL for the link:',
5928 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
5930 defaultLinkValue : 'http:/'+'/',
5932 * @cfg {Array} fontFamilies An array of available font families
5941 defaultFont: 'tahoma',
5943 * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to   (Non-breaking space) in Opera and IE6, ​ (Zero-width space) in all other browsers).
5945 defaultValue: (Ext.isOpera || Ext.isIE6) ? ' ' : '​',
5947 // private properties
5949 validationEvent : false,
5951 initialized : false,
5953 sourceEditMode : false,
5954 onFocus : Ext.emptyFn,
5957 defaultAutoCreate : {
5959 style:"width:500px;height:300px;",
5964 initComponent : function(){
5968 * Fires when the editor is fully initialized (including the iframe)
5969 * @param {HtmlEditor} this
5974 * Fires when the editor is first receives the focus. Any insertion must wait
5975 * until after this event.
5976 * @param {HtmlEditor} this
5981 * Fires before the textarea is updated with content from the editor iframe. Return false
5982 * to cancel the sync.
5983 * @param {HtmlEditor} this
5984 * @param {String} html
5989 * Fires before the iframe editor is updated with content from the textarea. Return false
5990 * to cancel the push.
5991 * @param {HtmlEditor} this
5992 * @param {String} html
5997 * Fires when the textarea is updated with content from the editor iframe.
5998 * @param {HtmlEditor} this
5999 * @param {String} html
6004 * Fires when the iframe editor is updated with content from the textarea.
6005 * @param {HtmlEditor} this
6006 * @param {String} html
6010 * @event editmodechange
6011 * Fires when the editor switches edit modes
6012 * @param {HtmlEditor} this
6013 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
6020 createFontOptions : function(){
6021 var buf = [], fs = this.fontFamilies, ff, lc;
6022 for(var i = 0, len = fs.length; i< len; i++){
6024 lc = ff.toLowerCase();
6026 '<option value="',lc,'" style="font-family:',ff,';"',
6027 (this.defaultFont == lc ? ' selected="true">' : '>'),
6032 return buf.join('');
6036 * Protected method that will not generally be called directly. It
6037 * is called when the editor creates its toolbar. Override this method if you need to
6038 * add custom toolbar buttons.
6039 * @param {HtmlEditor} editor
6041 createToolbar : function(editor){
6043 var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
\r
6046 function btn(id, toggle, handler){
6050 iconCls: 'x-edit-'+id,
6051 enableToggle:toggle !== false,
6053 handler:handler||editor.relayBtnCmd,
6054 clickEvent:'mousedown',
6055 tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
6056 overflowText: editor.buttonTips[id].title || undefined,
6062 if(this.enableFont && !Ext.isSafari2){
\r
6063 var fontSelectItem = new Ext.Toolbar.Item({
\r
6066 cls:'x-font-select',
\r
6067 html: this.createFontOptions()
\r
6077 if(this.enableFormat){
6085 if(this.enableFontSize){
6088 btn('increasefontsize', false, this.adjustFont),
6089 btn('decreasefontsize', false, this.adjustFont)
6093 if(this.enableColors){
6098 iconCls: 'x-edit-forecolor',
6099 clickEvent:'mousedown',
6100 tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,
6102 menu : new Ext.menu.ColorMenu({
6103 allowReselect: true,
6109 select: function(cp, color){
6110 this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
6114 clickEvent:'mousedown'
6119 iconCls: 'x-edit-backcolor',
6120 clickEvent:'mousedown',
6121 tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,
6123 menu : new Ext.menu.ColorMenu({
6127 allowReselect: true,
6130 select: function(cp, color){
6132 this.execCmd('useCSS', false);
6133 this.execCmd('hilitecolor', color);
6134 this.execCmd('useCSS', true);
6137 this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
6142 clickEvent:'mousedown'
6148 if(this.enableAlignments){
6152 btn('justifycenter'),
6158 if(this.enableLinks){
6161 btn('createlink', false, this.createLink)
6165 if(this.enableLists){
6168 btn('insertorderedlist'),
6169 btn('insertunorderedlist')
6172 if(this.enableSourceEdit){
6175 btn('sourceedit', true, function(btn){
6176 this.toggleSourceEdit(!this.sourceEditMode);
6182 // build the toolbar
\r
6183 var tb = new Ext.Toolbar({
\r
6184 renderTo: this.wrap.dom.firstChild,
\r
6188 if (fontSelectItem) {
\r
6189 this.fontSelect = fontSelectItem.el;
\r
6191 this.mon(this.fontSelect, 'change', function(){
\r
6192 var font = this.fontSelect.dom.value;
\r
6193 this.relayCmd('fontname', font);
\r
6194 this.deferFocus();
\r
6199 // stop form submits
\r
6200 this.mon(tb.el, 'click', function(e){
\r
6201 e.preventDefault();
\r
6209 onDisable: function(){
6211 Ext.form.HtmlEditor.superclass.onDisable.call(this);
6214 onEnable: function(){
6216 Ext.form.HtmlEditor.superclass.onEnable.call(this);
6219 setReadOnly: function(readOnly){
6220 if(this.initialized){
6221 var newDM = readOnly ? 'off' : 'on',
6222 doc = this.getDoc();
6223 if(String(doc.designMode).toLowerCase() != newDM){
6224 doc.designMode = newDM;
6226 this.disableItems(!readOnly);
6228 Ext.form.HtmlEditor.superclass.setReadOnly.call(this, readOnly);
6232 * Protected method that will not generally be called directly. It
6233 * is called when the editor initializes the iframe with HTML contents. Override this method if you
6234 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
6236 getDocMarkup : function(){
6237 return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
6241 getEditorBody : function(){
6242 var doc = this.getDoc();
6243 return doc.body || doc.documentElement;
6247 getDoc : function(){
6248 return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
6252 getWin : function(){
6253 return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
6257 onRender : function(ct, position){
6258 Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
6259 this.el.dom.style.border = '0 none';
6260 this.el.dom.setAttribute('tabIndex', -1);
6261 this.el.addClass('x-hidden');
6262 if(Ext.isIE){ // fix IE 1px bogus margin
6263 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
6265 this.wrap = this.el.wrap({
6266 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
6269 this.createToolbar(this);
6271 this.disableItems(true);
6273 // this.tb.doLayout();
6275 this.createIFrame();
6278 var sz = this.el.getSize();
6279 this.setSize(sz.width, this.height || sz.height);
6281 this.resizeEl = this.positionEl = this.wrap;
6284 createIFrame: function(){
6285 var iframe = document.createElement('iframe');
6286 iframe.name = Ext.id();
6287 iframe.frameBorder = '0';
6288 iframe.src = Ext.SSL_SECURE_URL;
6289 this.wrap.dom.appendChild(iframe);
6291 this.iframe = iframe;
6293 this.monitorTask = Ext.TaskMgr.start({
6294 run: this.checkDesignMode,
6300 initFrame : function(){
6301 Ext.TaskMgr.stop(this.monitorTask);
6302 var doc = this.getDoc();
6303 this.win = this.getWin();
6306 doc.write(this.getDocMarkup());
6309 var task = { // must defer to wait for browser to be ready
6311 var doc = this.getDoc();
6312 if(doc.body || doc.readyState == 'complete'){
6313 Ext.TaskMgr.stop(task);
6314 doc.designMode="on";
6315 this.initEditor.defer(10, this);
6322 Ext.TaskMgr.start(task);
6326 checkDesignMode : function(){
6327 if(this.wrap && this.wrap.dom.offsetWidth){
6328 var doc = this.getDoc();
6332 if(!doc.editorInitialized || String(doc.designMode).toLowerCase() != 'on'){
6338 disableItems: function(disabled){
6339 if(this.fontSelect){
6340 this.fontSelect.dom.disabled = disabled;
6342 this.tb.items.each(function(item){
6343 if(item.getItemId() != 'sourceedit'){
6344 item.setDisabled(disabled);
6350 onResize : function(w, h){
6351 Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
6352 if(this.el && this.iframe){
6353 if(Ext.isNumber(w)){
6354 var aw = w - this.wrap.getFrameWidth('lr');
6355 this.el.setWidth(aw);
6356 this.tb.setWidth(aw);
6357 this.iframe.style.width = Math.max(aw, 0) + 'px';
6359 if(Ext.isNumber(h)){
6360 var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
6361 this.el.setHeight(ah);
6362 this.iframe.style.height = Math.max(ah, 0) + 'px';
6363 var bd = this.getEditorBody();
6365 bd.style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
6372 * Toggles the editor between standard and source edit mode.
6373 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
6375 toggleSourceEdit : function(sourceEditMode){
6376 if(sourceEditMode === undefined){
6377 sourceEditMode = !this.sourceEditMode;
6379 this.sourceEditMode = sourceEditMode === true;
6380 var btn = this.tb.getComponent('sourceedit');
6382 if(btn.pressed !== this.sourceEditMode){
6383 btn.toggle(this.sourceEditMode);
6388 if(this.sourceEditMode){
6389 this.disableItems(true);
6391 this.iframe.className = 'x-hidden';
6392 this.el.removeClass('x-hidden');
6393 this.el.dom.removeAttribute('tabIndex');
6396 if(this.initialized && !this.readOnly){
6397 this.disableItems(false);
6400 this.iframe.className = '';
6401 this.el.addClass('x-hidden');
6402 this.el.dom.setAttribute('tabIndex', -1);
6405 var lastSize = this.lastSize;
6407 delete this.lastSize;
6408 this.setSize(lastSize);
6410 this.fireEvent('editmodechange', this, this.sourceEditMode);
6413 // private used internally
6414 createLink : function(){
6415 var url = prompt(this.createLinkText, this.defaultLinkValue);
6416 if(url && url != 'http:/'+'/'){
6417 this.relayCmd('createlink', url);
6422 initEvents : function(){
6423 this.originalValue = this.getValue();
6427 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
6430 markInvalid : Ext.emptyFn,
6433 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
6436 clearInvalid : Ext.emptyFn,
6438 // docs inherit from Field
6439 setValue : function(v){
6440 Ext.form.HtmlEditor.superclass.setValue.call(this, v);
6446 * Protected method that will not generally be called directly. If you need/want
6447 * custom HTML cleanup, this is the method you should override.
6448 * @param {String} html The HTML to be cleaned
6449 * @return {String} The cleaned HTML
6451 cleanHtml: function(html) {
6452 html = String(html);
6453 if(Ext.isWebKit){ // strip safari nonsense
6454 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
6458 * Neat little hack. Strips out all the non-digit characters from the default
6459 * value and compares it to the character code of the first character in the string
6460 * because it can cause encoding issues when posted to the server.
6462 if(html.charCodeAt(0) == this.defaultValue.replace(/\D/g, '')){
6463 html = html.substring(1);
6469 * Protected method that will not generally be called directly. Syncs the contents
6470 * of the editor iframe with the textarea.
6472 syncValue : function(){
6473 if(this.initialized){
6474 var bd = this.getEditorBody();
6475 var html = bd.innerHTML;
6477 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
6478 var m = bs.match(/text-align:(.*?);/i);
6480 html = '<div style="'+m[0]+'">' + html + '</div>';
6483 html = this.cleanHtml(html);
6484 if(this.fireEvent('beforesync', this, html) !== false){
6485 this.el.dom.value = html;
6486 this.fireEvent('sync', this, html);
6491 //docs inherit from Field
6492 getValue : function() {
6493 this[this.sourceEditMode ? 'pushValue' : 'syncValue']();
6494 return Ext.form.HtmlEditor.superclass.getValue.call(this);
6498 * Protected method that will not generally be called directly. Pushes the value of the textarea
6499 * into the iframe editor.
6501 pushValue : function(){
6502 if(this.initialized){
6503 var v = this.el.dom.value;
6504 if(!this.activated && v.length < 1){
6505 v = this.defaultValue;
6507 if(this.fireEvent('beforepush', this, v) !== false){
6508 this.getEditorBody().innerHTML = v;
6510 // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
6511 var d = this.getDoc(),
6512 mode = d.designMode.toLowerCase();
6514 d.designMode = mode.toggle('on', 'off');
6515 d.designMode = mode;
6517 this.fireEvent('push', this, v);
6523 deferFocus : function(){
6524 this.focus.defer(10, this);
6527 // docs inherit from Field
6529 if(this.win && !this.sourceEditMode){
6537 initEditor : function(){
6538 //Destroying the component during/before initEditor can cause issues.
6540 var dbody = this.getEditorBody(),
6541 ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat'),
6545 ss['background-attachment'] = 'fixed'; // w3c
6546 dbody.bgProperties = 'fixed'; // ie
6548 Ext.DomHelper.applyStyles(dbody, ss);
6550 doc = this.getDoc();
6554 Ext.EventManager.removeAll(doc);
6559 * We need to use createDelegate here, because when using buffer, the delayed task is added
6560 * as a property to the function. When the listener is removed, the task is deleted from the function.
6561 * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors
6562 * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.
6564 fn = this.onEditorEvent.createDelegate(this);
6565 Ext.EventManager.on(doc, {
6574 Ext.EventManager.on(doc, 'keypress', this.applyCommand, this);
6576 if(Ext.isIE || Ext.isWebKit || Ext.isOpera){
6577 Ext.EventManager.on(doc, 'keydown', this.fixKeys, this);
6579 doc.editorInitialized = true;
6580 this.initialized = true;
6582 this.setReadOnly(this.readOnly);
6583 this.fireEvent('initialize', this);
6588 onDestroy : function(){
6589 if(this.monitorTask){
6590 Ext.TaskMgr.stop(this.monitorTask);
6593 Ext.destroy(this.tb);
6594 var doc = this.getDoc();
6597 Ext.EventManager.removeAll(doc);
6598 for (var prop in doc){
6604 this.wrap.dom.innerHTML = '';
6610 this.el.removeAllListeners();
6613 this.purgeListeners();
6617 onFirstFocus : function(){
6618 this.activated = true;
6619 this.disableItems(false);
6620 if(Ext.isGecko){ // prevent silly gecko errors
6622 var s = this.win.getSelection();
6623 if(!s.focusNode || s.focusNode.nodeType != 3){
6624 var r = s.getRangeAt(0);
6625 r.selectNodeContents(this.getEditorBody());
6630 this.execCmd('useCSS', true);
6631 this.execCmd('styleWithCSS', false);
6634 this.fireEvent('activate', this);
6638 adjustFont: function(btn){
6639 var adjust = btn.getItemId() == 'increasefontsize' ? 1 : -1,
6640 doc = this.getDoc(),
6641 v = parseInt(doc.queryCommandValue('FontSize') || 2, 10);
6642 if((Ext.isSafari && !Ext.isSafari2) || Ext.isChrome || Ext.isAir){
6644 // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
6658 v = v.constrain(1, 6);
6660 if(Ext.isSafari){ // safari
6663 v = Math.max(1, v+adjust) + (Ext.isSafari ? 'px' : 0);
6665 this.execCmd('FontSize', v);
6669 onEditorEvent : function(e){
6670 this.updateToolbar();
6675 * Protected method that will not generally be called directly. It triggers
6676 * a toolbar update by reading the markup state of the current selection in the editor.
6678 updateToolbar: function(){
6684 if(!this.activated){
6685 this.onFirstFocus();
6689 var btns = this.tb.items.map,
6690 doc = this.getDoc();
6692 if(this.enableFont && !Ext.isSafari2){
6693 var name = (doc.queryCommandValue('FontName')||this.defaultFont).toLowerCase();
6694 if(name != this.fontSelect.dom.value){
6695 this.fontSelect.dom.value = name;
6698 if(this.enableFormat){
6699 btns.bold.toggle(doc.queryCommandState('bold'));
6700 btns.italic.toggle(doc.queryCommandState('italic'));
6701 btns.underline.toggle(doc.queryCommandState('underline'));
6703 if(this.enableAlignments){
6704 btns.justifyleft.toggle(doc.queryCommandState('justifyleft'));
6705 btns.justifycenter.toggle(doc.queryCommandState('justifycenter'));
6706 btns.justifyright.toggle(doc.queryCommandState('justifyright'));
6708 if(!Ext.isSafari2 && this.enableLists){
6709 btns.insertorderedlist.toggle(doc.queryCommandState('insertorderedlist'));
6710 btns.insertunorderedlist.toggle(doc.queryCommandState('insertunorderedlist'));
6713 Ext.menu.MenuMgr.hideAll();
6719 relayBtnCmd : function(btn){
6720 this.relayCmd(btn.getItemId());
6724 * Executes a Midas editor command on the editor document and performs necessary focus and
6725 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
6726 * @param {String} cmd The Midas command
6727 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
6729 relayCmd : function(cmd, value){
6732 this.execCmd(cmd, value);
6733 this.updateToolbar();
6738 * Executes a Midas editor command directly on the editor document.
6739 * For visual commands, you should use {@link #relayCmd} instead.
6740 * <b>This should only be called after the editor is initialized.</b>
6741 * @param {String} cmd The Midas command
6742 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
6744 execCmd : function(cmd, value){
6745 var doc = this.getDoc();
6746 doc.execCommand(cmd, false, value === undefined ? null : value);
6751 applyCommand : function(e){
6753 var c = e.getCharCode(), cmd;
6755 c = String.fromCharCode(c);
6778 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
6780 * @param {String} text
6782 insertAtCursor : function(text){
6783 if(!this.activated){
6788 var doc = this.getDoc(),
6789 r = doc.selection.createRange();
6797 this.execCmd('InsertHTML', text);
6803 fixKeys : function(){ // load time branching for fastest keydown performance
6807 doc = this.getDoc(),
6811 r = doc.selection.createRange();
6814 r.pasteHTML(' ');
6817 }else if(k == e.ENTER){
6818 r = doc.selection.createRange();
6820 var target = r.parentElement();
6821 if(!target || target.tagName.toLowerCase() != 'li'){
6823 r.pasteHTML('<br />');
6830 }else if(Ext.isOpera){
6836 this.execCmd('InsertHTML',' ');
6840 }else if(Ext.isWebKit){
6845 this.execCmd('InsertText','\t');
6847 }else if(k == e.ENTER){
6849 this.execCmd('InsertHtml','<br /><br />');
6857 * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>
6858 * @return {Ext.Toolbar}
6860 getToolbar : function(){
6865 * Object collection of toolbar tooltips for the buttons in the editor. The key
6866 * is the command id associated with that button and the value is a valid QuickTips object.
6871 title: 'Bold (Ctrl+B)',
6872 text: 'Make the selected text bold.',
6873 cls: 'x-html-editor-tip'
6876 title: 'Italic (Ctrl+I)',
6877 text: 'Make the selected text italic.',
6878 cls: 'x-html-editor-tip'
6886 title: 'Bold (Ctrl+B)',
6887 text: 'Make the selected text bold.',
6888 cls: 'x-html-editor-tip'
6891 title: 'Italic (Ctrl+I)',
6892 text: 'Make the selected text italic.',
6893 cls: 'x-html-editor-tip'
6896 title: 'Underline (Ctrl+U)',
6897 text: 'Underline the selected text.',
6898 cls: 'x-html-editor-tip'
6900 increasefontsize : {
6902 text: 'Increase the font size.',
6903 cls: 'x-html-editor-tip'
6905 decreasefontsize : {
6906 title: 'Shrink Text',
6907 text: 'Decrease the font size.',
6908 cls: 'x-html-editor-tip'
6911 title: 'Text Highlight Color',
6912 text: 'Change the background color of the selected text.',
6913 cls: 'x-html-editor-tip'
6916 title: 'Font Color',
6917 text: 'Change the color of the selected text.',
6918 cls: 'x-html-editor-tip'
6921 title: 'Align Text Left',
6922 text: 'Align text to the left.',
6923 cls: 'x-html-editor-tip'
6926 title: 'Center Text',
6927 text: 'Center text in the editor.',
6928 cls: 'x-html-editor-tip'
6931 title: 'Align Text Right',
6932 text: 'Align text to the right.',
6933 cls: 'x-html-editor-tip'
6935 insertunorderedlist : {
6936 title: 'Bullet List',
6937 text: 'Start a bulleted list.',
6938 cls: 'x-html-editor-tip'
6940 insertorderedlist : {
6941 title: 'Numbered List',
6942 text: 'Start a numbered list.',
6943 cls: 'x-html-editor-tip'
6947 text: 'Make the selected text a hyperlink.',
6948 cls: 'x-html-editor-tip'
6951 title: 'Source Edit',
6952 text: 'Switch to source editing mode.',
6953 cls: 'x-html-editor-tip'
6957 // hide stuff that is not compatible
6975 * @cfg {String} fieldClass @hide
6978 * @cfg {String} focusClass @hide
6981 * @cfg {String} autoCreate @hide
6984 * @cfg {String} inputType @hide
6987 * @cfg {String} invalidClass @hide
6990 * @cfg {String} invalidText @hide
6993 * @cfg {String} msgFx @hide
6996 * @cfg {String} validateOnBlur @hide
6999 * @cfg {Boolean} allowDomMove @hide
7002 * @cfg {String} applyTo @hide
7005 * @cfg {String} autoHeight @hide
7008 * @cfg {String} autoWidth @hide
7011 * @cfg {String} cls @hide
7014 * @cfg {String} disabled @hide
7017 * @cfg {String} disabledClass @hide
7020 * @cfg {String} msgTarget @hide
7023 * @cfg {String} readOnly @hide
7026 * @cfg {String} style @hide
7029 * @cfg {String} validationDelay @hide
7032 * @cfg {String} validationEvent @hide
7035 * @cfg {String} tabIndex @hide
7038 * @property disabled
7042 * @method applyToMarkup
7062 * @method setDisabled
7070 Ext.reg('htmleditor', Ext.form.HtmlEditor);/**
\r
7071 * @class Ext.form.TimeField
\r
7072 * @extends Ext.form.ComboBox
\r
7073 * Provides a time input field with a time dropdown and automatic time validation. Example usage:
\r
7075 new Ext.form.TimeField({
\r
7076 minValue: '9:00 AM',
\r
7077 maxValue: '6:00 PM',
\r
7082 * Create a new TimeField
\r
7083 * @param {Object} config
\r
7084 * @xtype timefield
\r
7086 Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {
\r
7088 * @cfg {Date/String} minValue
\r
7089 * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string
\r
7090 * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
\r
7092 minValue : undefined,
\r
7094 * @cfg {Date/String} maxValue
\r
7095 * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string
\r
7096 * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
\r
7098 maxValue : undefined,
\r
7100 * @cfg {String} minText
\r
7101 * The error text to display when the date in the cell is before minValue (defaults to
\r
7102 * 'The time in this field must be equal to or after {0}').
\r
7104 minText : "The time in this field must be equal to or after {0}",
\r
7106 * @cfg {String} maxText
\r
7107 * The error text to display when the time is after maxValue (defaults to
\r
7108 * 'The time in this field must be equal to or before {0}').
\r
7110 maxText : "The time in this field must be equal to or before {0}",
\r
7112 * @cfg {String} invalidText
\r
7113 * The error text to display when the time in the field is invalid (defaults to
\r
7114 * '{value} is not a valid time').
\r
7116 invalidText : "{0} is not a valid time",
\r
7118 * @cfg {String} format
\r
7119 * The default time format string which can be overriden for localization support. The format must be
\r
7120 * valid according to {@link Date#parseDate} (defaults to 'g:i A', e.g., '3:15 PM'). For 24-hour time
\r
7121 * format try 'H:i' instead.
\r
7125 * @cfg {String} altFormats
\r
7126 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
\r
7127 * format (defaults to 'g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H').
\r
7129 altFormats : "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H",
\r
7131 * @cfg {Number} increment
\r
7132 * The number of minutes between each time value in the list (defaults to 15).
\r
7136 // private override
\r
7138 // private override
\r
7139 triggerAction: 'all',
\r
7140 // private override
\r
7143 // private - This is the date to use when generating time values in the absence of either minValue
\r
7144 // or maxValue. Using the current date causes DST issues on DST boundary dates, so this is an
\r
7145 // arbitrary "safe" date that can be any date aside from DST boundary dates.
\r
7146 initDate: '1/1/2008',
\r
7149 initComponent : function(){
\r
7150 if(Ext.isDefined(this.minValue)){
\r
7151 this.setMinValue(this.minValue, true);
\r
7153 if(Ext.isDefined(this.maxValue)){
\r
7154 this.setMaxValue(this.maxValue, true);
\r
7157 this.generateStore(true);
\r
7159 Ext.form.TimeField.superclass.initComponent.call(this);
\r
7163 * Replaces any existing {@link #minValue} with the new time and refreshes the store.
\r
7164 * @param {Date/String} value The minimum time that can be selected
\r
7166 setMinValue: function(value, /* private */ initial){
\r
7167 this.setLimit(value, true, initial);
\r
7172 * Replaces any existing {@link #maxValue} with the new time and refreshes the store.
\r
7173 * @param {Date/String} value The maximum time that can be selected
\r
7175 setMaxValue: function(value, /* private */ initial){
\r
7176 this.setLimit(value, false, initial);
\r
7181 generateStore: function(initial){
\r
7182 var min = this.minValue || new Date(this.initDate).clearTime(),
\r
7183 max = this.maxValue || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1),
\r
7186 while(min <= max){
\r
7187 times.push(min.dateFormat(this.format));
\r
7188 min = min.add('mi', this.increment);
\r
7190 this.bindStore(times, initial);
\r
7194 setLimit: function(value, isMin, initial){
\r
7196 if(Ext.isString(value)){
\r
7197 d = this.parseDate(value);
\r
7198 }else if(Ext.isDate(value)){
\r
7202 var val = new Date(this.initDate).clearTime();
\r
7203 val.setHours(d.getHours(), d.getMinutes(), isMin ? 0 : 59, 0);
\r
7204 this[isMin ? 'minValue' : 'maxValue'] = val;
\r
7206 this.generateStore();
\r
7212 getValue : function(){
\r
7213 var v = Ext.form.TimeField.superclass.getValue.call(this);
\r
7214 return this.formatDate(this.parseDate(v)) || '';
\r
7218 setValue : function(value){
\r
7219 return Ext.form.TimeField.superclass.setValue.call(this, this.formatDate(this.parseDate(value)));
\r
7222 // private overrides
\r
7223 validateValue : Ext.form.DateField.prototype.validateValue,
\r
7224 parseDate : Ext.form.DateField.prototype.parseDate,
\r
7225 formatDate : Ext.form.DateField.prototype.formatDate,
\r
7228 beforeBlur : function(){
\r
7229 var v = this.parseDate(this.getRawValue());
\r
7231 this.setValue(v.dateFormat(this.format));
\r
7233 Ext.form.TimeField.superclass.beforeBlur.call(this);
\r
7237 * @cfg {Boolean} grow @hide
\r
7240 * @cfg {Number} growMin @hide
\r
7243 * @cfg {Number} growMax @hide
\r
7247 * @method autoSize
\r
7250 Ext.reg('timefield', Ext.form.TimeField);/**
7251 * @class Ext.form.Label
7252 * @extends Ext.BoxComponent
7253 * Basic Label field.
7255 * Creates a new Label
7256 * @param {Ext.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
7257 * element and its id used as the component id. If a string is passed, it is assumed to be the id of an existing element
7258 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
7261 Ext.form.Label = Ext.extend(Ext.BoxComponent, {
7263 * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
7264 * tags within the label's innerHTML, use the {@link #html} config instead.
7267 * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
7268 * attribute. If not specified, the attribute will not be added to the label.
7271 * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
7272 * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
7276 onRender : function(ct, position){
7278 this.el = document.createElement('label');
7279 this.el.id = this.getId();
7280 this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
7282 this.el.setAttribute('for', this.forId);
7285 Ext.form.Label.superclass.onRender.call(this, ct, position);
7289 * Updates the label's innerHTML with the specified string.
7290 * @param {String} text The new label text
7291 * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
7292 * to the label (defaults to true which encodes the value). This might be useful if you want to include
7293 * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
7294 * @return {Label} this
7296 setText : function(t, encode){
7297 var e = encode === false;
7298 this[!e ? 'text' : 'html'] = t;
7299 delete this[e ? 'text' : 'html'];
7301 this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
7307 Ext.reg('label', Ext.form.Label);/**
7308 * @class Ext.form.Action
7309 * <p>The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.</p>
7310 * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
7311 * the Form needs to perform an action such as submit or load. The Configuration options
7312 * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit},
7313 * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}</p>
7314 * <p>The instance of Action which performed the action is passed to the success
7315 * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit},
7316 * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}),
7317 * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and
7318 * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.</p>
7320 Ext.form.Action = function(form, options){
7322 this.options = options || {};
7326 * Failure type returned when client side validation of the Form fails
7327 * thus aborting a submit action. Client side validation is performed unless
7328 * {@link #clientValidation} is explicitly set to <tt>false</tt>.
7332 Ext.form.Action.CLIENT_INVALID = 'client';
7334 * <p>Failure type returned when server side processing fails and the {@link #result}'s
7335 * <tt style="font-weight:bold">success</tt> property is set to <tt>false</tt>.</p>
7336 * <p>In the case of a form submission, field-specific error messages may be returned in the
7337 * {@link #result}'s <tt style="font-weight:bold">errors</tt> property.</p>
7341 Ext.form.Action.SERVER_INVALID = 'server';
7343 * Failure type returned when a communication error happens when attempting
7344 * to send a request to the remote server. The {@link #response} may be examined to
7345 * provide further information.
7349 Ext.form.Action.CONNECT_FAILURE = 'connect';
7351 * Failure type returned when the response's <tt style="font-weight:bold">success</tt>
7352 * property is set to <tt>false</tt>, or no field values are returned in the response's
7353 * <tt style="font-weight:bold">data</tt> property.
7357 Ext.form.Action.LOAD_FAILURE = 'load';
7359 Ext.form.Action.prototype = {
7361 * @cfg {String} url The URL that the Action is to invoke.
7364 * @cfg {Boolean} reset When set to <tt><b>true</b></tt>, causes the Form to be
7365 * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens
7366 * <b>before</b> the {@link #success} callback is called and before the Form's
7367 * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires.
7370 * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the
7371 * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method.
7374 * @cfg {Mixed} params <p>Extra parameter values to pass. These are added to the Form's
7375 * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's
7377 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
7380 * @cfg {Number} timeout The number of seconds to wait for a server response before
7381 * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified,
7382 * defaults to the configured <tt>{@link Ext.form.BasicForm#timeout timeout}</tt> of the
7383 * {@link Ext.form.BasicForm form}.
7386 * @cfg {Function} success The function to call when a valid success return packet is recieved.
7387 * The function is passed the following parameters:<ul class="mdetail-params">
7388 * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
7389 * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. The {@link #result}
7390 * property of this object may be examined to perform custom postprocessing.</div></li>
7394 * @cfg {Function} failure The function to call when a failure packet was recieved, or when an
7395 * error ocurred in the Ajax communication.
7396 * The function is passed the following parameters:<ul class="mdetail-params">
7397 * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
7398 * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. If an Ajax
7399 * error ocurred, the failure type will be in {@link #failureType}. The {@link #result}
7400 * property of this object may be examined to perform custom postprocessing.</div></li>
7404 * @cfg {Object} scope The scope in which to call the callback functions (The <tt>this</tt> reference
7405 * for the callback functions).
7408 * @cfg {String} waitMsg The message to be displayed by a call to {@link Ext.MessageBox#wait}
7409 * during the time the action is being processed.
7412 * @cfg {String} waitTitle The title to be displayed by a call to {@link Ext.MessageBox#wait}
7413 * during the time the action is being processed.
7417 * The type of action this Action instance performs.
7418 * Currently only "submit" and "load" are supported.
7423 * The type of failure detected will be one of these: {@link #CLIENT_INVALID},
7424 * {@link #SERVER_INVALID}, {@link #CONNECT_FAILURE}, or {@link #LOAD_FAILURE}. Usage:
7426 var fp = new Ext.form.FormPanel({
7431 handler: function(){
7432 if(fp.getForm().isValid()){
7433 fp.getForm().submit({
7434 url: 'form-submit.php',
7435 waitMsg: 'Submitting your data...',
7436 success: function(form, action){
7437 // server responded with success = true
7438 var result = action.{@link #result};
7440 failure: function(form, action){
7441 if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
7442 Ext.Msg.alert('Error',
7443 'Status:'+action.{@link #response}.status+': '+
7444 action.{@link #response}.statusText);
7446 if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
7447 // server responded with success = false
7448 Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
7456 handler: function(){
7457 fp.getForm().reset();
7461 * @property failureType
7465 * The XMLHttpRequest object used to perform the action.
7466 * @property response
7470 * The decoded response object containing a boolean <tt style="font-weight:bold">success</tt> property and
7471 * other, action-specific properties.
7477 run : function(options){
7482 success : function(response){
7487 handleResponse : function(response){
7491 // default connection failure
7492 failure : function(response){
7493 this.response = response;
7494 this.failureType = Ext.form.Action.CONNECT_FAILURE;
7495 this.form.afterAction(this, false);
7499 // shared code among all Actions to validate that there was a response
7500 // with either responseText or responseXml
7501 processResponse : function(response){
7502 this.response = response;
7503 if(!response.responseText && !response.responseXML){
7506 this.result = this.handleResponse(response);
7510 // utility functions used internally
7511 getUrl : function(appendParams){
7512 var url = this.options.url || this.form.url || this.form.el.dom.action;
7514 var p = this.getParams();
7516 url = Ext.urlAppend(url, p);
7523 getMethod : function(){
7524 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7528 getParams : function(){
7529 var bp = this.form.baseParams;
7530 var p = this.options.params;
7532 if(typeof p == "object"){
7533 p = Ext.urlEncode(Ext.applyIf(p, bp));
7534 }else if(typeof p == 'string' && bp){
7535 p += '&' + Ext.urlEncode(bp);
7538 p = Ext.urlEncode(bp);
7544 createCallback : function(opts){
7545 var opts = opts || {};
7547 success: this.success,
7548 failure: this.failure,
7550 timeout: (opts.timeout*1000) || (this.form.timeout*1000),
7551 upload: this.form.fileUpload ? this.success : undefined
7557 * @class Ext.form.Action.Submit
7558 * @extends Ext.form.Action
7559 * <p>A class which handles submission of data from {@link Ext.form.BasicForm Form}s
7560 * and processes the returned response.</p>
7561 * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
7562 * {@link Ext.form.BasicForm#submit submit}ting.</p>
7563 * <p><u><b>Response Packet Criteria</b></u></p>
7564 * <p>A response packet may contain:
7565 * <div class="mdetail-params"><ul>
7566 * <li><b><code>success</code></b> property : Boolean
7567 * <div class="sub-desc">The <code>success</code> property is required.</div></li>
7568 * <li><b><code>errors</code></b> property : Object
7569 * <div class="sub-desc"><div class="sub-desc">The <code>errors</code> property,
7570 * which is optional, contains error messages for invalid fields.</div></li>
7572 * <p><u><b>JSON Packets</b></u></p>
7573 * <p>By default, response packets are assumed to be JSON, so a typical response
7574 * packet may look like this:</p><pre><code>
7578 clientCode: "Client not found",
7579 portOfLoading: "This field must not be null"
7582 * <p>Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback
7583 * or event handler methods. The object decoded from this JSON is available in the
7584 * {@link Ext.form.Action#result result} property.</p>
7585 * <p>Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:</p><pre><code>
7586 errorReader: new Ext.data.XmlReader({
7594 * <p>then the results may be sent back in XML format:</p><pre><code>
7595 <?xml version="1.0" encoding="UTF-8"?>
7596 <message success="false">
7599 <id>clientCode</id>
7600 <msg><![CDATA[Code not found. <br /><i>This is a test validation message from the server </i>]]></msg>
7603 <id>portOfLoading</id>
7604 <msg><![CDATA[Port not found. <br /><i>This is a test validation message from the server </i>]]></msg>
7609 * <p>Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback
7610 * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.</p>
7612 Ext.form.Action.Submit = function(form, options){
7613 Ext.form.Action.Submit.superclass.constructor.call(this, form, options);
7616 Ext.extend(Ext.form.Action.Submit, Ext.form.Action, {
7618 * @cfg {Ext.data.DataReader} errorReader <p><b>Optional. JSON is interpreted with
7619 * no need for an errorReader.</b></p>
7620 * <p>A Reader which reads a single record from the returned data. The DataReader's
7621 * <b>success</b> property specifies how submission success is determined. The Record's
7622 * data provides the error messages to apply to any invalid form Fields.</p>
7625 * @cfg {boolean} clientValidation Determines whether a Form's fields are validated
7626 * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission.
7627 * Pass <tt>false</tt> in the Form's submit options to prevent this. If not defined, pre-submission field validation
7634 var o = this.options;
7635 var method = this.getMethod();
7636 var isGet = method == 'GET';
7637 if(o.clientValidation === false || this.form.isValid()){
7638 Ext.Ajax.request(Ext.apply(this.createCallback(o), {
7639 form:this.form.el.dom,
7640 url:this.getUrl(isGet),
7643 params:!isGet ? this.getParams() : null,
7644 isUpload: this.form.fileUpload
7646 }else if (o.clientValidation !== false){ // client validation failed
7647 this.failureType = Ext.form.Action.CLIENT_INVALID;
7648 this.form.afterAction(this, false);
7653 success : function(response){
7654 var result = this.processResponse(response);
7655 if(result === true || result.success){
7656 this.form.afterAction(this, true);
7660 this.form.markInvalid(result.errors);
7662 this.failureType = Ext.form.Action.SERVER_INVALID;
7663 this.form.afterAction(this, false);
7667 handleResponse : function(response){
7668 if(this.form.errorReader){
7669 var rs = this.form.errorReader.read(response);
7672 for(var i = 0, len = rs.records.length; i < len; i++) {
7673 var r = rs.records[i];
7677 if(errors.length < 1){
7681 success : rs.success,
7685 return Ext.decode(response.responseText);
7691 * @class Ext.form.Action.Load
7692 * @extends Ext.form.Action
7693 * <p>A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.</p>
7694 * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
7695 * {@link Ext.form.BasicForm#load load}ing.</p>
7696 * <p><u><b>Response Packet Criteria</b></u></p>
7697 * <p>A response packet <b>must</b> contain:
7698 * <div class="mdetail-params"><ul>
7699 * <li><b><code>success</code></b> property : Boolean</li>
7700 * <li><b><code>data</code></b> property : Object</li>
7701 * <div class="sub-desc">The <code>data</code> property contains the values of Fields to load.
7702 * The individual value object for each Field is passed to the Field's
7703 * {@link Ext.form.Field#setValue setValue} method.</div></li>
7705 * <p><u><b>JSON Packets</b></u></p>
7706 * <p>By default, response packets are assumed to be JSON, so for the following form load call:<pre><code>
7707 var myFormPanel = new Ext.form.FormPanel({
7708 title: 'Client and routing info',
7710 fieldLabel: 'Client',
7713 fieldLabel: 'Port of loading',
7714 name: 'portOfLoading'
7716 fieldLabel: 'Port of discharge',
7717 name: 'portOfDischarge'
7720 myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
7721 url: '/getRoutingInfo.php',
7723 consignmentRef: myConsignmentRef
7725 failure: function(form, action) {
7726 Ext.Msg.alert("Load failed", action.result.errorMessage);
7730 * a <b>success response</b> packet may look like this:</p><pre><code>
7734 clientName: "Fred. Olsen Lines",
7735 portOfLoading: "FXT",
7736 portOfDischarge: "OSL"
7739 * while a <b>failure response</b> packet may look like this:</p><pre><code>
7742 errorMessage: "Consignment reference not found"
7744 * <p>Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s
7745 * callback or event handler methods. The object decoded from this JSON is available in the
7746 * {@link Ext.form.Action#result result} property.</p>
7748 Ext.form.Action.Load = function(form, options){
7749 Ext.form.Action.Load.superclass.constructor.call(this, form, options);
7750 this.reader = this.form.reader;
7753 Ext.extend(Ext.form.Action.Load, Ext.form.Action, {
7759 Ext.Ajax.request(Ext.apply(
7760 this.createCallback(this.options), {
7761 method:this.getMethod(),
7762 url:this.getUrl(false),
7763 headers: this.options.headers,
7764 params:this.getParams()
7769 success : function(response){
7770 var result = this.processResponse(response);
7771 if(result === true || !result.success || !result.data){
7772 this.failureType = Ext.form.Action.LOAD_FAILURE;
7773 this.form.afterAction(this, false);
7776 this.form.clearInvalid();
7777 this.form.setValues(result.data);
7778 this.form.afterAction(this, true);
7782 handleResponse : function(response){
7783 if(this.form.reader){
7784 var rs = this.form.reader.read(response);
7785 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7787 success : rs.success,
7791 return Ext.decode(response.responseText);
7798 * @class Ext.form.Action.DirectLoad
7799 * @extends Ext.form.Action.Load
7800 * <p>Provides Ext.direct support for loading form data.</p>
7801 * <p>This example illustrates usage of Ext.Direct to <b>load</b> a form through Ext.Direct.</p>
7803 var myFormPanel = new Ext.form.FormPanel({
7804 // configs for FormPanel
7805 title: 'Basic Information',
7806 renderTo: document.body,
7807 width: 300, height: 160,
7810 // configs apply to child items
7811 defaults: {anchor: '100%'},
7812 defaultType: 'textfield',
7817 fieldLabel: 'Email',
7820 fieldLabel: 'Company',
7824 // configs for BasicForm
7826 // The server-side method to call for load() requests
7827 load: Profile.getBasicInfo,
7828 // The server-side must mark the submit handler as a 'formHandler'
7829 submit: Profile.updateBasicInfo
7831 // specify the order for the passed params
7832 paramOrder: ['uid', 'foo']
7836 myFormPanel.getForm().load({
7837 // pass 2 arguments to server side getBasicInfo method (len=2)
7844 * The data packet sent to the server will resemble something like:
7848 "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
7849 "data":[34,"bar"] // note the order of the params
7853 * The form will process a data packet returned by the server that is similar
7854 * to the following format:
7858 "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
7862 "name":"Fred Flintstone",
7863 "company":"Slate Rock and Gravel",
7864 "email":"fred.flintstone@slaterg.com"
7871 Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
7872 constructor: function(form, opts) {
7873 Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
7875 type : 'directload',
7878 var args = this.getParams();
7879 args.push(this.success, this);
7880 this.form.api.load.apply(window, args);
7883 getParams : function() {
7884 var buf = [], o = {};
7885 var bp = this.form.baseParams;
7886 var p = this.options.params;
7887 Ext.apply(o, p, bp);
7888 var paramOrder = this.form.paramOrder;
7890 for(var i = 0, len = paramOrder.length; i < len; i++){
7891 buf.push(o[paramOrder[i]]);
7893 }else if(this.form.paramsAsHash){
7898 // Direct actions have already been processed and therefore
7899 // we can directly set the result; Direct Actions do not have
7900 // a this.response property.
7901 processResponse : function(result) {
7902 this.result = result;
7906 success : function(response, trans){
7907 if(trans.type == Ext.Direct.exceptions.SERVER){
7910 Ext.form.Action.DirectLoad.superclass.success.call(this, response);
7915 * @class Ext.form.Action.DirectSubmit
7916 * @extends Ext.form.Action.Submit
7917 * <p>Provides Ext.direct support for submitting form data.</p>
7918 * <p>This example illustrates usage of Ext.Direct to <b>submit</b> a form through Ext.Direct.</p>
7920 var myFormPanel = new Ext.form.FormPanel({
7921 // configs for FormPanel
7922 title: 'Basic Information',
7923 renderTo: document.body,
7924 width: 300, height: 160,
7928 handler: function(){
7929 myFormPanel.getForm().submit({
7938 // configs apply to child items
7939 defaults: {anchor: '100%'},
7940 defaultType: 'textfield',
7945 fieldLabel: 'Email',
7948 fieldLabel: 'Company',
7952 // configs for BasicForm
7954 // The server-side method to call for load() requests
7955 load: Profile.getBasicInfo,
7956 // The server-side must mark the submit handler as a 'formHandler'
7957 submit: Profile.updateBasicInfo
7959 // specify the order for the passed params
7960 paramOrder: ['uid', 'foo']
7963 * The data packet sent to the server will resemble something like:
7966 "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
7970 "extAction":"Profile","extMethod":"updateBasicInfo",
7971 "extType":"rpc","extTID":"6","extUpload":"false",
7972 "name":"Aaron Conran","email":"aaron@extjs.com","company":"Ext JS, LLC"
7977 * The form will process a data packet returned by the server that is similar
7980 // sample success packet (batched requests)
7983 "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,
7990 // sample failure packet (one request)
7992 "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
7995 "email":"already taken"
8002 * Also see the discussion in {@link Ext.form.Action.DirectLoad}.
8004 Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
8005 constructor : function(form, opts) {
8006 Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
8008 type : 'directsubmit',
8009 // override of Submit
8011 var o = this.options;
8012 if(o.clientValidation === false || this.form.isValid()){
8013 // tag on any additional params to be posted in the
8015 this.success.params = this.getParams();
8016 this.form.api.submit(this.form.el.dom, this.success, this);
8017 }else if (o.clientValidation !== false){ // client validation failed
8018 this.failureType = Ext.form.Action.CLIENT_INVALID;
8019 this.form.afterAction(this, false);
8023 getParams : function() {
8025 var bp = this.form.baseParams;
8026 var p = this.options.params;
8027 Ext.apply(o, p, bp);
8030 // Direct actions have already been processed and therefore
8031 // we can directly set the result; Direct Actions do not have
8032 // a this.response property.
8033 processResponse : function(result) {
8034 this.result = result;
8038 success : function(response, trans){
8039 if(trans.type == Ext.Direct.exceptions.SERVER){
8042 Ext.form.Action.DirectSubmit.superclass.success.call(this, response);
8046 Ext.form.Action.ACTION_TYPES = {
8047 'load' : Ext.form.Action.Load,
8048 'submit' : Ext.form.Action.Submit,
8049 'directload' : Ext.form.Action.DirectLoad,
8050 'directsubmit' : Ext.form.Action.DirectSubmit
8053 * @class Ext.form.VTypes
8054 * <p>This is a singleton object which contains a set of commonly used field validation functions.
8055 * The validations provided are basic and intended to be easily customizable and extended.</p>
8056 * <p>To add custom VTypes specify the <code>{@link Ext.form.TextField#vtype vtype}</code> validation
8057 * test function, and optionally specify any corresponding error text to display and any keystroke
8058 * filtering mask to apply. For example:</p>
8060 // custom Vtype for vtype:'time'
8061 var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
8062 Ext.apply(Ext.form.VTypes, {
8063 // vtype validation function
8064 time: function(val, field) {
8065 return timeTest.test(val);
8067 // vtype Text property: The error text to display when the validation function returns false
8068 timeText: 'Not a valid time. Must be in the format "12:34 PM".',
8069 // vtype Mask property: The keystroke filter mask
8070 timeMask: /[\d\s:amp]/i
8075 // custom Vtype for vtype:'IPAddress'
8076 Ext.apply(Ext.form.VTypes, {
8077 IPAddress: function(v) {
8078 return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
8080 IPAddressText: 'Must be a numeric IP address',
8081 IPAddressMask: /[\d\.]/i
8086 Ext.form.VTypes = function(){
8087 // closure these in so they are only created once.
8088 var alpha = /^[a-zA-Z_]+$/,
8089 alphanum = /^[a-zA-Z0-9_]+$/,
8090 email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/,
8091 url = /(((^https?)|(^ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8093 // All these messages and functions are configurable
8096 * The function used to validate email addresses. Note that this is a very basic validation -- complete
8097 * validation per the email RFC specifications is very complex and beyond the scope of this class, although
8098 * this function can be overridden if a more comprehensive validation scheme is desired. See the validation
8099 * section of the <a href="http://en.wikipedia.org/wiki/E-mail_address">Wikipedia article on email addresses</a>
8100 * for additional information. This implementation is intended to validate the following emails:<tt>
8101 * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
8103 * @param {String} value The email address
8104 * @return {Boolean} true if the RegExp test passed, and false if not.
8106 'email' : function(v){
8107 return email.test(v);
8110 * The error text to display when the email validation function returns false. Defaults to:
8111 * <tt>'This field should be an e-mail address in the format "user@example.com"'</tt>
8114 'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
8116 * The keystroke filter mask to be applied on email input. See the {@link #email} method for
8117 * information about more complex email validation. Defaults to:
8118 * <tt>/[a-z0-9_\.\-@]/i</tt>
8121 'emailMask' : /[a-z0-9_\.\-@]/i,
8124 * The function used to validate URLs
8125 * @param {String} value The URL
8126 * @return {Boolean} true if the RegExp test passed, and false if not.
8128 'url' : function(v){
8132 * The error text to display when the url validation function returns false. Defaults to:
8133 * <tt>'This field should be a URL in the format "http:/'+'/www.example.com"'</tt>
8136 'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
8139 * The function used to validate alpha values
8140 * @param {String} value The value
8141 * @return {Boolean} true if the RegExp test passed, and false if not.
8143 'alpha' : function(v){
8144 return alpha.test(v);
8147 * The error text to display when the alpha validation function returns false. Defaults to:
8148 * <tt>'This field should only contain letters and _'</tt>
8151 'alphaText' : 'This field should only contain letters and _',
8153 * The keystroke filter mask to be applied on alpha input. Defaults to:
8154 * <tt>/[a-z_]/i</tt>
8157 'alphaMask' : /[a-z_]/i,
8160 * The function used to validate alphanumeric values
8161 * @param {String} value The value
8162 * @return {Boolean} true if the RegExp test passed, and false if not.
8164 'alphanum' : function(v){
8165 return alphanum.test(v);
8168 * The error text to display when the alphanumeric validation function returns false. Defaults to:
8169 * <tt>'This field should only contain letters, numbers and _'</tt>
8172 'alphanumText' : 'This field should only contain letters, numbers and _',
8174 * The keystroke filter mask to be applied on alphanumeric input. Defaults to:
8175 * <tt>/[a-z0-9_]/i</tt>
8178 'alphanumMask' : /[a-z0-9_]/i