Upgrade to ExtJS 3.1.1 - Released 02/08/2010
[extjs.git] / pkgs / pkg-forms-debug.js
1 /*!
2  * Ext JS Library 3.1.1
3  * Copyright(c) 2006-2010 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
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.
11  * @constructor
12  * Creates a new Field
13  * @param {Object} config Configuration options
14  * @xtype field
15  */
16 Ext.form.Field = Ext.extend(Ext.BoxComponent,  {
17     /**
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>
20      * @type Ext.Element
21      * @property label
22      */
23     /**
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.
28      */
29     /**
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).
32      */
33     /**
34      * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
35      */
36     /**
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()}.
40      */
41     /**
42      * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to '').
43      */
44
45     /**
46      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to 'x-form-invalid')
47      */
48     invalidClass : 'x-form-invalid',
49     /**
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')
52      */
53     invalidText : 'The value in this field is invalid',
54     /**
55      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to 'x-form-focus')
56      */
57     focusClass : 'x-form-focus',
58     /**
59      * @cfg {Boolean} preventMark
60      * <tt>true</tt> to disable {@link #markInvalid marking the field invalid}.
61      * Defaults to <tt>false</tt>.
62      */
63     /**
64      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
65       automatic validation (defaults to 'keyup').
66      */
67     validationEvent : 'keyup',
68     /**
69      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
70      */
71     validateOnBlur : true,
72     /**
73      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation
74      * is initiated (defaults to 250)
75      */
76     validationDelay : 250,
77     /**
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>
82      */
83     defaultAutoCreate : {tag: 'input', type: 'text', size: '20', autocomplete: 'off'},
84     /**
85      * @cfg {String} fieldClass The default CSS class for the field (defaults to 'x-form-field')
86      */
87     fieldClass : 'x-form-field',
88     /**
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>
98      * </ul></div>
99      */
100     msgTarget : 'qtip',
101     /**
102      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field
103      * (defaults to 'normal').
104      */
105     msgFx : 'normal',
106     /**
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>
114      */
115     readOnly : false,
116     /**
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>
120      */
121     disabled : false,
122     /**
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>.
125      */
126     submitValue: true,
127
128     // private
129     isFormField : true,
130
131     // private
132     msgDisplay: '',
133
134     // private
135     hasFocus : false,
136
137     // private
138     initComponent : function(){
139         Ext.form.Field.superclass.initComponent.call(this);
140         this.addEvents(
141             /**
142              * @event focus
143              * Fires when this field receives input focus.
144              * @param {Ext.form.Field} this
145              */
146             'focus',
147             /**
148              * @event blur
149              * Fires when this field loses input focus.
150              * @param {Ext.form.Field} this
151              */
152             'blur',
153             /**
154              * @event specialkey
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({
160     ...
161     items: [{
162             fieldLabel: 'Field 1',
163             name: 'field1',
164             allowBlank: false
165         },{
166             fieldLabel: 'Field 2',
167             name: 'field2',
168             listeners: {
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();
174                         form.submit();
175                     }
176                 }
177             }
178         }
179     ],
180     ...
181 });
182              * </code></pre>
183              * @param {Ext.form.Field} this
184              * @param {Ext.EventObject} e The event object
185              */
186             'specialkey',
187             /**
188              * @event change
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
193              */
194             'change',
195             /**
196              * @event invalid
197              * Fires after the field has been marked as invalid.
198              * @param {Ext.form.Field} this
199              * @param {String} msg The validation message
200              */
201             'invalid',
202             /**
203              * @event valid
204              * Fires after the field has been validated with no errors.
205              * @param {Ext.form.Field} this
206              */
207             'valid'
208         );
209     },
210
211     /**
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}
215      */
216     getName : function(){
217         return this.rendered && this.el.dom.name ? this.el.dom.name : this.name || this.id || '';
218     },
219
220     // private
221     onRender : function(ct, position){
222         if(!this.el){
223             var cfg = this.getAutoCreate();
224
225             if(!cfg.name){
226                 cfg.name = this.name || this.id;
227             }
228             if(this.inputType){
229                 cfg.type = this.inputType;
230             }
231             this.autoEl = cfg;
232         }
233         Ext.form.Field.superclass.onRender.call(this, ct, position);
234         if(this.submitValue === false){
235             this.el.dom.removeAttribute('name');
236         }
237         var type = this.el.dom.type;
238         if(type){
239             if(type == 'password'){
240                 type = 'text';
241             }
242             this.el.addClass('x-form-'+type);
243         }
244         if(this.readOnly){
245             this.setReadOnly(true);
246         }
247         if(this.tabIndex !== undefined){
248             this.el.dom.setAttribute('tabIndex', this.tabIndex);
249         }
250
251         this.el.addClass([this.fieldClass, this.cls]);
252     },
253
254     // private
255     getItemCt : function(){
256         return this.itemCt;
257     },
258
259     // private
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);
265         }
266         /**
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>.
270          * @type mixed
271          * @property originalValue
272          */
273         this.originalValue = this.getValue();
274     },
275
276     /**
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.
285      */
286     isDirty : function() {
287         if(this.disabled || !this.rendered) {
288             return false;
289         }
290         return String(this.getValue()) !== String(this.originalValue);
291     },
292
293     /**
294      * Sets the read only state of this field.
295      * @param {Boolean} readOnly Whether the field should be read only.
296      */
297     setReadOnly : function(readOnly){
298         if(this.rendered){
299             this.el.dom.readOnly = readOnly;
300         }
301         this.readOnly = readOnly;
302     },
303
304     // private
305     afterRender : function(){
306         Ext.form.Field.superclass.afterRender.call(this);
307         this.initEvents();
308         this.initValue();
309     },
310
311     // private
312     fireKey : function(e){
313         if(e.isSpecialKey()){
314             this.fireEvent('specialkey', this, e);
315         }
316     },
317
318     /**
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}
321      */
322     reset : function(){
323         this.setValue(this.originalValue);
324         this.clearInvalid();
325     },
326
327     // private
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);
331
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);
335     },
336
337     // private
338     preFocus: Ext.emptyFn,
339
340     // private
341     onFocus : function(){
342         this.preFocus();
343         if(this.focusClass){
344             this.el.addClass(this.focusClass);
345         }
346         if(!this.hasFocus){
347             this.hasFocus = true;
348             /**
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>
352              * @type mixed
353              * @property startValue
354              */
355             this.startValue = this.getValue();
356             this.fireEvent('focus', this);
357         }
358     },
359
360     // private
361     beforeBlur : Ext.emptyFn,
362
363     // private
364     onBlur : function(){
365         this.beforeBlur();
366         if(this.focusClass){
367             this.el.removeClass(this.focusClass);
368         }
369         this.hasFocus = false;
370         if(this.validationEvent !== false && (this.validateOnBlur || this.validationEvent == 'blur')){
371             this.validate();
372         }
373         var v = this.getValue();
374         if(String(v) !== String(this.startValue)){
375             this.fireEvent('change', this, v, this.startValue);
376         }
377         this.fireEvent('blur', this);
378         this.postBlur();
379     },
380
381     // private
382     postBlur : Ext.emptyFn,
383
384     /**
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
390      */
391     isValid : function(preventMark){
392         if(this.disabled){
393             return true;
394         }
395         var restore = this.preventMark;
396         this.preventMark = preventMark === true;
397         var v = this.validateValue(this.processValue(this.getRawValue()));
398         this.preventMark = restore;
399         return v;
400     },
401
402     /**
403      * Validates the field value
404      * @return {Boolean} True if the value is valid, else false
405      */
406     validate : function(){
407         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
408             this.clearInvalid();
409             return true;
410         }
411         return false;
412     },
413
414     /**
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
420      */
421     processValue : function(value){
422         return value;
423     },
424
425     /**
426      * @private
427      * Subclasses should provide the validation implementation by overriding this
428      * @param {Mixed} value
429      */
430     validateValue : function(value){
431         return true;
432     },
433
434     /**
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.
437      */
438     getActiveError : function(){
439         return this.activeError || '';
440     },
441
442     /**
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})
450      */
451     markInvalid : function(msg){
452         if(!this.rendered || this.preventMark){ // not rendered
453             return;
454         }
455         msg = msg || this.invalidText;
456
457         var mt = this.getMessageHandler();
458         if(mt){
459             mt.mark(this, msg);
460         }else if(this.msgTarget){
461             this.el.addClass(this.invalidClass);
462             var t = Ext.getDom(this.msgTarget);
463             if(t){
464                 t.innerHTML = msg;
465                 t.style.display = this.msgDisplay;
466             }
467         }
468         this.activeError = msg;
469         this.fireEvent('invalid', this, msg);
470     },
471
472     /**
473      * Clear any invalid styles/messages for this field
474      */
475     clearInvalid : function(){
476         if(!this.rendered || this.preventMark){ // not rendered
477             return;
478         }
479         this.el.removeClass(this.invalidClass);
480         var mt = this.getMessageHandler();
481         if(mt){
482             mt.clear(this);
483         }else if(this.msgTarget){
484             this.el.removeClass(this.invalidClass);
485             var t = Ext.getDom(this.msgTarget);
486             if(t){
487                 t.innerHTML = '';
488                 t.style.display = 'none';
489             }
490         }
491         delete this.activeError;
492         this.fireEvent('valid', this);
493     },
494
495     // private
496     getMessageHandler : function(){
497         return Ext.form.MessageTargets[this.msgTarget];
498     },
499
500     // private
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
504     },
505
506     // Alignment for 'under' target
507     alignErrorEl : function(){
508         this.errorEl.setWidth(this.getErrorCt().getWidth(true) - 20);
509     },
510
511     // Alignment for 'side' target
512     alignErrorIcon : function(){
513         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
514     },
515
516     /**
517      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
518      * @return {Mixed} value The field value
519      */
520     getRawValue : function(){
521         var v = this.rendered ? this.el.getValue() : Ext.value(this.value, '');
522         if(v === this.emptyText){
523             v = '';
524         }
525         return v;
526     },
527
528     /**
529      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
530      * @return {Mixed} value The field value
531      */
532     getValue : function(){
533         if(!this.rendered) {
534             return this.value;
535         }
536         var v = this.el.getValue();
537         if(v === this.emptyText || v === undefined){
538             v = '';
539         }
540         return v;
541     },
542
543     /**
544      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
545      * @param {Mixed} value The value to set
546      * @return {Mixed} value The field value that is set
547      */
548     setRawValue : function(v){
549         return this.rendered ? (this.el.dom.value = (Ext.isEmpty(v) ? '' : v)) : '';
550     },
551
552     /**
553      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
554      * @param {Mixed} value The value to set
555      * @return {Ext.form.Field} this
556      */
557     setValue : function(v){
558         this.value = v;
559         if(this.rendered){
560             this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
561             this.validate();
562         }
563         return this;
564     },
565
566     // private, does not work for all fields
567     append : function(v){
568          this.setValue([this.getValue(), v].join(''));
569     }
570
571     /**
572      * @cfg {Boolean} autoWidth @hide
573      */
574     /**
575      * @cfg {Boolean} autoHeight @hide
576      */
577
578     /**
579      * @cfg {String} autoEl @hide
580      */
581 });
582
583
584 Ext.form.MessageTargets = {
585     'qtip' : {
586         mark: function(field, msg){
587             field.el.addClass(field.invalidClass);
588             field.el.dom.qtip = msg;
589             field.el.dom.qclass = 'x-form-invalid-tip';
590             if(Ext.QuickTips){ // fix for floating editors interacting with DND
591                 Ext.QuickTips.enable();
592             }
593         },
594         clear: function(field){
595             field.el.removeClass(field.invalidClass);
596             field.el.dom.qtip = '';
597         }
598     },
599     'title' : {
600         mark: function(field, msg){
601             field.el.addClass(field.invalidClass);
602             field.el.dom.title = msg;
603         },
604         clear: function(field){
605             field.el.dom.title = '';
606         }
607     },
608     'under' : {
609         mark: function(field, msg){
610             field.el.addClass(field.invalidClass);
611             if(!field.errorEl){
612                 var elp = field.getErrorCt();
613                 if(!elp){ // field has no container el
614                     field.el.dom.title = msg;
615                     return;
616                 }
617                 field.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
618                 field.on('resize', field.alignErrorEl, field);
619                 field.on('destroy', function(){
620                     Ext.destroy(this.errorEl);
621                 }, field);
622             }
623             field.alignErrorEl();
624             field.errorEl.update(msg);
625             Ext.form.Field.msgFx[field.msgFx].show(field.errorEl, field);
626         },
627         clear: function(field){
628             field.el.removeClass(field.invalidClass);
629             if(field.errorEl){
630                 Ext.form.Field.msgFx[field.msgFx].hide(field.errorEl, field);
631             }else{
632                 field.el.dom.title = '';
633             }
634         }
635     },
636     'side' : {
637         mark: function(field, msg){
638             field.el.addClass(field.invalidClass);
639             if(!field.errorIcon){
640                 var elp = field.getErrorCt();
641                 if(!elp){ // field has no container el
642                     field.el.dom.title = msg;
643                     return;
644                 }
645                 field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
646                 field.on('resize', field.alignErrorIcon, field);
647                 field.on('destroy', function(){
648                     Ext.destroy(this.errorIcon);
649                 }, field);
650             }
651             field.alignErrorIcon();
652             field.errorIcon.dom.qtip = msg;
653             field.errorIcon.dom.qclass = 'x-form-invalid-tip';
654             field.errorIcon.show();
655         },
656         clear: function(field){
657             field.el.removeClass(field.invalidClass);
658             if(field.errorIcon){
659                 field.errorIcon.dom.qtip = '';
660                 field.errorIcon.hide();
661             }else{
662                 field.el.dom.title = '';
663             }
664         }
665     }
666 };
667
668 // anything other than normal should be considered experimental
669 Ext.form.Field.msgFx = {
670     normal : {
671         show: function(msgEl, f){
672             msgEl.setDisplayed('block');
673         },
674
675         hide : function(msgEl, f){
676             msgEl.setDisplayed(false).update('');
677         }
678     },
679
680     slide : {
681         show: function(msgEl, f){
682             msgEl.slideIn('t', {stopFx:true});
683         },
684
685         hide : function(msgEl, f){
686             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
687         }
688     },
689
690     slideRight : {
691         show: function(msgEl, f){
692             msgEl.fixDisplay();
693             msgEl.alignTo(f.el, 'tl-tr');
694             msgEl.slideIn('l', {stopFx:true});
695         },
696
697         hide : function(msgEl, f){
698             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
699         }
700     }
701 };
702 Ext.reg('field', Ext.form.Field);
703 /**
704  * @class Ext.form.TextField
705  * @extends Ext.form.Field
706  * <p>Basic text field.  Can be used as a direct replacement for traditional text inputs,
707  * or as the base class for more sophisticated input controls (like {@link Ext.form.TextArea}
708  * and {@link Ext.form.ComboBox}).</p>
709  * <p><b><u>Validation</u></b></p>
710  * <p>The validation procedure is described in the documentation for {@link #validateValue}.</p>
711  * <p><b><u>Alter Validation Behavior</u></b></p>
712  * <p>Validation behavior for each field can be configured:</p>
713  * <div class="mdetail-params"><ul>
714  * <li><code>{@link Ext.form.TextField#invalidText invalidText}</code> : the default validation message to
715  * show if any validation step above does not provide a message when invalid</li>
716  * <li><code>{@link Ext.form.TextField#maskRe maskRe}</code> : filter out keystrokes before any validation occurs</li>
717  * <li><code>{@link Ext.form.TextField#stripCharsRe stripCharsRe}</code> : filter characters after being typed in,
718  * but before being validated</li>
719  * <li><code>{@link Ext.form.Field#invalidClass invalidClass}</code> : alternate style when invalid</li>
720  * <li><code>{@link Ext.form.Field#validateOnBlur validateOnBlur}</code>,
721  * <code>{@link Ext.form.Field#validationDelay validationDelay}</code>, and
722  * <code>{@link Ext.form.Field#validationEvent validationEvent}</code> : modify how/when validation is triggered</li>
723  * </ul></div>
724  * 
725  * @constructor Creates a new TextField
726  * @param {Object} config Configuration options
727  * 
728  * @xtype textfield
729  */
730 Ext.form.TextField = Ext.extend(Ext.form.Field,  {
731     /**
732      * @cfg {String} vtypeText A custom error message to display in place of the default message provided
733      * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>''</tt>).  <b>Note</b>:
734      * only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
735      */
736     /**
737      * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
738      * before validation (defaults to <tt>null</tt>).
739      */
740     /**
741      * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
742      * (defaults to <tt>false</tt>)
743      */
744     grow : false,
745     /**
746      * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
747      * to <tt>30</tt>)
748      */
749     growMin : 30,
750     /**
751      * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
752      * to <tt>800</tt>)
753      */
754     growMax : 800,
755     /**
756      * @cfg {String} vtype A validation type name as defined in {@link Ext.form.VTypes} (defaults to <tt>null</tt>)
757      */
758     vtype : null,
759     /**
760      * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
761      * not match (defaults to <tt>null</tt>)
762      */
763     maskRe : null,
764     /**
765      * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
766      * to <tt>false</tt>)
767      */
768     disableKeyFilter : false,
769     /**
770      * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
771      * <tt>true</tt>)
772      */
773     allowBlank : true,
774     /**
775      * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
776      */
777     minLength : 0,
778     /**
779      * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
780      * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
781      * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
782      * entered into the field use <tt><b>{@link Ext.form.Field#autoCreate autoCreate}</b></tt> to add
783      * any attributes you want to a field, for example:<pre><code>
784 var myField = new Ext.form.NumberField({
785     id: 'mobile',
786     anchor:'90%',
787     fieldLabel: 'Mobile',
788     maxLength: 16, // for validation
789     autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete: 'off', maxlength: '10'}
790 });
791 </code></pre>
792      */
793     maxLength : Number.MAX_VALUE,
794     /**
795      * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
796      * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
797      */
798     minLengthText : 'The minimum length for this field is {0}',
799     /**
800      * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
801      * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
802      */
803     maxLengthText : 'The maximum length for this field is {0}',
804     /**
805      * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
806      * receives input focus (defaults to <tt>false</tt>)
807      */
808     selectOnFocus : false,
809     /**
810      * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
811      * fails (defaults to <tt>'This field is required'</tt>)
812      */
813     blankText : 'This field is required',
814     /**
815      * @cfg {Function} validator
816      * <p>A custom validation function to be called during field validation ({@link #validateValue})
817      * (defaults to <tt>null</tt>). If specified, this function will be called first, allowing the
818      * developer to override the default validation process.</p>
819      * <br><p>This function will be passed the following Parameters:</p>
820      * <div class="mdetail-params"><ul>
821      * <li><code>value</code>: <i>Mixed</i>
822      * <div class="sub-desc">The current field value</div></li>
823      * </ul></div>
824      * <br><p>This function is to Return:</p>
825      * <div class="mdetail-params"><ul>
826      * <li><code>true</code>: <i>Boolean</i>
827      * <div class="sub-desc"><code>true</code> if the value is valid</div></li>
828      * <li><code>msg</code>: <i>String</i>
829      * <div class="sub-desc">An error message if the value is invalid</div></li>
830      * </ul></div>
831      */
832     validator : null,
833     /**
834      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
835      * (defaults to <tt>null</tt>). If the test fails, the field will be marked invalid using
836      * <b><tt>{@link #regexText}</tt></b>.
837      */
838     regex : null,
839     /**
840      * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
841      * test fails during validation (defaults to <tt>''</tt>)
842      */
843     regexText : '',
844     /**
845      * @cfg {String} emptyText The default text to place into an empty field (defaults to <tt>null</tt>).
846      * <b>Note</b>: that this value will be submitted to the server if this field is enabled and configured
847      * with a {@link #name}.
848      */
849     emptyText : null,
850     /**
851      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
852      * (defaults to <tt>'x-form-empty-field'</tt>).  This class is automatically added and removed as needed
853      * depending on the current field value.
854      */
855     emptyClass : 'x-form-empty-field',
856
857     /**
858      * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input
859      * field (defaults to <tt>false</tt>)
860      */
861
862     initComponent : function(){
863         Ext.form.TextField.superclass.initComponent.call(this);
864         this.addEvents(
865             /**
866              * @event autosize
867              * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered. The field may or
868              * may not have actually changed size according to the default logic, but this event provides
869              * a hook for the developer to apply additional logic at runtime to resize the field if needed.
870              * @param {Ext.form.Field} this This text field
871              * @param {Number} width The new field width
872              */
873             'autosize',
874
875             /**
876              * @event keydown
877              * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
878              * is set to true.
879              * @param {Ext.form.TextField} this This text field
880              * @param {Ext.EventObject} e
881              */
882             'keydown',
883             /**
884              * @event keyup
885              * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
886              * is set to true.
887              * @param {Ext.form.TextField} this This text field
888              * @param {Ext.EventObject} e
889              */
890             'keyup',
891             /**
892              * @event keypress
893              * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
894              * is set to true.
895              * @param {Ext.form.TextField} this This text field
896              * @param {Ext.EventObject} e
897              */
898             'keypress'
899         );
900     },
901
902     // private
903     initEvents : function(){
904         Ext.form.TextField.superclass.initEvents.call(this);
905         if(this.validationEvent == 'keyup'){
906             this.validationTask = new Ext.util.DelayedTask(this.validate, this);
907             this.mon(this.el, 'keyup', this.filterValidation, this);
908         }
909         else if(this.validationEvent !== false && this.validationEvent != 'blur'){
910                 this.mon(this.el, this.validationEvent, this.validate, this, {buffer: this.validationDelay});
911         }
912         if(this.selectOnFocus || this.emptyText){            
913             this.mon(this.el, 'mousedown', this.onMouseDown, this);
914             
915             if(this.emptyText){
916                 this.applyEmptyText();
917             }
918         }
919         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Ext.form.VTypes[this.vtype+'Mask']))){
920                 this.mon(this.el, 'keypress', this.filterKeys, this);
921         }
922         if(this.grow){
923                 this.mon(this.el, 'keyup', this.onKeyUpBuffered, this, {buffer: 50});
924                         this.mon(this.el, 'click', this.autoSize, this);
925         }
926         if(this.enableKeyEvents){
927             this.mon(this.el, {
928                 scope: this,
929                 keyup: this.onKeyUp,
930                 keydown: this.onKeyDown,
931                 keypress: this.onKeyPress
932             });
933         }
934     },
935     
936     onMouseDown: function(e){
937         if(!this.hasFocus){
938             this.mon(this.el, 'mouseup', Ext.emptyFn, this, { single: true, preventDefault: true });
939         }
940     },
941
942     processValue : function(value){
943         if(this.stripCharsRe){
944             var newValue = value.replace(this.stripCharsRe, '');
945             if(newValue !== value){
946                 this.setRawValue(newValue);
947                 return newValue;
948             }
949         }
950         return value;
951     },
952
953     filterValidation : function(e){
954         if(!e.isNavKeyPress()){
955             this.validationTask.delay(this.validationDelay);
956         }
957     },
958     
959     //private
960     onDisable: function(){
961         Ext.form.TextField.superclass.onDisable.call(this);
962         if(Ext.isIE){
963             this.el.dom.unselectable = 'on';
964         }
965     },
966     
967     //private
968     onEnable: function(){
969         Ext.form.TextField.superclass.onEnable.call(this);
970         if(Ext.isIE){
971             this.el.dom.unselectable = '';
972         }
973     },
974
975     // private
976     onKeyUpBuffered : function(e){
977         if(this.doAutoSize(e)){
978             this.autoSize();
979         }
980     },
981     
982     // private
983     doAutoSize : function(e){
984         return !e.isNavKeyPress();
985     },
986
987     // private
988     onKeyUp : function(e){
989         this.fireEvent('keyup', this, e);
990     },
991
992     // private
993     onKeyDown : function(e){
994         this.fireEvent('keydown', this, e);
995     },
996
997     // private
998     onKeyPress : function(e){
999         this.fireEvent('keypress', this, e);
1000     },
1001
1002     /**
1003      * Resets the current field value to the originally-loaded value and clears any validation messages.
1004      * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyClass}</b></tt> if the
1005      * original value was blank.
1006      */
1007     reset : function(){
1008         Ext.form.TextField.superclass.reset.call(this);
1009         this.applyEmptyText();
1010     },
1011
1012     applyEmptyText : function(){
1013         if(this.rendered && this.emptyText && this.getRawValue().length < 1 && !this.hasFocus){
1014             this.setRawValue(this.emptyText);
1015             this.el.addClass(this.emptyClass);
1016         }
1017     },
1018
1019     // private
1020     preFocus : function(){
1021         var el = this.el;
1022         if(this.emptyText){
1023             if(el.dom.value == this.emptyText){
1024                 this.setRawValue('');
1025             }
1026             el.removeClass(this.emptyClass);
1027         }
1028         if(this.selectOnFocus){
1029             el.dom.select();
1030         }
1031     },
1032
1033     // private
1034     postBlur : function(){
1035         this.applyEmptyText();
1036     },
1037
1038     // private
1039     filterKeys : function(e){
1040         if(e.ctrlKey){
1041             return;
1042         }
1043         var k = e.getKey();
1044         if(Ext.isGecko && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
1045             return;
1046         }
1047         var cc = String.fromCharCode(e.getCharCode());
1048         if(!Ext.isGecko && e.isSpecialKey() && !cc){
1049             return;
1050         }
1051         if(!this.maskRe.test(cc)){
1052             e.stopEvent();
1053         }
1054     },
1055
1056     setValue : function(v){
1057         if(this.emptyText && this.el && !Ext.isEmpty(v)){
1058             this.el.removeClass(this.emptyClass);
1059         }
1060         Ext.form.TextField.superclass.setValue.apply(this, arguments);
1061         this.applyEmptyText();
1062         this.autoSize();
1063         return this;
1064     },
1065
1066     /**
1067      * <p>Validates a value according to the field's validation rules and marks the field as invalid
1068      * if the validation fails. Validation rules are processed in the following order:</p>
1069      * <div class="mdetail-params"><ul>
1070      * 
1071      * <li><b>1. Field specific validator</b>
1072      * <div class="sub-desc">
1073      * <p>A validator offers a way to customize and reuse a validation specification.
1074      * If a field is configured with a <code>{@link #validator}</code>
1075      * function, it will be passed the current field value.  The <code>{@link #validator}</code>
1076      * function is expected to return either:
1077      * <div class="mdetail-params"><ul>
1078      * <li>Boolean <tt>true</tt> if the value is valid (validation continues).</li>
1079      * <li>a String to represent the invalid message if invalid (validation halts).</li>
1080      * </ul></div>
1081      * </div></li>
1082      * 
1083      * <li><b>2. Basic Validation</b>
1084      * <div class="sub-desc">
1085      * <p>If the <code>{@link #validator}</code> has not halted validation,
1086      * basic validation proceeds as follows:</p>
1087      * 
1088      * <div class="mdetail-params"><ul>
1089      * 
1090      * <li><code>{@link #allowBlank}</code> : (Invalid message =
1091      * <code>{@link #emptyText}</code>)<div class="sub-desc">
1092      * Depending on the configuration of <code>{@link #allowBlank}</code>, a
1093      * blank field will cause validation to halt at this step and return
1094      * Boolean true or false accordingly.  
1095      * </div></li>
1096      * 
1097      * <li><code>{@link #minLength}</code> : (Invalid message =
1098      * <code>{@link #minLengthText}</code>)<div class="sub-desc">
1099      * If the passed value does not satisfy the <code>{@link #minLength}</code>
1100      * specified, validation halts.
1101      * </div></li>
1102      * 
1103      * <li><code>{@link #maxLength}</code> : (Invalid message =
1104      * <code>{@link #maxLengthText}</code>)<div class="sub-desc">
1105      * If the passed value does not satisfy the <code>{@link #maxLength}</code>
1106      * specified, validation halts.
1107      * </div></li>
1108      * 
1109      * </ul></div>
1110      * </div></li>
1111      * 
1112      * <li><b>3. Preconfigured Validation Types (VTypes)</b>
1113      * <div class="sub-desc">
1114      * <p>If none of the prior validation steps halts validation, a field
1115      * configured with a <code>{@link #vtype}</code> will utilize the
1116      * corresponding {@link Ext.form.VTypes VTypes} validation function.
1117      * If invalid, either the field's <code>{@link #vtypeText}</code> or
1118      * the VTypes vtype Text property will be used for the invalid message.
1119      * Keystrokes on the field will be filtered according to the VTypes
1120      * vtype Mask property.</p>
1121      * </div></li>
1122      * 
1123      * <li><b>4. Field specific regex test</b>
1124      * <div class="sub-desc">
1125      * <p>If none of the prior validation steps halts validation, a field's
1126      * configured <code>{@link #regex}</code> test will be processed.
1127      * The invalid message for this test is configured with
1128      * <code>{@link #regexText}</code>.</p>
1129      * </div></li>
1130      * 
1131      * @param {Mixed} value The value to validate
1132      * @return {Boolean} True if the value is valid, else false
1133      */
1134     validateValue : function(value){
1135         if(Ext.isFunction(this.validator)){
1136             var msg = this.validator(value);
1137             if(msg !== true){
1138                 this.markInvalid(msg);
1139                 return false;
1140             }
1141         }
1142         if(value.length < 1 || value === this.emptyText){ // if it's blank
1143              if(this.allowBlank){
1144                  this.clearInvalid();
1145                  return true;
1146              }else{
1147                  this.markInvalid(this.blankText);
1148                  return false;
1149              }
1150         }
1151         if(value.length < this.minLength){
1152             this.markInvalid(String.format(this.minLengthText, this.minLength));
1153             return false;
1154         }
1155         if(value.length > this.maxLength){
1156             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
1157             return false;
1158         }       
1159         if(this.vtype){
1160             var vt = Ext.form.VTypes;
1161             if(!vt[this.vtype](value, this)){
1162                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
1163                 return false;
1164             }
1165         }
1166         if(this.regex && !this.regex.test(value)){
1167             this.markInvalid(this.regexText);
1168             return false;
1169         }
1170         return true;
1171     },
1172
1173     /**
1174      * Selects text in this field
1175      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
1176      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
1177      */
1178     selectText : function(start, end){
1179         var v = this.getRawValue();
1180         var doFocus = false;
1181         if(v.length > 0){
1182             start = start === undefined ? 0 : start;
1183             end = end === undefined ? v.length : end;
1184             var d = this.el.dom;
1185             if(d.setSelectionRange){
1186                 d.setSelectionRange(start, end);
1187             }else if(d.createTextRange){
1188                 var range = d.createTextRange();
1189                 range.moveStart('character', start);
1190                 range.moveEnd('character', end-v.length);
1191                 range.select();
1192             }
1193             doFocus = Ext.isGecko || Ext.isOpera;
1194         }else{
1195             doFocus = true;
1196         }
1197         if(doFocus){
1198             this.focus();
1199         }
1200     },
1201
1202     /**
1203      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
1204      * This only takes effect if <tt><b>{@link #grow}</b> = true</tt>, and fires the {@link #autosize} event.
1205      */
1206     autoSize : function(){
1207         if(!this.grow || !this.rendered){
1208             return;
1209         }
1210         if(!this.metrics){
1211             this.metrics = Ext.util.TextMetrics.createInstance(this.el);
1212         }
1213         var el = this.el;
1214         var v = el.dom.value;
1215         var d = document.createElement('div');
1216         d.appendChild(document.createTextNode(v));
1217         v = d.innerHTML;
1218         Ext.removeNode(d);
1219         d = null;
1220         v += '&#160;';
1221         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
1222         this.el.setWidth(w);
1223         this.fireEvent('autosize', this, w);
1224     },
1225         
1226         onDestroy: function(){
1227                 if(this.validationTask){
1228                         this.validationTask.cancel();
1229                         this.validationTask = null;
1230                 }
1231                 Ext.form.TextField.superclass.onDestroy.call(this);
1232         }
1233 });
1234 Ext.reg('textfield', Ext.form.TextField);
1235 /**
1236  * @class Ext.form.TriggerField
1237  * @extends Ext.form.TextField
1238  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
1239  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
1240  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
1241  * for which you can provide a custom implementation.  For example:
1242  * <pre><code>
1243 var trigger = new Ext.form.TriggerField();
1244 trigger.onTriggerClick = myTriggerFn;
1245 trigger.applyToMarkup('my-field');
1246 </code></pre>
1247  *
1248  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
1249  * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
1250  *
1251  * @constructor
1252  * Create a new TriggerField.
1253  * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
1254  * to the base TextField)
1255  * @xtype trigger
1256  */
1257 Ext.form.TriggerField = Ext.extend(Ext.form.TextField,  {
1258     /**
1259      * @cfg {String} triggerClass
1260      * An additional CSS class used to style the trigger button.  The trigger will always get the
1261      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
1262      */
1263     /**
1264      * @cfg {Mixed} triggerConfig
1265      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the
1266      * trigger element for this Field. (Optional).</p>
1267      * <p>Specify this when you need a customized element to act as the trigger button for a TriggerField.</p>
1268      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning
1269      * and appearance of the trigger.  Defaults to:</p>
1270      * <pre><code>{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}</code></pre>
1271      */
1272     /**
1273      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
1274      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
1275      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
1276      * <pre><code>{tag: "input", type: "text", size: "16", autocomplete: "off"}</code></pre>
1277      */
1278     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
1279     /**
1280      * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base
1281      * text field (defaults to <tt>false</tt>)
1282      */
1283     hideTrigger:false,
1284     /**
1285      * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field,
1286      * the field will only respond to a click on the trigger to set the value. (defaults to <tt>true</tt>).
1287      */
1288     editable: true,
1289     /**
1290      * @cfg {Boolean} readOnly <tt>true</tt> to prevent the user from changing the field, and
1291      * hides the trigger.  Superceeds the editable and hideTrigger options if the value is true.
1292      * (defaults to <tt>false</tt>)
1293      */
1294     readOnly: false,
1295     /**
1296      * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
1297      * <tt>x-trigger-wrap-focus</tt>.
1298      */
1299     wrapFocusClass: 'x-trigger-wrap-focus',
1300     /**
1301      * @hide
1302      * @method autoSize
1303      */
1304     autoSize: Ext.emptyFn,
1305     // private
1306     monitorTab : true,
1307     // private
1308     deferHeight : true,
1309     // private
1310     mimicing : false,
1311
1312     actionMode: 'wrap',
1313
1314     defaultTriggerWidth: 17,
1315
1316     // private
1317     onResize : function(w, h){
1318         Ext.form.TriggerField.superclass.onResize.call(this, w, h);
1319         var tw = this.getTriggerWidth();
1320         if(Ext.isNumber(w)){
1321             this.el.setWidth(w - tw);
1322         }
1323         this.wrap.setWidth(this.el.getWidth() + tw);
1324     },
1325
1326     getTriggerWidth: function(){
1327         var tw = this.trigger.getWidth();
1328         if(!this.hideTrigger && tw === 0){
1329             tw = this.defaultTriggerWidth;
1330         }
1331         return tw;
1332     },
1333
1334     // private
1335     alignErrorIcon : function(){
1336         if(this.wrap){
1337             this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
1338         }
1339     },
1340
1341     // private
1342     onRender : function(ct, position){
1343         this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
1344         Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
1345
1346         this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
1347         this.trigger = this.wrap.createChild(this.triggerConfig ||
1348                 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
1349         this.initTrigger();
1350         if(!this.width){
1351             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
1352         }
1353         this.resizeEl = this.positionEl = this.wrap;
1354     },
1355
1356     updateEditState: function(){
1357         if(this.rendered){
1358             if (this.readOnly) {
1359                 this.el.dom.readOnly = true;
1360                 this.el.addClass('x-trigger-noedit');
1361                 this.mun(this.el, 'click', this.onTriggerClick, this);
1362                 this.trigger.setDisplayed(false);
1363             } else {
1364                 if (!this.editable) {
1365                     this.el.dom.readOnly = true;
1366                     this.el.addClass('x-trigger-noedit');
1367                     this.mon(this.el, 'click', this.onTriggerClick, this);
1368                 } else {
1369                     this.el.dom.readOnly = false;
1370                     this.el.removeClass('x-trigger-noedit');
1371                     this.mun(this.el, 'click', this.onTriggerClick, this);
1372                 }
1373                 this.trigger.setDisplayed(!this.hideTrigger);
1374             }
1375             this.onResize(this.width || this.wrap.getWidth());
1376         }
1377     },
1378
1379     setHideTrigger: function(hideTrigger){
1380         if(hideTrigger != this.hideTrigger){
1381             this.hideTrigger = hideTrigger;
1382             this.updateEditState();
1383         }
1384     },
1385
1386     /**
1387      * @param {Boolean} value True to allow the user to directly edit the field text
1388      * Allow or prevent the user from directly editing the field text.  If false is passed,
1389      * the user will only be able to modify the field using the trigger.  Will also add
1390      * a click event to the text field which will call the trigger. This method
1391      * is the runtime equivalent of setting the 'editable' config option at config time.
1392      */
1393     setEditable: function(editable){
1394         if(editable != this.editable){
1395             this.editable = editable;
1396             this.updateEditState();
1397         }
1398     },
1399
1400     /**
1401      * @param {Boolean} value True to prevent the user changing the field and explicitly
1402      * hide the trigger.
1403      * Setting this to true will superceed settings editable and hideTrigger.
1404      * Setting this to false will defer back to editable and hideTrigger. This method
1405      * is the runtime equivalent of setting the 'readOnly' config option at config time.
1406      */
1407     setReadOnly: function(readOnly){
1408         if(readOnly != this.readOnly){
1409             this.readOnly = readOnly;
1410             this.updateEditState();
1411         }
1412     },
1413
1414     afterRender : function(){
1415         Ext.form.TriggerField.superclass.afterRender.call(this);
1416         this.updateEditState();
1417     },
1418
1419     // private
1420     initTrigger : function(){
1421         this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
1422         this.trigger.addClassOnOver('x-form-trigger-over');
1423         this.trigger.addClassOnClick('x-form-trigger-click');
1424     },
1425
1426     // private
1427     onDestroy : function(){
1428         Ext.destroy(this.trigger, this.wrap);
1429         if (this.mimicing){
1430             this.doc.un('mousedown', this.mimicBlur, this);
1431         }
1432         delete this.doc;
1433         Ext.form.TriggerField.superclass.onDestroy.call(this);
1434     },
1435
1436     // private
1437     onFocus : function(){
1438         Ext.form.TriggerField.superclass.onFocus.call(this);
1439         if(!this.mimicing){
1440             this.wrap.addClass(this.wrapFocusClass);
1441             this.mimicing = true;
1442             this.doc.on('mousedown', this.mimicBlur, this, {delay: 10});
1443             if(this.monitorTab){
1444                 this.on('specialkey', this.checkTab, this);
1445             }
1446         }
1447     },
1448
1449     // private
1450     checkTab : function(me, e){
1451         if(e.getKey() == e.TAB){
1452             this.triggerBlur();
1453         }
1454     },
1455
1456     // private
1457     onBlur : Ext.emptyFn,
1458
1459     // private
1460     mimicBlur : function(e){
1461         if(!this.isDestroyed && !this.wrap.contains(e.target) && this.validateBlur(e)){
1462             this.triggerBlur();
1463         }
1464     },
1465
1466     // private
1467     triggerBlur : function(){
1468         this.mimicing = false;
1469         this.doc.un('mousedown', this.mimicBlur, this);
1470         if(this.monitorTab && this.el){
1471             this.un('specialkey', this.checkTab, this);
1472         }
1473         Ext.form.TriggerField.superclass.onBlur.call(this);
1474         if(this.wrap){
1475             this.wrap.removeClass(this.wrapFocusClass);
1476         }
1477     },
1478
1479     beforeBlur : Ext.emptyFn,
1480
1481     // private
1482     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
1483     validateBlur : function(e){
1484         return true;
1485     },
1486
1487     /**
1488      * The function that should handle the trigger's click event.  This method does nothing by default
1489      * until overridden by an implementing function.  See Ext.form.ComboBox and Ext.form.DateField for
1490      * sample implementations.
1491      * @method
1492      * @param {EventObject} e
1493      */
1494     onTriggerClick : Ext.emptyFn
1495
1496     /**
1497      * @cfg {Boolean} grow @hide
1498      */
1499     /**
1500      * @cfg {Number} growMin @hide
1501      */
1502     /**
1503      * @cfg {Number} growMax @hide
1504      */
1505 });
1506
1507 /**
1508  * @class Ext.form.TwinTriggerField
1509  * @extends Ext.form.TriggerField
1510  * TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
1511  * to be extended by an implementing class.  For an example of implementing this class, see the custom
1512  * SearchField implementation here:
1513  * <a href="http://extjs.com/deploy/ext/examples/form/custom.html">http://extjs.com/deploy/ext/examples/form/custom.html</a>
1514  */
1515 Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
1516     /**
1517      * @cfg {Mixed} triggerConfig
1518      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements
1519      * for this Field. (Optional).</p>
1520      * <p>Specify this when you need a customized element to contain the two trigger elements for this Field.
1521      * Each trigger element must be marked by the CSS class <tt>x-form-trigger</tt> (also see
1522      * <tt>{@link #trigger1Class}</tt> and <tt>{@link #trigger2Class}</tt>).</p>
1523      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing,
1524      * positioning and appearance of the triggers.</p>
1525      */
1526     /**
1527      * @cfg {String} trigger1Class
1528      * An additional CSS class used to style the trigger button.  The trigger will always get the
1529      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
1530      */
1531     /**
1532      * @cfg {String} trigger2Class
1533      * An additional CSS class used to style the trigger button.  The trigger will always get the
1534      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
1535      */
1536
1537     initComponent : function(){
1538         Ext.form.TwinTriggerField.superclass.initComponent.call(this);
1539
1540         this.triggerConfig = {
1541             tag:'span', cls:'x-form-twin-triggers', cn:[
1542             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
1543             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
1544         ]};
1545     },
1546
1547     getTrigger : function(index){
1548         return this.triggers[index];
1549     },
1550
1551     initTrigger : function(){
1552         var ts = this.trigger.select('.x-form-trigger', true);
1553         var triggerField = this;
1554         ts.each(function(t, all, index){
1555             var triggerIndex = 'Trigger'+(index+1);
1556             t.hide = function(){
1557                 var w = triggerField.wrap.getWidth();
1558                 this.dom.style.display = 'none';
1559                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
1560                 this['hidden' + triggerIndex] = true;
1561             };
1562             t.show = function(){
1563                 var w = triggerField.wrap.getWidth();
1564                 this.dom.style.display = '';
1565                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
1566                 this['hidden' + triggerIndex] = false;
1567             };
1568
1569             if(this['hide'+triggerIndex]){
1570                 t.dom.style.display = 'none';
1571                 this['hidden' + triggerIndex] = true;
1572             }
1573             this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true});
1574             t.addClassOnOver('x-form-trigger-over');
1575             t.addClassOnClick('x-form-trigger-click');
1576         }, this);
1577         this.triggers = ts.elements;
1578     },
1579
1580     getTriggerWidth: function(){
1581         var tw = 0;
1582         Ext.each(this.triggers, function(t, index){
1583             var triggerIndex = 'Trigger' + (index + 1),
1584                 w = t.getWidth();
1585             if(w === 0 && !this['hidden' + triggerIndex]){
1586                 tw += this.defaultTriggerWidth;
1587             }else{
1588                 tw += w;
1589             }
1590         }, this);
1591         return tw;
1592     },
1593
1594     // private
1595     onDestroy : function() {
1596         Ext.destroy(this.triggers);
1597         Ext.form.TwinTriggerField.superclass.onDestroy.call(this);
1598     },
1599
1600     /**
1601      * The function that should handle the trigger's click event.  This method does nothing by default
1602      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
1603      * for additional information.
1604      * @method
1605      * @param {EventObject} e
1606      */
1607     onTrigger1Click : Ext.emptyFn,
1608     /**
1609      * The function that should handle the trigger's click event.  This method does nothing by default
1610      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
1611      * for additional information.
1612      * @method
1613      * @param {EventObject} e
1614      */
1615     onTrigger2Click : Ext.emptyFn
1616 });
1617 Ext.reg('trigger', Ext.form.TriggerField);
1618 /**
1619  * @class Ext.form.TextArea
1620  * @extends Ext.form.TextField
1621  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
1622  * support for auto-sizing.
1623  * @constructor
1624  * Creates a new TextArea
1625  * @param {Object} config Configuration options
1626  * @xtype textarea
1627  */
1628 Ext.form.TextArea = Ext.extend(Ext.form.TextField,  {
1629     /**
1630      * @cfg {Number} growMin The minimum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
1631      * (defaults to <tt>60</tt>)
1632      */
1633     growMin : 60,
1634     /**
1635      * @cfg {Number} growMax The maximum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
1636      * (defaults to <tt>1000</tt>)
1637      */
1638     growMax: 1000,
1639     growAppend : '&#160;\n&#160;',
1640
1641     enterIsSpecial : false,
1642
1643     /**
1644      * @cfg {Boolean} preventScrollbars <tt>true</tt> to prevent scrollbars from appearing regardless of how much text is
1645      * in the field. This option is only relevant when {@link #grow} is <tt>true</tt>. Equivalent to setting overflow: hidden, defaults to 
1646      * <tt>false</tt>.
1647      */
1648     preventScrollbars: false,
1649     /**
1650      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
1651      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
1652      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
1653      * <pre><code>{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}</code></pre>
1654      */
1655
1656     // private
1657     onRender : function(ct, position){
1658         if(!this.el){
1659             this.defaultAutoCreate = {
1660                 tag: "textarea",
1661                 style:"width:100px;height:60px;",
1662                 autocomplete: "off"
1663             };
1664         }
1665         Ext.form.TextArea.superclass.onRender.call(this, ct, position);
1666         if(this.grow){
1667             this.textSizeEl = Ext.DomHelper.append(document.body, {
1668                 tag: "pre", cls: "x-form-grow-sizer"
1669             });
1670             if(this.preventScrollbars){
1671                 this.el.setStyle("overflow", "hidden");
1672             }
1673             this.el.setHeight(this.growMin);
1674         }
1675     },
1676
1677     onDestroy : function(){
1678         Ext.removeNode(this.textSizeEl);
1679         Ext.form.TextArea.superclass.onDestroy.call(this);
1680     },
1681
1682     fireKey : function(e){
1683         if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
1684             this.fireEvent("specialkey", this, e);
1685         }
1686     },
1687     
1688     // private
1689     doAutoSize : function(e){
1690         return !e.isNavKeyPress() || e.getKey() == e.ENTER;
1691     },
1692
1693     /**
1694      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
1695      * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
1696      */
1697     autoSize: function(){
1698         if(!this.grow || !this.textSizeEl){
1699             return;
1700         }
1701         var el = this.el,
1702             v = Ext.util.Format.htmlEncode(el.dom.value),
1703             ts = this.textSizeEl,
1704             h;
1705             
1706         Ext.fly(ts).setWidth(this.el.getWidth());
1707         if(v.length < 1){
1708             v = "&#160;&#160;";
1709         }else{
1710             v += this.growAppend;
1711             if(Ext.isIE){
1712                 v = v.replace(/\n/g, '&#160;<br />');
1713             }
1714         }
1715         ts.innerHTML = v;
1716         h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
1717         if(h != this.lastHeight){
1718             this.lastHeight = h;
1719             this.el.setHeight(h);
1720             this.fireEvent("autosize", this, h);
1721         }
1722     }
1723 });
1724 Ext.reg('textarea', Ext.form.TextArea);/**
1725  * @class Ext.form.NumberField
1726  * @extends Ext.form.TextField
1727  * Numeric text field that provides automatic keystroke filtering and numeric validation.
1728  * @constructor
1729  * Creates a new NumberField
1730  * @param {Object} config Configuration options
1731  * @xtype numberfield
1732  */
1733 Ext.form.NumberField = Ext.extend(Ext.form.TextField,  {
1734     /**
1735      * @cfg {RegExp} stripCharsRe @hide
1736      */
1737     /**
1738      * @cfg {RegExp} maskRe @hide
1739      */
1740     /**
1741      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
1742      */
1743     fieldClass: "x-form-field x-form-num-field",
1744     /**
1745      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
1746      */
1747     allowDecimals : true,
1748     /**
1749      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
1750      */
1751     decimalSeparator : ".",
1752     /**
1753      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
1754      */
1755     decimalPrecision : 2,
1756     /**
1757      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
1758      */
1759     allowNegative : true,
1760     /**
1761      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
1762      */
1763     minValue : Number.NEGATIVE_INFINITY,
1764     /**
1765      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
1766      */
1767     maxValue : Number.MAX_VALUE,
1768     /**
1769      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
1770      */
1771     minText : "The minimum value for this field is {0}",
1772     /**
1773      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
1774      */
1775     maxText : "The maximum value for this field is {0}",
1776     /**
1777      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
1778      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
1779      */
1780     nanText : "{0} is not a valid number",
1781     /**
1782      * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
1783      */
1784     baseChars : "0123456789",
1785
1786     // private
1787     initEvents : function(){
1788         var allowed = this.baseChars + '';
1789         if (this.allowDecimals) {
1790             allowed += this.decimalSeparator;
1791         }
1792         if (this.allowNegative) {
1793             allowed += '-';
1794         }
1795         this.maskRe = new RegExp('[' + Ext.escapeRe(allowed) + ']');
1796         Ext.form.NumberField.superclass.initEvents.call(this);
1797     },
1798
1799     // private
1800     validateValue : function(value){
1801         if(!Ext.form.NumberField.superclass.validateValue.call(this, value)){
1802             return false;
1803         }
1804         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
1805              return true;
1806         }
1807         value = String(value).replace(this.decimalSeparator, ".");
1808         if(isNaN(value)){
1809             this.markInvalid(String.format(this.nanText, value));
1810             return false;
1811         }
1812         var num = this.parseValue(value);
1813         if(num < this.minValue){
1814             this.markInvalid(String.format(this.minText, this.minValue));
1815             return false;
1816         }
1817         if(num > this.maxValue){
1818             this.markInvalid(String.format(this.maxText, this.maxValue));
1819             return false;
1820         }
1821         return true;
1822     },
1823
1824     getValue : function(){
1825         return this.fixPrecision(this.parseValue(Ext.form.NumberField.superclass.getValue.call(this)));
1826     },
1827
1828     setValue : function(v){
1829         v = Ext.isNumber(v) ? v : parseFloat(String(v).replace(this.decimalSeparator, "."));
1830         v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
1831         return Ext.form.NumberField.superclass.setValue.call(this, v);
1832     },
1833     
1834     /**
1835      * Replaces any existing {@link #minValue} with the new value.
1836      * @param {Number} value The minimum value
1837      */
1838     setMinValue : function(value){
1839         this.minValue = Ext.num(value, Number.NEGATIVE_INFINITY);
1840     },
1841     
1842     /**
1843      * Replaces any existing {@link #maxValue} with the new value.
1844      * @param {Number} value The maximum value
1845      */
1846     setMaxValue : function(value){
1847         this.maxValue = Ext.num(value, Number.MAX_VALUE);    
1848     },
1849
1850     // private
1851     parseValue : function(value){
1852         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
1853         return isNaN(value) ? '' : value;
1854     },
1855
1856     // private
1857     fixPrecision : function(value){
1858         var nan = isNaN(value);
1859         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
1860            return nan ? '' : value;
1861         }
1862         return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
1863     },
1864
1865     beforeBlur : function(){
1866         var v = this.parseValue(this.getRawValue());
1867         if(!Ext.isEmpty(v)){
1868             this.setValue(this.fixPrecision(v));
1869         }
1870     }
1871 });
1872 Ext.reg('numberfield', Ext.form.NumberField);/**
1873  * @class Ext.form.DateField
1874  * @extends Ext.form.TriggerField
1875  * Provides a date input field with a {@link Ext.DatePicker} dropdown and automatic date validation.
1876  * @constructor
1877  * Create a new DateField
1878  * @param {Object} config
1879  * @xtype datefield
1880  */
1881 Ext.form.DateField = Ext.extend(Ext.form.TriggerField,  {
1882     /**
1883      * @cfg {String} format
1884      * The default date format string which can be overriden for localization support.  The format must be
1885      * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/Y'</tt>).
1886      */
1887     format : "m/d/Y",
1888     /**
1889      * @cfg {String} altFormats
1890      * Multiple date formats separated by "<tt>|</tt>" to try when parsing a user input value and it
1891      * does not match the defined format (defaults to
1892      * <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>).
1893      */
1894     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",
1895     /**
1896      * @cfg {String} disabledDaysText
1897      * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)
1898      */
1899     disabledDaysText : "Disabled",
1900     /**
1901      * @cfg {String} disabledDatesText
1902      * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)
1903      */
1904     disabledDatesText : "Disabled",
1905     /**
1906      * @cfg {String} minText
1907      * The error text to display when the date in the cell is before <tt>{@link #minValue}</tt> (defaults to
1908      * <tt>'The date in this field must be after {minValue}'</tt>).
1909      */
1910     minText : "The date in this field must be equal to or after {0}",
1911     /**
1912      * @cfg {String} maxText
1913      * The error text to display when the date in the cell is after <tt>{@link #maxValue}</tt> (defaults to
1914      * <tt>'The date in this field must be before {maxValue}'</tt>).
1915      */
1916     maxText : "The date in this field must be equal to or before {0}",
1917     /**
1918      * @cfg {String} invalidText
1919      * The error text to display when the date in the field is invalid (defaults to
1920      * <tt>'{value} is not a valid date - it must be in the format {format}'</tt>).
1921      */
1922     invalidText : "{0} is not a valid date - it must be in the format {1}",
1923     /**
1924      * @cfg {String} triggerClass
1925      * An additional CSS class used to style the trigger button.  The trigger will always get the
1926      * class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
1927      * (defaults to <tt>'x-form-date-trigger'</tt> which displays a calendar icon).
1928      */
1929     triggerClass : 'x-form-date-trigger',
1930     /**
1931      * @cfg {Boolean} showToday
1932      * <tt>false</tt> to hide the footer area of the DatePicker containing the Today button and disable
1933      * the keyboard handler for spacebar that selects the current date (defaults to <tt>true</tt>).
1934      */
1935     showToday : true,
1936     /**
1937      * @cfg {Date/String} minValue
1938      * The minimum allowed date. Can be either a Javascript date object or a string date in a
1939      * valid format (defaults to null).
1940      */
1941     /**
1942      * @cfg {Date/String} maxValue
1943      * The maximum allowed date. Can be either a Javascript date object or a string date in a
1944      * valid format (defaults to null).
1945      */
1946     /**
1947      * @cfg {Array} disabledDays
1948      * An array of days to disable, 0 based (defaults to null). Some examples:<pre><code>
1949 // disable Sunday and Saturday:
1950 disabledDays:  [0, 6]
1951 // disable weekdays:
1952 disabledDays: [1,2,3,4,5]
1953      * </code></pre>
1954      */
1955     /**
1956      * @cfg {Array} disabledDates
1957      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
1958      * expression so they are very powerful. Some examples:<pre><code>
1959 // disable these exact dates:
1960 disabledDates: ["03/08/2003", "09/16/2003"]
1961 // disable these days for every year:
1962 disabledDates: ["03/08", "09/16"]
1963 // only match the beginning (useful if you are using short years):
1964 disabledDates: ["^03/08"]
1965 // disable every day in March 2006:
1966 disabledDates: ["03/../2006"]
1967 // disable every day in every March:
1968 disabledDates: ["^03"]
1969      * </code></pre>
1970      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
1971      * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
1972      * it, you will have to escape the dot when restricting dates. For example: <tt>["03\\.08\\.03"]</tt>.
1973      */
1974     /**
1975      * @cfg {String/Object} autoCreate
1976      * A {@link Ext.DomHelper DomHelper element specification object}, or <tt>true</tt> for the default element
1977      * specification object:<pre><code>
1978      * autoCreate: {tag: "input", type: "text", size: "10", autocomplete: "off"}
1979      * </code></pre>
1980      */
1981
1982     // private
1983     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
1984
1985     initComponent : function(){
1986         Ext.form.DateField.superclass.initComponent.call(this);
1987
1988         this.addEvents(
1989             /**
1990              * @event select
1991              * Fires when a date is selected via the date picker.
1992              * @param {Ext.form.DateField} this
1993              * @param {Date} date The date that was selected
1994              */
1995             'select'
1996         );
1997
1998         if(Ext.isString(this.minValue)){
1999             this.minValue = this.parseDate(this.minValue);
2000         }
2001         if(Ext.isString(this.maxValue)){
2002             this.maxValue = this.parseDate(this.maxValue);
2003         }
2004         this.disabledDatesRE = null;
2005         this.initDisabledDays();
2006     },
2007     
2008     initEvents: function() {
2009         Ext.form.DateField.superclass.initEvents.call(this);
2010         this.keyNav = new Ext.KeyNav(this.el, {
2011             "down": function(e) {
2012                 this.onTriggerClick();
2013             },
2014             scope: this,
2015             forceKeyDown: true
2016         });
2017     },
2018
2019
2020     // private
2021     initDisabledDays : function(){
2022         if(this.disabledDates){
2023             var dd = this.disabledDates,
2024                 len = dd.length - 1, 
2025                 re = "(?:";
2026                 
2027             Ext.each(dd, function(d, i){
2028                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
2029                 if(i != len){
2030                     re += '|';
2031                 }
2032             }, this);
2033             this.disabledDatesRE = new RegExp(re + ')');
2034         }
2035     },
2036
2037     /**
2038      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
2039      * @param {Array} disabledDates An array of date strings (see the <tt>{@link #disabledDates}</tt> config
2040      * for details on supported values) used to disable a pattern of dates.
2041      */
2042     setDisabledDates : function(dd){
2043         this.disabledDates = dd;
2044         this.initDisabledDays();
2045         if(this.menu){
2046             this.menu.picker.setDisabledDates(this.disabledDatesRE);
2047         }
2048     },
2049
2050     /**
2051      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
2052      * @param {Array} disabledDays An array of disabled day indexes. See the <tt>{@link #disabledDays}</tt>
2053      * config for details on supported values.
2054      */
2055     setDisabledDays : function(dd){
2056         this.disabledDays = dd;
2057         if(this.menu){
2058             this.menu.picker.setDisabledDays(dd);
2059         }
2060     },
2061
2062     /**
2063      * Replaces any existing <tt>{@link #minValue}</tt> with the new value and refreshes the DatePicker.
2064      * @param {Date} value The minimum date that can be selected
2065      */
2066     setMinValue : function(dt){
2067         this.minValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
2068         if(this.menu){
2069             this.menu.picker.setMinDate(this.minValue);
2070         }
2071     },
2072
2073     /**
2074      * Replaces any existing <tt>{@link #maxValue}</tt> with the new value and refreshes the DatePicker.
2075      * @param {Date} value The maximum date that can be selected
2076      */
2077     setMaxValue : function(dt){
2078         this.maxValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
2079         if(this.menu){
2080             this.menu.picker.setMaxDate(this.maxValue);
2081         }
2082     },
2083
2084     // private
2085     validateValue : function(value){
2086         value = this.formatDate(value);
2087         if(!Ext.form.DateField.superclass.validateValue.call(this, value)){
2088             return false;
2089         }
2090         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
2091              return true;
2092         }
2093         var svalue = value;
2094         value = this.parseDate(value);
2095         if(!value){
2096             this.markInvalid(String.format(this.invalidText, svalue, this.format));
2097             return false;
2098         }
2099         var time = value.getTime();
2100         if(this.minValue && time < this.minValue.getTime()){
2101             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
2102             return false;
2103         }
2104         if(this.maxValue && time > this.maxValue.getTime()){
2105             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
2106             return false;
2107         }
2108         if(this.disabledDays){
2109             var day = value.getDay();
2110             for(var i = 0; i < this.disabledDays.length; i++) {
2111                 if(day === this.disabledDays[i]){
2112                     this.markInvalid(this.disabledDaysText);
2113                     return false;
2114                 }
2115             }
2116         }
2117         var fvalue = this.formatDate(value);
2118         if(this.disabledDatesRE && this.disabledDatesRE.test(fvalue)){
2119             this.markInvalid(String.format(this.disabledDatesText, fvalue));
2120             return false;
2121         }
2122         return true;
2123     },
2124
2125     // private
2126     // Provides logic to override the default TriggerField.validateBlur which just returns true
2127     validateBlur : function(){
2128         return !this.menu || !this.menu.isVisible();
2129     },
2130
2131     /**
2132      * Returns the current date value of the date field.
2133      * @return {Date} The date value
2134      */
2135     getValue : function(){
2136         return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";
2137     },
2138
2139     /**
2140      * Sets the value of the date field.  You can pass a date object or any string that can be
2141      * parsed into a valid date, using <tt>{@link #format}</tt> as the date format, according
2142      * to the same rules as {@link Date#parseDate} (the default format used is <tt>"m/d/Y"</tt>).
2143      * <br />Usage:
2144      * <pre><code>
2145 //All of these calls set the same date value (May 4, 2006)
2146
2147 //Pass a date object:
2148 var dt = new Date('5/4/2006');
2149 dateField.setValue(dt);
2150
2151 //Pass a date string (default format):
2152 dateField.setValue('05/04/2006');
2153
2154 //Pass a date string (custom format):
2155 dateField.format = 'Y-m-d';
2156 dateField.setValue('2006-05-04');
2157 </code></pre>
2158      * @param {String/Date} date The date or valid date string
2159      * @return {Ext.form.Field} this
2160      */
2161     setValue : function(date){
2162         return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
2163     },
2164
2165     // private
2166     parseDate : function(value){
2167         if(!value || Ext.isDate(value)){
2168             return value;
2169         }
2170         var v = Date.parseDate(value, this.format);
2171         if(!v && this.altFormats){
2172             if(!this.altFormatsArray){
2173                 this.altFormatsArray = this.altFormats.split("|");
2174             }
2175             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
2176                 v = Date.parseDate(value, this.altFormatsArray[i]);
2177             }
2178         }
2179         return v;
2180     },
2181
2182     // private
2183     onDestroy : function(){
2184                 Ext.destroy(this.menu, this.keyNav);
2185         Ext.form.DateField.superclass.onDestroy.call(this);
2186     },
2187
2188     // private
2189     formatDate : function(date){
2190         return Ext.isDate(date) ? date.dateFormat(this.format) : date;
2191     },
2192
2193     /**
2194      * @method onTriggerClick
2195      * @hide
2196      */
2197     // private
2198     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
2199     onTriggerClick : function(){
2200         if(this.disabled){
2201             return;
2202         }
2203         if(this.menu == null){
2204             this.menu = new Ext.menu.DateMenu({
2205                 hideOnClick: false,
2206                 focusOnSelect: false
2207             });
2208         }
2209         this.onFocus();
2210         Ext.apply(this.menu.picker,  {
2211             minDate : this.minValue,
2212             maxDate : this.maxValue,
2213             disabledDatesRE : this.disabledDatesRE,
2214             disabledDatesText : this.disabledDatesText,
2215             disabledDays : this.disabledDays,
2216             disabledDaysText : this.disabledDaysText,
2217             format : this.format,
2218             showToday : this.showToday,
2219             minText : String.format(this.minText, this.formatDate(this.minValue)),
2220             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
2221         });
2222         this.menu.picker.setValue(this.getValue() || new Date());
2223         this.menu.show(this.el, "tl-bl?");
2224         this.menuEvents('on');
2225     },
2226     
2227     //private
2228     menuEvents: function(method){
2229         this.menu[method]('select', this.onSelect, this);
2230         this.menu[method]('hide', this.onMenuHide, this);
2231         this.menu[method]('show', this.onFocus, this);
2232     },
2233     
2234     onSelect: function(m, d){
2235         this.setValue(d);
2236         this.fireEvent('select', this, d);
2237         this.menu.hide();
2238     },
2239     
2240     onMenuHide: function(){
2241         this.focus(false, 60);
2242         this.menuEvents('un');
2243     },
2244
2245     // private
2246     beforeBlur : function(){
2247         var v = this.parseDate(this.getRawValue());
2248         if(v){
2249             this.setValue(v);
2250         }
2251     }
2252
2253     /**
2254      * @cfg {Boolean} grow @hide
2255      */
2256     /**
2257      * @cfg {Number} growMin @hide
2258      */
2259     /**
2260      * @cfg {Number} growMax @hide
2261      */
2262     /**
2263      * @hide
2264      * @method autoSize
2265      */
2266 });
2267 Ext.reg('datefield', Ext.form.DateField);/**\r
2268  * @class Ext.form.DisplayField\r
2269  * @extends Ext.form.Field\r
2270  * A display-only text field which is not validated and not submitted.\r
2271  * @constructor\r
2272  * Creates a new DisplayField.\r
2273  * @param {Object} config Configuration options\r
2274  * @xtype displayfield\r
2275  */\r
2276 Ext.form.DisplayField = Ext.extend(Ext.form.Field,  {\r
2277     validationEvent : false,\r
2278     validateOnBlur : false,\r
2279     defaultAutoCreate : {tag: "div"},\r
2280     /**\r
2281      * @cfg {String} fieldClass The default CSS class for the field (defaults to <tt>"x-form-display-field"</tt>)\r
2282      */\r
2283     fieldClass : "x-form-display-field",\r
2284     /**\r
2285      * @cfg {Boolean} htmlEncode <tt>false</tt> to skip HTML-encoding the text when rendering it (defaults to\r
2286      * <tt>false</tt>). This might be useful if you want to include tags in the field's innerHTML rather than\r
2287      * rendering them as string literals per the default logic.\r
2288      */\r
2289     htmlEncode: false,\r
2290 \r
2291     // private\r
2292     initEvents : Ext.emptyFn,\r
2293 \r
2294     isValid : function(){\r
2295         return true;\r
2296     },\r
2297 \r
2298     validate : function(){\r
2299         return true;\r
2300     },\r
2301 \r
2302     getRawValue : function(){\r
2303         var v = this.rendered ? this.el.dom.innerHTML : Ext.value(this.value, '');\r
2304         if(v === this.emptyText){\r
2305             v = '';\r
2306         }\r
2307         if(this.htmlEncode){\r
2308             v = Ext.util.Format.htmlDecode(v);\r
2309         }\r
2310         return v;\r
2311     },\r
2312 \r
2313     getValue : function(){\r
2314         return this.getRawValue();\r
2315     },\r
2316     \r
2317     getName: function() {\r
2318         return this.name;\r
2319     },\r
2320 \r
2321     setRawValue : function(v){\r
2322         if(this.htmlEncode){\r
2323             v = Ext.util.Format.htmlEncode(v);\r
2324         }\r
2325         return this.rendered ? (this.el.dom.innerHTML = (Ext.isEmpty(v) ? '' : v)) : (this.value = v);\r
2326     },\r
2327 \r
2328     setValue : function(v){\r
2329         this.setRawValue(v);\r
2330         return this;\r
2331     }\r
2332     /** \r
2333      * @cfg {String} inputType \r
2334      * @hide\r
2335      */\r
2336     /** \r
2337      * @cfg {Boolean} disabled \r
2338      * @hide\r
2339      */\r
2340     /** \r
2341      * @cfg {Boolean} readOnly \r
2342      * @hide\r
2343      */\r
2344     /** \r
2345      * @cfg {Boolean} validateOnBlur \r
2346      * @hide\r
2347      */\r
2348     /** \r
2349      * @cfg {Number} validationDelay \r
2350      * @hide\r
2351      */\r
2352     /** \r
2353      * @cfg {String/Boolean} validationEvent \r
2354      * @hide\r
2355      */\r
2356 });\r
2357 \r
2358 Ext.reg('displayfield', Ext.form.DisplayField);\r
2359 /**
2360  * @class Ext.form.ComboBox
2361  * @extends Ext.form.TriggerField
2362  * <p>A combobox control with support for autocomplete, remote-loading, paging and many other features.</p>
2363  * <p>A ComboBox works in a similar manner to a traditional HTML &lt;select> field. The difference is
2364  * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input
2365  * field to hold the value of the valueField. The <i>{@link #displayField}</i> is shown in the text field
2366  * which is named according to the {@link #name}.</p>
2367  * <p><b><u>Events</u></b></p>
2368  * <p>To do something when something in ComboBox is selected, configure the select event:<pre><code>
2369 var cb = new Ext.form.ComboBox({
2370     // all of your config options
2371     listeners:{
2372          scope: yourScope,
2373          'select': yourFunction
2374     }
2375 });
2376
2377 // Alternatively, you can assign events after the object is created:
2378 var cb = new Ext.form.ComboBox(yourOptions);
2379 cb.on('select', yourFunction, yourScope);
2380  * </code></pre></p>
2381  *
2382  * <p><b><u>ComboBox in Grid</u></b></p>
2383  * <p>If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer}
2384  * will be needed to show the displayField when the editor is not active.  Set up the renderer manually, or implement
2385  * a reusable render, for example:<pre><code>
2386 // create reusable renderer
2387 Ext.util.Format.comboRenderer = function(combo){
2388     return function(value){
2389         var record = combo.findRecord(combo.{@link #valueField}, value);
2390         return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
2391     }
2392 }
2393
2394 // create the combo instance
2395 var combo = new Ext.form.ComboBox({
2396     {@link #typeAhead}: true,
2397     {@link #triggerAction}: 'all',
2398     {@link #lazyRender}:true,
2399     {@link #mode}: 'local',
2400     {@link #store}: new Ext.data.ArrayStore({
2401         id: 0,
2402         fields: [
2403             'myId',
2404             'displayText'
2405         ],
2406         data: [[1, 'item1'], [2, 'item2']]
2407     }),
2408     {@link #valueField}: 'myId',
2409     {@link #displayField}: 'displayText'
2410 });
2411
2412 // snippet of column model used within grid
2413 var cm = new Ext.grid.ColumnModel([{
2414        ...
2415     },{
2416        header: "Some Header",
2417        dataIndex: 'whatever',
2418        width: 130,
2419        editor: combo, // specify reference to combo instance
2420        renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
2421     },
2422     ...
2423 ]);
2424  * </code></pre></p>
2425  *
2426  * <p><b><u>Filtering</u></b></p>
2427  * <p>A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox
2428  * store manually see <tt>{@link #lastQuery}</tt>.</p>
2429  * @constructor
2430  * Create a new ComboBox.
2431  * @param {Object} config Configuration options
2432  * @xtype combo
2433  */
2434 Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, {
2435     /**
2436      * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox.
2437      * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or
2438      * {@link Ext.form.FormPanel}, you must also set <tt>{@link #lazyRender} = true</tt>.
2439      */
2440     /**
2441      * @cfg {Boolean} lazyRender <tt>true</tt> to prevent the ComboBox from rendering until requested
2442      * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}),
2443      * defaults to <tt>false</tt>).
2444      */
2445     /**
2446      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or <tt>true</tt> for a default
2447      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
2448      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
2449      * <pre><code>{tag: "input", type: "text", size: "24", autocomplete: "off"}</code></pre>
2450      */
2451     /**
2452      * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).
2453      * Acceptable values for this property are:
2454      * <div class="mdetail-params"><ul>
2455      * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
2456      * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally,
2457      * automatically generating {@link Ext.data.Field#name field names} to work with all data components.
2458      * <div class="mdetail-params"><ul>
2459      * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
2460      * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
2461      * {@link #valueField} and {@link #displayField})</div></li>
2462      * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
2463      * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
2464      * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
2465      * </div></li></ul></div></li></ul></div>
2466      * <p>See also <tt>{@link #mode}</tt>.</p>
2467      */
2468     /**
2469      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
2470      * the dropdown list (defaults to undefined, with no header element)
2471      */
2472
2473     // private
2474     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
2475     /**
2476      * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown
2477      * list (defaults to the width of the ComboBox field).  See also <tt>{@link #minListWidth}
2478      */
2479     /**
2480      * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
2481      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field1'</tt> if
2482      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
2483      * the store configuration}).
2484      * <p>See also <tt>{@link #valueField}</tt>.</p>
2485      * <p><b>Note</b>: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a
2486      * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not
2487      * active.</p>
2488      */
2489     /**
2490      * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this
2491      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field2'</tt> if
2492      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
2493      * the store configuration}).
2494      * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be
2495      * mapped.  See also <tt>{@link #hiddenName}</tt>, <tt>{@link #hiddenValue}</tt>, and <tt>{@link #displayField}</tt>.</p>
2496      */
2497     /**
2498      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
2499      * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically
2500      * post during a form submission.  See also {@link #valueField}.
2501      * <p><b>Note</b>: the hidden field's id will also default to this name if {@link #hiddenId} is not specified.
2502      * The ComboBox {@link Ext.Component#id id} and the <tt>{@link #hiddenId}</tt> <b>should be different</b>, since
2503      * no two DOM nodes should share the same id.  So, if the ComboBox <tt>{@link Ext.form.Field#name name}</tt> and
2504      * <tt>hiddenName</tt> are the same, you should specify a unique <tt>{@link #hiddenId}</tt>.</p>
2505      */
2506     /**
2507      * @cfg {String} hiddenId If <tt>{@link #hiddenName}</tt> is specified, <tt>hiddenId</tt> can also be provided
2508      * to give the hidden field a unique id (defaults to the <tt>{@link #hiddenName}</tt>).  The <tt>hiddenId</tt>
2509      * and combo {@link Ext.Component#id id} should be different, since no two DOM
2510      * nodes should share the same id.
2511      */
2512     /**
2513      * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is
2514      * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured
2515      * <tt>{@link Ext.form.Field#value value}</tt>.
2516      */
2517     /**
2518      * @cfg {String} listClass The CSS class to add to the predefined <tt>'x-combo-list'</tt> class
2519      * applied the dropdown list element (defaults to '').
2520      */
2521     listClass : '',
2522     /**
2523      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list
2524      * (defaults to <tt>'x-combo-selected'</tt>)
2525      */
2526     selectedClass : 'x-combo-selected',
2527     /**
2528      * @cfg {String} listEmptyText The empty text to display in the data view if no items are found.
2529      * (defaults to '')
2530      */
2531     listEmptyText: '',
2532     /**
2533      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always
2534      * get the class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
2535      * (defaults to <tt>'x-form-arrow-trigger'</tt> which displays a downward arrow icon).
2536      */
2537     triggerClass : 'x-form-arrow-trigger',
2538     /**
2539      * @cfg {Boolean/String} shadow <tt>true</tt> or <tt>"sides"</tt> for the default effect, <tt>"frame"</tt> for
2540      * 4-way shadow, and <tt>"drop"</tt> for bottom-right
2541      */
2542     shadow : 'sides',
2543     /**
2544      * @cfg {String/Array} listAlign A valid anchor position value. See <tt>{@link Ext.Element#alignTo}</tt> for details
2545      * on supported anchor positions and offsets. To specify x/y offsets as well, this value
2546      * may be specified as an Array of <tt>{@link Ext.Element#alignTo}</tt> method arguments.</p>
2547      * <pre><code>[ 'tl-bl?', [6,0] ]</code></pre>(defaults to <tt>'tl-bl?'</tt>)
2548      */
2549     listAlign : 'tl-bl?',
2550     /**
2551      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown
2552      * (defaults to <tt>300</tt>)
2553      */
2554     maxHeight : 300,
2555     /**
2556      * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its
2557      * distance to the viewport edges (defaults to <tt>90</tt>)
2558      */
2559     minHeight : 90,
2560     /**
2561      * @cfg {String} triggerAction The action to execute when the trigger is clicked.
2562      * <div class="mdetail-params"><ul>
2563      * <li><b><tt>'query'</tt></b> : <b>Default</b>
2564      * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.</p></li>
2565      * <li><b><tt>'all'</tt></b> :
2566      * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>
2567      * </ul></div>
2568      * <p>See also <code>{@link #queryParam}</code>.</p>
2569      */
2570     triggerAction : 'query',
2571     /**
2572      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
2573      * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #mode} = 'remote'</tt> or <tt>0</tt> if
2574      * <tt>{@link #mode} = 'local'</tt>, does not apply if
2575      * <tt>{@link Ext.form.TriggerField#editable editable} = false</tt>).
2576      */
2577     minChars : 4,
2578     /**
2579      * @cfg {Boolean} autoSelect <tt>true</tt> to select the first result gathered by the data store (defaults
2580      * to <tt>true</tt>).  A false value would require a manual selection from the dropdown list to set the components value
2581      * unless the value of ({@link #typeAheadDelay}) were true.
2582      */
2583     autoSelect : true,
2584     /**
2585      * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being
2586      * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
2587      * to <tt>false</tt>)
2588      */
2589     typeAhead : false,
2590     /**
2591      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
2592      * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #mode} = 'remote'</tt>
2593      * or <tt>10</tt> if <tt>{@link #mode} = 'local'</tt>)
2594      */
2595     queryDelay : 500,
2596     /**
2597      * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.PagingToolbar} is displayed in the
2598      * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
2599      * {@link Ext.PagingToolbar#pageSize limit} parameters. Only applies when <tt>{@link #mode} = 'remote'</tt>
2600      * (defaults to <tt>0</tt>).
2601      */
2602     pageSize : 0,
2603     /**
2604      * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.
2605      * Only applies when <tt>{@link Ext.form.TriggerField#editable editable} = true</tt> (defaults to
2606      * <tt>false</tt>).
2607      */
2608     selectOnFocus : false,
2609     /**
2610      * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)
2611      * as it will be passed on the querystring (defaults to <tt>'query'</tt>)
2612      */
2613     queryParam : 'query',
2614     /**
2615      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
2616      * when <tt>{@link #mode} = 'remote'</tt> (defaults to <tt>'Loading...'</tt>)
2617      */
2618     loadingText : 'Loading...',
2619     /**
2620      * @cfg {Boolean} resizable <tt>true</tt> to add a resize handle to the bottom of the dropdown list
2621      * (creates an {@link Ext.Resizable} with 'se' {@link Ext.Resizable#pinned pinned} handles).
2622      * Defaults to <tt>false</tt>.
2623      */
2624     resizable : false,
2625     /**
2626      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if
2627      * <tt>{@link #resizable} = true</tt> (defaults to <tt>8</tt>)
2628      */
2629     handleHeight : 8,
2630     /**
2631      * @cfg {String} allQuery The text query to send to the server to return all records for the list
2632      * with no filtering (defaults to '')
2633      */
2634     allQuery: '',
2635     /**
2636      * @cfg {String} mode Acceptable values are:
2637      * <div class="mdetail-params"><ul>
2638      * <li><b><tt>'remote'</tt></b> : <b>Default</b>
2639      * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger
2640      * is clicked. If you do not want the store to be automatically loaded the first time the trigger is
2641      * clicked, set to <tt>'local'</tt> and manually load the store.  To force a requery of the store
2642      * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>
2643      * <li><b><tt>'local'</tt></b> :
2644      * <p class="sub-desc">ComboBox loads local data</p>
2645      * <pre><code>
2646 var combo = new Ext.form.ComboBox({
2647     renderTo: document.body,
2648     mode: 'local',
2649     store: new Ext.data.ArrayStore({
2650         id: 0,
2651         fields: [
2652             'myId',  // numeric value is the key
2653             'displayText'
2654         ],
2655         data: [[1, 'item1'], [2, 'item2']]  // data is local
2656     }),
2657     valueField: 'myId',
2658     displayField: 'displayText',
2659     triggerAction: 'all'
2660 });
2661      * </code></pre></li>
2662      * </ul></div>
2663      */
2664     mode: 'remote',
2665     /**
2666      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to <tt>70</tt>, will
2667      * be ignored if <tt>{@link #listWidth}</tt> has a higher value)
2668      */
2669     minListWidth : 70,
2670     /**
2671      * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,
2672      * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)
2673      */
2674     forceSelection : false,
2675     /**
2676      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
2677      * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)
2678      */
2679     typeAheadDelay : 250,
2680     /**
2681      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
2682      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
2683      * default text is used, it means there is no value set and no validation will occur on this field.
2684      */
2685
2686     /**
2687      * @cfg {Boolean} lazyInit <tt>true</tt> to not initialize the list for this combo until the field is focused
2688      * (defaults to <tt>true</tt>)
2689      */
2690     lazyInit : true,
2691
2692     /**
2693      * @cfg {Boolean} clearFilterOnReset <tt>true</tt> to clear any filters on the store (when in local mode) when reset is called
2694      * (defaults to <tt>true</tt>)
2695      */
2696     clearFilterOnReset : true,
2697
2698     /**
2699      * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
2700      * If a hiddenName is specified, setting this to true will cause both the hidden field and the element to be submitted.
2701      * Defaults to <tt>undefined</tt>.
2702      */
2703     submitValue: undefined,
2704
2705     /**
2706      * The value of the match string used to filter the store. Delete this property to force a requery.
2707      * Example use:
2708      * <pre><code>
2709 var combo = new Ext.form.ComboBox({
2710     ...
2711     mode: 'remote',
2712     ...
2713     listeners: {
2714         // delete the previous query in the beforequery event or set
2715         // combo.lastQuery = null (this will reload the store the next time it expands)
2716         beforequery: function(qe){
2717             delete qe.combo.lastQuery;
2718         }
2719     }
2720 });
2721      * </code></pre>
2722      * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
2723      * configure the combo with <tt>lastQuery=''</tt>. Example use:
2724      * <pre><code>
2725 var combo = new Ext.form.ComboBox({
2726     ...
2727     mode: 'local',
2728     triggerAction: 'all',
2729     lastQuery: ''
2730 });
2731      * </code></pre>
2732      * @property lastQuery
2733      * @type String
2734      */
2735
2736     // private
2737     initComponent : function(){
2738         Ext.form.ComboBox.superclass.initComponent.call(this);
2739         this.addEvents(
2740             /**
2741              * @event expand
2742              * Fires when the dropdown list is expanded
2743              * @param {Ext.form.ComboBox} combo This combo box
2744              */
2745             'expand',
2746             /**
2747              * @event collapse
2748              * Fires when the dropdown list is collapsed
2749              * @param {Ext.form.ComboBox} combo This combo box
2750              */
2751             'collapse',
2752
2753             /**
2754              * @event beforeselect
2755              * Fires before a list item is selected. Return false to cancel the selection.
2756              * @param {Ext.form.ComboBox} combo This combo box
2757              * @param {Ext.data.Record} record The data record returned from the underlying store
2758              * @param {Number} index The index of the selected item in the dropdown list
2759              */
2760             'beforeselect',
2761             /**
2762              * @event select
2763              * Fires when a list item is selected
2764              * @param {Ext.form.ComboBox} combo This combo box
2765              * @param {Ext.data.Record} record The data record returned from the underlying store
2766              * @param {Number} index The index of the selected item in the dropdown list
2767              */
2768             'select',
2769             /**
2770              * @event beforequery
2771              * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
2772              * cancel property to true.
2773              * @param {Object} queryEvent An object that has these properties:<ul>
2774              * <li><code>combo</code> : Ext.form.ComboBox <div class="sub-desc">This combo box</div></li>
2775              * <li><code>query</code> : String <div class="sub-desc">The query</div></li>
2776              * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>
2777              * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>
2778              * </ul>
2779              */
2780             'beforequery'
2781         );
2782         if(this.transform){
2783             var s = Ext.getDom(this.transform);
2784             if(!this.hiddenName){
2785                 this.hiddenName = s.name;
2786             }
2787             if(!this.store){
2788                 this.mode = 'local';
2789                 var d = [], opts = s.options;
2790                 for(var i = 0, len = opts.length;i < len; i++){
2791                     var o = opts[i],
2792                         value = (o.hasAttribute ? o.hasAttribute('value') : o.getAttributeNode('value').specified) ? o.value : o.text;
2793                     if(o.selected && Ext.isEmpty(this.value, true)) {
2794                         this.value = value;
2795                     }
2796                     d.push([value, o.text]);
2797                 }
2798                 this.store = new Ext.data.ArrayStore({
2799                     'id': 0,
2800                     fields: ['value', 'text'],
2801                     data : d,
2802                     autoDestroy: true
2803                 });
2804                 this.valueField = 'value';
2805                 this.displayField = 'text';
2806             }
2807             s.name = Ext.id(); // wipe out the name in case somewhere else they have a reference
2808             if(!this.lazyRender){
2809                 this.target = true;
2810                 this.el = Ext.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
2811                 this.render(this.el.parentNode, s);
2812             }
2813             Ext.removeNode(s);
2814         }
2815         //auto-configure store from local array data
2816         else if(this.store){
2817             this.store = Ext.StoreMgr.lookup(this.store);
2818             if(this.store.autoCreated){
2819                 this.displayField = this.valueField = 'field1';
2820                 if(!this.store.expandData){
2821                     this.displayField = 'field2';
2822                 }
2823                 this.mode = 'local';
2824             }
2825         }
2826
2827         this.selectedIndex = -1;
2828         if(this.mode == 'local'){
2829             if(!Ext.isDefined(this.initialConfig.queryDelay)){
2830                 this.queryDelay = 10;
2831             }
2832             if(!Ext.isDefined(this.initialConfig.minChars)){
2833                 this.minChars = 0;
2834             }
2835         }
2836     },
2837
2838     // private
2839     onRender : function(ct, position){
2840         if(this.hiddenName && !Ext.isDefined(this.submitValue)){
2841             this.submitValue = false;
2842         }
2843         Ext.form.ComboBox.superclass.onRender.call(this, ct, position);
2844         if(this.hiddenName){
2845             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,
2846                     id: (this.hiddenId||this.hiddenName)}, 'before', true);
2847
2848         }
2849         if(Ext.isGecko){
2850             this.el.dom.setAttribute('autocomplete', 'off');
2851         }
2852
2853         if(!this.lazyInit){
2854             this.initList();
2855         }else{
2856             this.on('focus', this.initList, this, {single: true});
2857         }
2858     },
2859
2860     // private
2861     initValue : function(){
2862         Ext.form.ComboBox.superclass.initValue.call(this);
2863         if(this.hiddenField){
2864             this.hiddenField.value =
2865                 Ext.value(Ext.isDefined(this.hiddenValue) ? this.hiddenValue : this.value, '');
2866         }
2867     },
2868
2869     // private
2870     initList : function(){
2871         if(!this.list){
2872             var cls = 'x-combo-list',
2873                 listParent = Ext.getDom(this.getListParent() || Ext.getBody()),
2874                 zindex = parseInt(Ext.fly(listParent).getStyle('z-index') ,10);
2875
2876             if (this.ownerCt && !zindex){
2877                 this.findParentBy(function(ct){
2878                     zindex = parseInt(ct.getPositionEl().getStyle('z-index'), 10);
2879                     return !!zindex;
2880                 });
2881             }
2882
2883             this.list = new Ext.Layer({
2884                 parentEl: listParent,
2885                 shadow: this.shadow,
2886                 cls: [cls, this.listClass].join(' '),
2887                 constrain:false,
2888                 zindex: (zindex || 12000) + 5
2889             });
2890
2891             var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
2892             this.list.setSize(lw, 0);
2893             this.list.swallowEvent('mousewheel');
2894             this.assetHeight = 0;
2895             if(this.syncFont !== false){
2896                 this.list.setStyle('font-size', this.el.getStyle('font-size'));
2897             }
2898             if(this.title){
2899                 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
2900                 this.assetHeight += this.header.getHeight();
2901             }
2902
2903             this.innerList = this.list.createChild({cls:cls+'-inner'});
2904             this.mon(this.innerList, 'mouseover', this.onViewOver, this);
2905             this.mon(this.innerList, 'mousemove', this.onViewMove, this);
2906             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
2907
2908             if(this.pageSize){
2909                 this.footer = this.list.createChild({cls:cls+'-ft'});
2910                 this.pageTb = new Ext.PagingToolbar({
2911                     store: this.store,
2912                     pageSize: this.pageSize,
2913                     renderTo:this.footer
2914                 });
2915                 this.assetHeight += this.footer.getHeight();
2916             }
2917
2918             if(!this.tpl){
2919                 /**
2920                 * @cfg {String/Ext.XTemplate} tpl <p>The template string, or {@link Ext.XTemplate} instance to
2921                 * use to display each item in the dropdown list. The dropdown list is displayed in a
2922                 * DataView. See {@link #view}.</p>
2923                 * <p>The default template string is:</p><pre><code>
2924                   '&lt;tpl for=".">&lt;div class="x-combo-list-item">{' + this.displayField + '}&lt;/div>&lt;/tpl>'
2925                 * </code></pre>
2926                 * <p>Override the default value to create custom UI layouts for items in the list.
2927                 * For example:</p><pre><code>
2928                   '&lt;tpl for=".">&lt;div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}&lt;/div>&lt;/tpl>'
2929                 * </code></pre>
2930                 * <p>The template <b>must</b> contain one or more substitution parameters using field
2931                 * names from the Combo's</b> {@link #store Store}. In the example above an
2932                 * <pre>ext:qtip</pre> attribute is added to display other fields from the Store.</p>
2933                 * <p>To preserve the default visual look of list items, add the CSS class name
2934                 * <pre>x-combo-list-item</pre> to the template's container element.</p>
2935                 * <p>Also see {@link #itemSelector} for additional details.</p>
2936                 */
2937                 this.tpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
2938                 /**
2939                  * @cfg {String} itemSelector
2940                  * <p>A simple CSS selector (e.g. div.some-class or span:first-child) that will be
2941                  * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown
2942                  * display will be working with.</p>
2943                  * <p><b>Note</b>: this setting is <b>required</b> if a custom XTemplate has been
2944                  * specified in {@link #tpl} which assigns a class other than <pre>'x-combo-list-item'</pre>
2945                  * to dropdown list items</b>
2946                  */
2947             }
2948
2949             /**
2950             * The {@link Ext.DataView DataView} used to display the ComboBox's options.
2951             * @type Ext.DataView
2952             */
2953             this.view = new Ext.DataView({
2954                 applyTo: this.innerList,
2955                 tpl: this.tpl,
2956                 singleSelect: true,
2957                 selectedClass: this.selectedClass,
2958                 itemSelector: this.itemSelector || '.' + cls + '-item',
2959                 emptyText: this.listEmptyText,
2960                 deferEmptyText: false
2961             });
2962
2963             this.mon(this.view, {
2964                 containerclick : this.onViewClick,
2965                 click : this.onViewClick,
2966                 scope :this
2967             });
2968
2969             this.bindStore(this.store, true);
2970
2971             if(this.resizable){
2972                 this.resizer = new Ext.Resizable(this.list,  {
2973                    pinned:true, handles:'se'
2974                 });
2975                 this.mon(this.resizer, 'resize', function(r, w, h){
2976                     this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
2977                     this.listWidth = w;
2978                     this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
2979                     this.restrictHeight();
2980                 }, this);
2981
2982                 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
2983             }
2984         }
2985     },
2986
2987     /**
2988      * <p>Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.</p>
2989      * A custom implementation may be provided as a configuration option if the floating list needs to be rendered
2990      * to a different Element. An example might be rendering the list inside a Menu so that clicking
2991      * the list does not hide the Menu:<pre><code>
2992 var store = new Ext.data.ArrayStore({
2993     autoDestroy: true,
2994     fields: ['initials', 'fullname'],
2995     data : [
2996         ['FF', 'Fred Flintstone'],
2997         ['BR', 'Barney Rubble']
2998     ]
2999 });
3000
3001 var combo = new Ext.form.ComboBox({
3002     store: store,
3003     displayField: 'fullname',
3004     emptyText: 'Select a name...',
3005     forceSelection: true,
3006     getListParent: function() {
3007         return this.el.up('.x-menu');
3008     },
3009     iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
3010     mode: 'local',
3011     selectOnFocus: true,
3012     triggerAction: 'all',
3013     typeAhead: true,
3014     width: 135
3015 });
3016
3017 var menu = new Ext.menu.Menu({
3018     id: 'mainMenu',
3019     items: [
3020         combo // A Field in a Menu
3021     ]
3022 });
3023 </code></pre>
3024      */
3025     getListParent : function() {
3026         return document.body;
3027     },
3028
3029     /**
3030      * Returns the store associated with this combo.
3031      * @return {Ext.data.Store} The store
3032      */
3033     getStore : function(){
3034         return this.store;
3035     },
3036
3037     // private
3038     bindStore : function(store, initial){
3039         if(this.store && !initial){
3040             if(this.store !== store && this.store.autoDestroy){
3041                 this.store.destroy();
3042             }else{
3043                 this.store.un('beforeload', this.onBeforeLoad, this);
3044                 this.store.un('load', this.onLoad, this);
3045                 this.store.un('exception', this.collapse, this);
3046             }
3047             if(!store){
3048                 this.store = null;
3049                 if(this.view){
3050                     this.view.bindStore(null);
3051                 }
3052                 if(this.pageTb){
3053                     this.pageTb.bindStore(null);
3054                 }
3055             }
3056         }
3057         if(store){
3058             if(!initial) {
3059                 this.lastQuery = null;
3060                 if(this.pageTb) {
3061                     this.pageTb.bindStore(store);
3062                 }
3063             }
3064
3065             this.store = Ext.StoreMgr.lookup(store);
3066             this.store.on({
3067                 scope: this,
3068                 beforeload: this.onBeforeLoad,
3069                 load: this.onLoad,
3070                 exception: this.collapse
3071             });
3072
3073             if(this.view){
3074                 this.view.bindStore(store);
3075             }
3076         }
3077     },
3078
3079     reset : function(){
3080         Ext.form.ComboBox.superclass.reset.call(this);
3081         if(this.clearFilterOnReset && this.mode == 'local'){
3082             this.store.clearFilter();
3083         }
3084     },
3085
3086     // private
3087     initEvents : function(){
3088         Ext.form.ComboBox.superclass.initEvents.call(this);
3089
3090
3091         this.keyNav = new Ext.KeyNav(this.el, {
3092             "up" : function(e){
3093                 this.inKeyMode = true;
3094                 this.selectPrev();
3095             },
3096
3097             "down" : function(e){
3098                 if(!this.isExpanded()){
3099                     this.onTriggerClick();
3100                 }else{
3101                     this.inKeyMode = true;
3102                     this.selectNext();
3103                 }
3104             },
3105
3106             "enter" : function(e){
3107                 this.onViewClick();
3108             },
3109
3110             "esc" : function(e){
3111                 this.collapse();
3112             },
3113
3114             "tab" : function(e){
3115                 this.collapse();
3116                 return true;
3117             },
3118
3119             scope : this,
3120
3121             doRelay : function(e, h, hname){
3122                 if(hname == 'down' || this.scope.isExpanded()){
3123                     // this MUST be called before ComboBox#fireKey()
3124                     var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
3125                     if(!Ext.isIE && Ext.EventManager.useKeydown){
3126                         // call Combo#fireKey() for browsers which use keydown event (except IE)
3127                         this.scope.fireKey(e);
3128                     }
3129                     return relay;
3130                 }
3131                 return true;
3132             },
3133
3134             forceKeyDown : true,
3135             defaultEventAction: 'stopEvent'
3136         });
3137         this.queryDelay = Math.max(this.queryDelay || 10,
3138                 this.mode == 'local' ? 10 : 250);
3139         this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
3140         if(this.typeAhead){
3141             this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
3142         }
3143         if(!this.enableKeyEvents){
3144             this.mon(this.el, 'keyup', this.onKeyUp, this);
3145         }
3146     },
3147
3148
3149     // private
3150     onDestroy : function(){
3151         if (this.dqTask){
3152             this.dqTask.cancel();
3153             this.dqTask = null;
3154         }
3155         this.bindStore(null);
3156         Ext.destroy(
3157             this.resizer,
3158             this.view,
3159             this.pageTb,
3160             this.list
3161         );
3162         Ext.destroyMembers(this, 'hiddenField');
3163         Ext.form.ComboBox.superclass.onDestroy.call(this);
3164     },
3165
3166     // private
3167     fireKey : function(e){
3168         if (!this.isExpanded()) {
3169             Ext.form.ComboBox.superclass.fireKey.call(this, e);
3170         }
3171     },
3172
3173     // private
3174     onResize : function(w, h){
3175         Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
3176         if(this.isVisible() && this.list){
3177             this.doResize(w);
3178         }else{
3179             this.bufferSize = w;
3180         }
3181     },
3182
3183     doResize: function(w){
3184         if(!Ext.isDefined(this.listWidth)){
3185             var lw = Math.max(w, this.minListWidth);
3186             this.list.setWidth(lw);
3187             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
3188         }
3189     },
3190
3191     // private
3192     onEnable : function(){
3193         Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
3194         if(this.hiddenField){
3195             this.hiddenField.disabled = false;
3196         }
3197     },
3198
3199     // private
3200     onDisable : function(){
3201         Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
3202         if(this.hiddenField){
3203             this.hiddenField.disabled = true;
3204         }
3205     },
3206
3207     // private
3208     onBeforeLoad : function(){
3209         if(!this.hasFocus){
3210             return;
3211         }
3212         this.innerList.update(this.loadingText ?
3213                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
3214         this.restrictHeight();
3215         this.selectedIndex = -1;
3216     },
3217
3218     // private
3219     onLoad : function(){
3220         if(!this.hasFocus){
3221             return;
3222         }
3223         if(this.store.getCount() > 0 || this.listEmptyText){
3224             this.expand();
3225             this.restrictHeight();
3226             if(this.lastQuery == this.allQuery){
3227                 if(this.editable){
3228                     this.el.dom.select();
3229                 }
3230
3231                 if(this.autoSelect !== false && !this.selectByValue(this.value, true)){
3232                     this.select(0, true);
3233                 }
3234             }else{
3235                 if(this.autoSelect !== false){
3236                     this.selectNext();
3237                 }
3238                 if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){
3239                     this.taTask.delay(this.typeAheadDelay);
3240                 }
3241             }
3242         }else{
3243             this.collapse();
3244         }
3245
3246     },
3247
3248     // private
3249     onTypeAhead : function(){
3250         if(this.store.getCount() > 0){
3251             var r = this.store.getAt(0);
3252             var newValue = r.data[this.displayField];
3253             var len = newValue.length;
3254             var selStart = this.getRawValue().length;
3255             if(selStart != len){
3256                 this.setRawValue(newValue);
3257                 this.selectText(selStart, newValue.length);
3258             }
3259         }
3260     },
3261
3262     // private
3263     assertValue  : function(){
3264
3265         var val = this.getRawValue(),
3266             rec = this.findRecord(this.displayField, val);
3267
3268         if(!rec && this.forceSelection){
3269             if(val.length > 0 && val != this.emptyText){
3270                 this.el.dom.value = Ext.value(this.lastSelectionText, '');
3271                 this.applyEmptyText();
3272             }else{
3273                 this.clearValue();
3274             }
3275         }else{
3276             if(rec){
3277                 val = rec.get(this.valueField || this.displayField);
3278             }
3279             this.setValue(val);
3280         }
3281
3282     },
3283
3284     // private
3285     onSelect : function(record, index){
3286         if(this.fireEvent('beforeselect', this, record, index) !== false){
3287             this.setValue(record.data[this.valueField || this.displayField]);
3288             this.collapse();
3289             this.fireEvent('select', this, record, index);
3290         }
3291     },
3292
3293     // inherit docs
3294     getName: function(){
3295         var hf = this.hiddenField;
3296         return hf && hf.name ? hf.name : this.hiddenName || Ext.form.ComboBox.superclass.getName.call(this);
3297     },
3298
3299     /**
3300      * Returns the currently selected field value or empty string if no value is set.
3301      * @return {String} value The selected value
3302      */
3303     getValue : function(){
3304         if(this.valueField){
3305             return Ext.isDefined(this.value) ? this.value : '';
3306         }else{
3307             return Ext.form.ComboBox.superclass.getValue.call(this);
3308         }
3309     },
3310
3311     /**
3312      * Clears any text/value currently set in the field
3313      */
3314     clearValue : function(){
3315         if(this.hiddenField){
3316             this.hiddenField.value = '';
3317         }
3318         this.setRawValue('');
3319         this.lastSelectionText = '';
3320         this.applyEmptyText();
3321         this.value = '';
3322     },
3323
3324     /**
3325      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
3326      * will be displayed in the field.  If the value does not match the data value of an existing item,
3327      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
3328      * Otherwise the field will be blank (although the value will still be set).
3329      * @param {String} value The value to match
3330      * @return {Ext.form.Field} this
3331      */
3332     setValue : function(v){
3333         var text = v;
3334         if(this.valueField){
3335             var r = this.findRecord(this.valueField, v);
3336             if(r){
3337                 text = r.data[this.displayField];
3338             }else if(Ext.isDefined(this.valueNotFoundText)){
3339                 text = this.valueNotFoundText;
3340             }
3341         }
3342         this.lastSelectionText = text;
3343         if(this.hiddenField){
3344             this.hiddenField.value = Ext.value(v, '');
3345         }
3346         Ext.form.ComboBox.superclass.setValue.call(this, text);
3347         this.value = v;
3348         return this;
3349     },
3350
3351     // private
3352     findRecord : function(prop, value){
3353         var record;
3354         if(this.store.getCount() > 0){
3355             this.store.each(function(r){
3356                 if(r.data[prop] == value){
3357                     record = r;
3358                     return false;
3359                 }
3360             });
3361         }
3362         return record;
3363     },
3364
3365     // private
3366     onViewMove : function(e, t){
3367         this.inKeyMode = false;
3368     },
3369
3370     // private
3371     onViewOver : function(e, t){
3372         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
3373             return;
3374         }
3375         var item = this.view.findItemFromChild(t);
3376         if(item){
3377             var index = this.view.indexOf(item);
3378             this.select(index, false);
3379         }
3380     },
3381
3382     // private
3383     onViewClick : function(doFocus){
3384         var index = this.view.getSelectedIndexes()[0],
3385             s = this.store,
3386             r = s.getAt(index);
3387         if(r){
3388             this.onSelect(r, index);
3389         }else {
3390             this.collapse();
3391         }
3392         if(doFocus !== false){
3393             this.el.focus();
3394         }
3395     },
3396
3397
3398     // private
3399     restrictHeight : function(){
3400         this.innerList.dom.style.height = '';
3401         var inner = this.innerList.dom,
3402             pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,
3403             h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
3404             ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
3405             hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
3406             space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;
3407
3408         h = Math.min(h, space, this.maxHeight);
3409
3410         this.innerList.setHeight(h);
3411         this.list.beginUpdate();
3412         this.list.setHeight(h+pad);
3413         this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
3414         this.list.endUpdate();
3415     },
3416
3417     /**
3418      * Returns true if the dropdown list is expanded, else false.
3419      */
3420     isExpanded : function(){
3421         return this.list && this.list.isVisible();
3422     },
3423
3424     /**
3425      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
3426      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
3427      * @param {String} value The data value of the item to select
3428      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
3429      * selected item if it is not currently in view (defaults to true)
3430      * @return {Boolean} True if the value matched an item in the list, else false
3431      */
3432     selectByValue : function(v, scrollIntoView){
3433         if(!Ext.isEmpty(v, true)){
3434             var r = this.findRecord(this.valueField || this.displayField, v);
3435             if(r){
3436                 this.select(this.store.indexOf(r), scrollIntoView);
3437                 return true;
3438             }
3439         }
3440         return false;
3441     },
3442
3443     /**
3444      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
3445      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
3446      * @param {Number} index The zero-based index of the list item to select
3447      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
3448      * selected item if it is not currently in view (defaults to true)
3449      */
3450     select : function(index, scrollIntoView){
3451         this.selectedIndex = index;
3452         this.view.select(index);
3453         if(scrollIntoView !== false){
3454             var el = this.view.getNode(index);
3455             if(el){
3456                 this.innerList.scrollChildIntoView(el, false);
3457             }
3458         }
3459
3460     },
3461
3462     // private
3463     selectNext : function(){
3464         var ct = this.store.getCount();
3465         if(ct > 0){
3466             if(this.selectedIndex == -1){
3467                 this.select(0);
3468             }else if(this.selectedIndex < ct-1){
3469                 this.select(this.selectedIndex+1);
3470             }
3471         }
3472     },
3473
3474     // private
3475     selectPrev : function(){
3476         var ct = this.store.getCount();
3477         if(ct > 0){
3478             if(this.selectedIndex == -1){
3479                 this.select(0);
3480             }else if(this.selectedIndex !== 0){
3481                 this.select(this.selectedIndex-1);
3482             }
3483         }
3484     },
3485
3486     // private
3487     onKeyUp : function(e){
3488         var k = e.getKey();
3489         if(this.editable !== false && this.readOnly !== true && (k == e.BACKSPACE || !e.isSpecialKey())){
3490
3491             this.lastKey = k;
3492             this.dqTask.delay(this.queryDelay);
3493         }
3494         Ext.form.ComboBox.superclass.onKeyUp.call(this, e);
3495     },
3496
3497     // private
3498     validateBlur : function(){
3499         return !this.list || !this.list.isVisible();
3500     },
3501
3502     // private
3503     initQuery : function(){
3504         this.doQuery(this.getRawValue());
3505     },
3506
3507     // private
3508     beforeBlur : function(){
3509         this.assertValue();
3510     },
3511
3512     // private
3513     postBlur  : function(){
3514         Ext.form.ComboBox.superclass.postBlur.call(this);
3515         this.collapse();
3516         this.inKeyMode = false;
3517     },
3518
3519     /**
3520      * Execute a query to filter the dropdown list.  Fires the {@link #beforequery} event prior to performing the
3521      * query allowing the query action to be canceled if needed.
3522      * @param {String} query The SQL query to execute
3523      * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer
3524      * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option.  It
3525      * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)
3526      */
3527     doQuery : function(q, forceAll){
3528         q = Ext.isEmpty(q) ? '' : q;
3529         var qe = {
3530             query: q,
3531             forceAll: forceAll,
3532             combo: this,
3533             cancel:false
3534         };
3535         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
3536             return false;
3537         }
3538         q = qe.query;
3539         forceAll = qe.forceAll;
3540         if(forceAll === true || (q.length >= this.minChars)){
3541             if(this.lastQuery !== q){
3542                 this.lastQuery = q;
3543                 if(this.mode == 'local'){
3544                     this.selectedIndex = -1;
3545                     if(forceAll){
3546                         this.store.clearFilter();
3547                     }else{
3548                         this.store.filter(this.displayField, q);
3549                     }
3550                     this.onLoad();
3551                 }else{
3552                     this.store.baseParams[this.queryParam] = q;
3553                     this.store.load({
3554                         params: this.getParams(q)
3555                     });
3556                     this.expand();
3557                 }
3558             }else{
3559                 this.selectedIndex = -1;
3560                 this.onLoad();
3561             }
3562         }
3563     },
3564
3565     // private
3566     getParams : function(q){
3567         var p = {};
3568         //p[this.queryParam] = q;
3569         if(this.pageSize){
3570             p.start = 0;
3571             p.limit = this.pageSize;
3572         }
3573         return p;
3574     },
3575
3576     /**
3577      * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.
3578      */
3579     collapse : function(){
3580         if(!this.isExpanded()){
3581             return;
3582         }
3583         this.list.hide();
3584         Ext.getDoc().un('mousewheel', this.collapseIf, this);
3585         Ext.getDoc().un('mousedown', this.collapseIf, this);
3586         this.fireEvent('collapse', this);
3587     },
3588
3589     // private
3590     collapseIf : function(e){
3591         if(!e.within(this.wrap) && !e.within(this.list)){
3592             this.collapse();
3593         }
3594     },
3595
3596     /**
3597      * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.
3598      */
3599     expand : function(){
3600         if(this.isExpanded() || !this.hasFocus){
3601             return;
3602         }
3603         if(this.bufferSize){
3604             this.doResize(this.bufferSize);
3605             delete this.bufferSize;
3606         }
3607         this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
3608         this.list.show();
3609         if(Ext.isGecko2){
3610             this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
3611         }
3612         this.mon(Ext.getDoc(), {
3613             scope: this,
3614             mousewheel: this.collapseIf,
3615             mousedown: this.collapseIf
3616         });
3617         this.fireEvent('expand', this);
3618     },
3619
3620     /**
3621      * @method onTriggerClick
3622      * @hide
3623      */
3624     // private
3625     // Implements the default empty TriggerField.onTriggerClick function
3626     onTriggerClick : function(){
3627         if(this.readOnly || this.disabled){
3628             return;
3629         }
3630         if(this.isExpanded()){
3631             this.collapse();
3632             this.el.focus();
3633         }else {
3634             this.onFocus({});
3635             if(this.triggerAction == 'all') {
3636                 this.doQuery(this.allQuery, true);
3637             } else {
3638                 this.doQuery(this.getRawValue());
3639             }
3640             this.el.focus();
3641         }
3642     }
3643
3644     /**
3645      * @hide
3646      * @method autoSize
3647      */
3648     /**
3649      * @cfg {Boolean} grow @hide
3650      */
3651     /**
3652      * @cfg {Number} growMin @hide
3653      */
3654     /**
3655      * @cfg {Number} growMax @hide
3656      */
3657
3658 });
3659 Ext.reg('combo', Ext.form.ComboBox);
3660 /**
3661  * @class Ext.form.Checkbox
3662  * @extends Ext.form.Field
3663  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
3664  * @constructor
3665  * Creates a new Checkbox
3666  * @param {Object} config Configuration options
3667  * @xtype checkbox
3668  */
3669 Ext.form.Checkbox = Ext.extend(Ext.form.Field,  {
3670     /**
3671      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
3672      */
3673     focusClass : undefined,
3674     /**
3675      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to 'x-form-field')
3676      */
3677     fieldClass : 'x-form-field',
3678     /**
3679      * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
3680      */
3681     checked : false,
3682     /**
3683      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
3684      * {tag: 'input', type: 'checkbox', autocomplete: 'off'})
3685      */
3686     defaultAutoCreate : { tag: 'input', type: 'checkbox', autocomplete: 'off'},
3687     /**
3688      * @cfg {String} boxLabel The text that appears beside the checkbox
3689      */
3690     /**
3691      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
3692      */
3693     /**
3694      * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of 
3695      * handling the check event). The handler is passed the following parameters:
3696      * <div class="mdetail-params"><ul>
3697      * <li><b>checkbox</b> : Ext.form.Checkbox<div class="sub-desc">The Checkbox being toggled.</div></li>
3698      * <li><b>checked</b> : Boolean<div class="sub-desc">The new checked state of the checkbox.</div></li>
3699      * </ul></div>
3700      */
3701     /**
3702      * @cfg {Object} scope An object to use as the scope ('this' reference) of the {@link #handler} function
3703      * (defaults to this Checkbox).
3704      */
3705
3706     // private
3707     actionMode : 'wrap',
3708     
3709         // private
3710     initComponent : function(){
3711         Ext.form.Checkbox.superclass.initComponent.call(this);
3712         this.addEvents(
3713             /**
3714              * @event check
3715              * Fires when the checkbox is checked or unchecked.
3716              * @param {Ext.form.Checkbox} this This checkbox
3717              * @param {Boolean} checked The new checked value
3718              */
3719             'check'
3720         );
3721     },
3722
3723     // private
3724     onResize : function(){
3725         Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
3726         if(!this.boxLabel && !this.fieldLabel){
3727             this.el.alignTo(this.wrap, 'c-c');
3728         }
3729     },
3730
3731     // private
3732     initEvents : function(){
3733         Ext.form.Checkbox.superclass.initEvents.call(this);
3734         this.mon(this.el, {
3735             scope: this,
3736             click: this.onClick,
3737             change: this.onClick
3738         });
3739     },
3740
3741     /**
3742      * @hide
3743      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
3744      * @method
3745      */
3746     markInvalid : Ext.emptyFn,
3747     /**
3748      * @hide
3749      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
3750      * @method
3751      */
3752     clearInvalid : Ext.emptyFn,
3753
3754     // private
3755     onRender : function(ct, position){
3756         Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
3757         if(this.inputValue !== undefined){
3758             this.el.dom.value = this.inputValue;
3759         }
3760         this.wrap = this.el.wrap({cls: 'x-form-check-wrap'});
3761         if(this.boxLabel){
3762             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
3763         }
3764         if(this.checked){
3765             this.setValue(true);
3766         }else{
3767             this.checked = this.el.dom.checked;
3768         }
3769         // Need to repaint for IE, otherwise positioning is broken
3770         if(Ext.isIE){
3771             this.wrap.repaint();
3772         }
3773         this.resizeEl = this.positionEl = this.wrap;
3774     },
3775
3776     // private
3777     onDestroy : function(){
3778         Ext.destroy(this.wrap);
3779         Ext.form.Checkbox.superclass.onDestroy.call(this);
3780     },
3781
3782     // private
3783     initValue : function() {
3784         this.originalValue = this.getValue();
3785     },
3786
3787     /**
3788      * Returns the checked state of the checkbox.
3789      * @return {Boolean} True if checked, else false
3790      */
3791     getValue : function(){
3792         if(this.rendered){
3793             return this.el.dom.checked;
3794         }
3795         return this.checked;
3796     },
3797
3798         // private
3799     onClick : function(){
3800         if(this.el.dom.checked != this.checked){
3801             this.setValue(this.el.dom.checked);
3802         }
3803     },
3804
3805     /**
3806      * Sets the checked state of the checkbox, fires the 'check' event, and calls a
3807      * <code>{@link #handler}</code> (if configured).
3808      * @param {Boolean/String} checked The following values will check the checkbox:
3809      * <code>true, 'true', '1', or 'on'</code>. Any other value will uncheck the checkbox.
3810      * @return {Ext.form.Field} this
3811      */
3812     setValue : function(v){
3813         var checked = this.checked ;
3814         this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
3815         if(this.rendered){
3816             this.el.dom.checked = this.checked;
3817             this.el.dom.defaultChecked = this.checked;
3818         }
3819         if(checked != this.checked){
3820             this.fireEvent('check', this, this.checked);
3821             if(this.handler){
3822                 this.handler.call(this.scope || this, this, this.checked);
3823             }
3824         }
3825         return this;
3826     }
3827 });
3828 Ext.reg('checkbox', Ext.form.Checkbox);
3829 /**
3830  * @class Ext.form.CheckboxGroup
3831  * @extends Ext.form.Field
3832  * <p>A grouping container for {@link Ext.form.Checkbox} controls.</p>
3833  * <p>Sample usage:</p>
3834  * <pre><code>
3835 var myCheckboxGroup = new Ext.form.CheckboxGroup({
3836     id:'myGroup',
3837     xtype: 'checkboxgroup',
3838     fieldLabel: 'Single Column',
3839     itemCls: 'x-check-group-alt',
3840     // Put all controls in a single column with width 100%
3841     columns: 1,
3842     items: [
3843         {boxLabel: 'Item 1', name: 'cb-col-1'},
3844         {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
3845         {boxLabel: 'Item 3', name: 'cb-col-3'}
3846     ]
3847 });
3848  * </code></pre>
3849  * @constructor
3850  * Creates a new CheckboxGroup
3851  * @param {Object} config Configuration options
3852  * @xtype checkboxgroup
3853  */
3854 Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {
3855     /**
3856      * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects
3857      * to arrange in the group.
3858      */
3859     /**
3860      * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
3861      * checkbox/radio controls using automatic layout.  This config can take several types of values:
3862      * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width
3863      * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>
3864      * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be
3865      * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>
3866      * <li><b>Array</b> : Object<p class="sub-desc">You can also specify an array of column widths, mixing integer
3867      * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will
3868      * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float
3869      * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field
3870      * container you should do so.</p></li></ul>
3871      */
3872     columns : 'auto',
3873     /**
3874      * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column
3875      * top to bottom before starting on the next column.  The number of controls in each column will be automatically
3876      * calculated to keep columns as even as possible.  The default value is false, so that controls will be added
3877      * to columns one at a time, completely filling each row left to right before starting on the next row.
3878      */
3879     vertical : false,
3880     /**
3881      * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).
3882      * If no items are selected at validation time, {@link @blankText} will be used as the error text.
3883      */
3884     allowBlank : true,
3885     /**
3886      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must
3887      * select at least one item in this group")
3888      */
3889     blankText : "You must select at least one item in this group",
3890
3891     // private
3892     defaultType : 'checkbox',
3893
3894     // private
3895     groupCls : 'x-form-check-group',
3896
3897     // private
3898     initComponent: function(){
3899         this.addEvents(
3900             /**
3901              * @event change
3902              * Fires when the state of a child checkbox changes.
3903              * @param {Ext.form.CheckboxGroup} this
3904              * @param {Array} checked An array containing the checked boxes.
3905              */
3906             'change'
3907         );
3908         this.on('change', this.validate, this);
3909         Ext.form.CheckboxGroup.superclass.initComponent.call(this);
3910     },
3911
3912     // private
3913     onRender : function(ct, position){
3914         if(!this.el){
3915             var panelCfg = {
3916                 autoEl: {
3917                     id: this.id
3918                 },
3919                 cls: this.groupCls,
3920                 layout: 'column',
3921                 renderTo: ct,
3922                 bufferResize: false // Default this to false, since it doesn't really have a proper ownerCt.
3923             };
3924             var colCfg = {
3925                 xtype: 'container',
3926                 defaultType: this.defaultType,
3927                 layout: 'form',
3928                 defaults: {
3929                     hideLabel: true,
3930                     anchor: '100%'
3931                 }
3932             };
3933
3934             if(this.items[0].items){
3935
3936                 // The container has standard ColumnLayout configs, so pass them in directly
3937
3938                 Ext.apply(panelCfg, {
3939                     layoutConfig: {columns: this.items.length},
3940                     defaults: this.defaults,
3941                     items: this.items
3942                 });
3943                 for(var i=0, len=this.items.length; i<len; i++){
3944                     Ext.applyIf(this.items[i], colCfg);
3945                 }
3946
3947             }else{
3948
3949                 // The container has field item configs, so we have to generate the column
3950                 // panels first then move the items into the columns as needed.
3951
3952                 var numCols, cols = [];
3953
3954                 if(typeof this.columns == 'string'){ // 'auto' so create a col per item
3955                     this.columns = this.items.length;
3956                 }
3957                 if(!Ext.isArray(this.columns)){
3958                     var cs = [];
3959                     for(var i=0; i<this.columns; i++){
3960                         cs.push((100/this.columns)*.01); // distribute by even %
3961                     }
3962                     this.columns = cs;
3963                 }
3964
3965                 numCols = this.columns.length;
3966
3967                 // Generate the column configs with the correct width setting
3968                 for(var i=0; i<numCols; i++){
3969                     var cc = Ext.apply({items:[]}, colCfg);
3970                     cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = this.columns[i];
3971                     if(this.defaults){
3972                         cc.defaults = Ext.apply(cc.defaults || {}, this.defaults)
3973                     }
3974                     cols.push(cc);
3975                 };
3976
3977                 // Distribute the original items into the columns
3978                 if(this.vertical){
3979                     var rows = Math.ceil(this.items.length / numCols), ri = 0;
3980                     for(var i=0, len=this.items.length; i<len; i++){
3981                         if(i>0 && i%rows==0){
3982                             ri++;
3983                         }
3984                         if(this.items[i].fieldLabel){
3985                             this.items[i].hideLabel = false;
3986                         }
3987                         cols[ri].items.push(this.items[i]);
3988                     };
3989                 }else{
3990                     for(var i=0, len=this.items.length; i<len; i++){
3991                         var ci = i % numCols;
3992                         if(this.items[i].fieldLabel){
3993                             this.items[i].hideLabel = false;
3994                         }
3995                         cols[ci].items.push(this.items[i]);
3996                     };
3997                 }
3998
3999                 Ext.apply(panelCfg, {
4000                     layoutConfig: {columns: numCols},
4001                     items: cols
4002                 });
4003             }
4004
4005             this.panel = new Ext.Container(panelCfg);
4006             this.panel.ownerCt = this;
4007             this.el = this.panel.getEl();
4008
4009             if(this.forId && this.itemCls){
4010                 var l = this.el.up(this.itemCls).child('label', true);
4011                 if(l){
4012                     l.setAttribute('htmlFor', this.forId);
4013                 }
4014             }
4015
4016             var fields = this.panel.findBy(function(c){
4017                 return c.isFormField;
4018             }, this);
4019
4020             this.items = new Ext.util.MixedCollection();
4021             this.items.addAll(fields);
4022         }
4023         Ext.form.CheckboxGroup.superclass.onRender.call(this, ct, position);
4024     },
4025
4026     initValue : function(){
4027         if(this.value){
4028             this.setValue.apply(this, this.buffered ? this.value : [this.value]);
4029             delete this.buffered;
4030             delete this.value;
4031         }
4032     },
4033
4034     afterRender : function(){
4035         Ext.form.CheckboxGroup.superclass.afterRender.call(this);
4036         this.eachItem(function(item){
4037             item.on('check', this.fireChecked, this);
4038             item.inGroup = true;
4039         });
4040     },
4041
4042     // private
4043     doLayout: function(){
4044         //ugly method required to layout hidden items
4045         if(this.rendered){
4046             this.panel.forceLayout = this.ownerCt.forceLayout;
4047             this.panel.doLayout();
4048         }
4049     },
4050
4051     // private
4052     fireChecked: function(){
4053         var arr = [];
4054         this.eachItem(function(item){
4055             if(item.checked){
4056                 arr.push(item);
4057             }
4058         });
4059         this.fireEvent('change', this, arr);
4060     },
4061
4062     // private
4063     validateValue : function(value){
4064         if(!this.allowBlank){
4065             var blank = true;
4066             this.eachItem(function(f){
4067                 if(f.checked){
4068                     return (blank = false);
4069                 }
4070             });
4071             if(blank){
4072                 this.markInvalid(this.blankText);
4073                 return false;
4074             }
4075         }
4076         return true;
4077     },
4078
4079     // private
4080     isDirty: function(){
4081         //override the behaviour to check sub items.
4082         if (this.disabled || !this.rendered) {
4083             return false;
4084         }
4085
4086         var dirty = false;
4087         this.eachItem(function(item){
4088             if(item.isDirty()){
4089                 dirty = true;
4090                 return false;
4091             }
4092         });
4093         return dirty;
4094     },
4095
4096     // private
4097     onDisable : function(){
4098         this.eachItem(function(item){
4099             item.disable();
4100         });
4101     },
4102
4103     // private
4104     onEnable : function(){
4105         this.eachItem(function(item){
4106             item.enable();
4107         });
4108     },
4109
4110     // private
4111     doLayout: function(){
4112         if(this.rendered){
4113             this.panel.forceLayout = this.ownerCt.forceLayout;
4114             this.panel.doLayout();
4115         }
4116     },
4117
4118     // private
4119     onResize : function(w, h){
4120         this.panel.setSize(w, h);
4121         this.panel.doLayout();
4122     },
4123
4124     // inherit docs from Field
4125     reset : function(){
4126         this.eachItem(function(c){
4127             if(c.reset){
4128                 c.reset();
4129             }
4130         });
4131         // Defer the clearInvalid so if BaseForm's collection is being iterated it will be called AFTER it is complete.
4132         // Important because reset is being called on both the group and the individual items.
4133         (function() {
4134             this.clearInvalid();
4135         }).defer(50, this);
4136     },
4137
4138     /**
4139      * {@link Ext.form.Checkbox#setValue Set the value(s)} of an item or items
4140      * in the group. Examples illustrating how this method may be called:
4141      * <pre><code>
4142 // call with name and value
4143 myCheckboxGroup.setValue('cb-col-1', true);
4144 // call with an array of boolean values
4145 myCheckboxGroup.setValue([true, false, false]);
4146 // call with an object literal specifying item:value pairs
4147 myCheckboxGroup.setValue({
4148     'cb-col-2': false,
4149     'cb-col-3': true
4150 });
4151 // use comma separated string to set items with name to true (checked)
4152 myCheckboxGroup.setValue('cb-col-1,cb-col-3');
4153      * </code></pre>
4154      * See {@link Ext.form.Checkbox#setValue} for additional information.
4155      * @param {Mixed} id The checkbox to check, or as described by example shown.
4156      * @param {Boolean} value (optional) The value to set the item.
4157      * @return {Ext.form.CheckboxGroup} this
4158      */
4159     setValue: function(){
4160         if(this.rendered){
4161             this.onSetValue.apply(this, arguments);
4162         }else{
4163             this.buffered = true;
4164             this.value = arguments;
4165         }
4166         return this;
4167     },
4168
4169     onSetValue: function(id, value){
4170         if(arguments.length == 1){
4171             if(Ext.isArray(id)){
4172                 // an array of boolean values
4173                 Ext.each(id, function(val, idx){
4174                     var item = this.items.itemAt(idx);
4175                     if(item){
4176                         item.setValue(val);
4177                     }
4178                 }, this);
4179             }else if(Ext.isObject(id)){
4180                 // set of name/value pairs
4181                 for(var i in id){
4182                     var f = this.getBox(i);
4183                     if(f){
4184                         f.setValue(id[i]);
4185                     }
4186                 }
4187             }else{
4188                 this.setValueForItem(id);
4189             }
4190         }else{
4191             var f = this.getBox(id);
4192             if(f){
4193                 f.setValue(value);
4194             }
4195         }
4196     },
4197
4198     // private
4199     beforeDestroy: function(){
4200         Ext.destroy(this.panel);
4201         Ext.form.CheckboxGroup.superclass.beforeDestroy.call(this);
4202
4203     },
4204
4205     setValueForItem : function(val){
4206         val = String(val).split(',');
4207         this.eachItem(function(item){
4208             if(val.indexOf(item.inputValue)> -1){
4209                 item.setValue(true);
4210             }
4211         });
4212     },
4213
4214     // private
4215     getBox : function(id){
4216         var box = null;
4217         this.eachItem(function(f){
4218             if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
4219                 box = f;
4220                 return false;
4221             }
4222         });
4223         return box;
4224     },
4225
4226     /**
4227      * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
4228      * @return {Array} An array of the selected checkboxes.
4229      */
4230     getValue : function(){
4231         var out = [];
4232         this.eachItem(function(item){
4233             if(item.checked){
4234                 out.push(item);
4235             }
4236         });
4237         return out;
4238     },
4239
4240     // private
4241     eachItem: function(fn){
4242         if(this.items && this.items.each){
4243             this.items.each(fn, this);
4244         }
4245     },
4246
4247     /**
4248      * @cfg {String} name
4249      * @hide
4250      */
4251
4252     /**
4253      * @method getRawValue
4254      * @hide
4255      */
4256     getRawValue : Ext.emptyFn,
4257
4258     /**
4259      * @method setRawValue
4260      * @hide
4261      */
4262     setRawValue : Ext.emptyFn
4263
4264 });
4265
4266 Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
4267 /**
4268  * @class Ext.form.Radio
4269  * @extends Ext.form.Checkbox
4270  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
4271  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
4272  * @constructor
4273  * Creates a new Radio
4274  * @param {Object} config Configuration options
4275  * @xtype radio
4276  */
4277 Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
4278     inputType: 'radio',
4279
4280     /**
4281      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
4282      * @method
4283      */
4284     markInvalid : Ext.emptyFn,
4285     /**
4286      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
4287      * @method
4288      */
4289     clearInvalid : Ext.emptyFn,
4290
4291     /**
4292      * If this radio is part of a group, it will return the selected value
4293      * @return {String}
4294      */
4295     getGroupValue : function(){
4296         var p = this.el.up('form') || Ext.getBody();
4297         var c = p.child('input[name='+this.el.dom.name+']:checked', true);
4298         return c ? c.value : null;
4299     },
4300
4301     // private
4302     onClick : function(){
4303         if(this.el.dom.checked != this.checked){
4304                         var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
4305                         els.each(function(el){
4306                                 if(el.dom.id == this.id){
4307                                         this.setValue(true);
4308                                 }else{
4309                                         Ext.getCmp(el.dom.id).setValue(false);
4310                                 }
4311                         }, this);
4312                 }
4313     },
4314
4315     /**
4316      * Sets either the checked/unchecked status of this Radio, or, if a string value
4317      * is passed, checks a sibling Radio of the same name whose value is the value specified.
4318      * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
4319      * @return {Ext.form.Field} this
4320      */
4321     setValue : function(v){
4322         if (typeof v == 'boolean') {
4323             Ext.form.Radio.superclass.setValue.call(this, v);
4324         } else if (this.rendered) {
4325             var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
4326             if(r){
4327                 Ext.getCmp(r.id).setValue(true);
4328             }
4329         }
4330         return this;
4331     },
4332
4333     // private
4334     getCheckEl: function(){
4335         if(this.inGroup){
4336             return this.el.up('.x-form-radio-group')
4337         }
4338         return this.el.up('form') || Ext.getBody();
4339     }
4340 });
4341 Ext.reg('radio', Ext.form.Radio);
4342 /**
4343  * @class Ext.form.RadioGroup
4344  * @extends Ext.form.CheckboxGroup
4345  * A grouping container for {@link Ext.form.Radio} controls.
4346  * @constructor
4347  * Creates a new RadioGroup
4348  * @param {Object} config Configuration options
4349  * @xtype radiogroup
4350  */
4351 Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {
4352     /**
4353      * @cfg {Array} items An Array of {@link Ext.form.Radio Radio}s or Radio config objects
4354      * to arrange in the group.
4355      */
4356     /**
4357      * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
4358      * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
4359      * be used as the error text.
4360      */
4361     allowBlank : true,
4362     /**
4363      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
4364      * (defaults to 'You must select one item in this group')
4365      */
4366     blankText : 'You must select one item in this group',
4367     
4368     // private
4369     defaultType : 'radio',
4370     
4371     // private
4372     groupCls : 'x-form-radio-group',
4373     
4374     /**
4375      * @event change
4376      * Fires when the state of a child radio changes.
4377      * @param {Ext.form.RadioGroup} this
4378      * @param {Ext.form.Radio} checked The checked radio
4379      */
4380     
4381     /**
4382      * Gets the selected {@link Ext.form.Radio} in the group, if it exists.
4383      * @return {Ext.form.Radio} The selected radio.
4384      */
4385     getValue : function(){
4386         var out = null;
4387         this.eachItem(function(item){
4388             if(item.checked){
4389                 out = item;
4390                 return false;
4391             }
4392         });
4393         return out;
4394     },
4395     
4396     /**
4397      * Sets the checked radio in the group.
4398      * @param {String/Ext.form.Radio} id The radio to check.
4399      * @param {Boolean} value The value to set the radio.
4400      * @return {Ext.form.RadioGroup} this
4401      */
4402     onSetValue : function(id, value){
4403         if(arguments.length > 1){
4404             var f = this.getBox(id);
4405             if(f){
4406                 f.setValue(value);
4407                 if(f.checked){
4408                     this.eachItem(function(item){
4409                         if (item !== f){
4410                             item.setValue(false);
4411                         }
4412                     });
4413                 }
4414             }
4415         }else{
4416             this.setValueForItem(id);
4417         }
4418     },
4419     
4420     setValueForItem : function(val){
4421         val = String(val).split(',')[0];
4422         this.eachItem(function(item){
4423             item.setValue(val == item.inputValue);
4424         });
4425     },
4426     
4427     // private
4428     fireChecked : function(){
4429         if(!this.checkTask){
4430             this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);
4431         }
4432         this.checkTask.delay(10);
4433     },
4434     
4435     // private
4436     bufferChecked : function(){
4437         var out = null;
4438         this.eachItem(function(item){
4439             if(item.checked){
4440                 out = item;
4441                 return false;
4442             }
4443         });
4444         this.fireEvent('change', this, out);
4445     },
4446     
4447     onDestroy : function(){
4448         if(this.checkTask){
4449             this.checkTask.cancel();
4450             this.checkTask = null;
4451         }
4452         Ext.form.RadioGroup.superclass.onDestroy.call(this);
4453     }
4454
4455 });
4456
4457 Ext.reg('radiogroup', Ext.form.RadioGroup);
4458 /**\r
4459  * @class Ext.form.Hidden\r
4460  * @extends Ext.form.Field\r
4461  * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.\r
4462  * @constructor\r
4463  * Create a new Hidden field.\r
4464  * @param {Object} config Configuration options\r
4465  * @xtype hidden\r
4466  */\r
4467 Ext.form.Hidden = Ext.extend(Ext.form.Field, {\r
4468     // private\r
4469     inputType : 'hidden',\r
4470 \r
4471     // private\r
4472     onRender : function(){\r
4473         Ext.form.Hidden.superclass.onRender.apply(this, arguments);\r
4474     },\r
4475 \r
4476     // private\r
4477     initEvents : function(){\r
4478         this.originalValue = this.getValue();\r
4479     },\r
4480 \r
4481     // These are all private overrides\r
4482     setSize : Ext.emptyFn,\r
4483     setWidth : Ext.emptyFn,\r
4484     setHeight : Ext.emptyFn,\r
4485     setPosition : Ext.emptyFn,\r
4486     setPagePosition : Ext.emptyFn,\r
4487     markInvalid : Ext.emptyFn,\r
4488     clearInvalid : Ext.emptyFn\r
4489 });\r
4490 Ext.reg('hidden', Ext.form.Hidden);/**
4491  * @class Ext.form.BasicForm
4492  * @extends Ext.util.Observable
4493  * <p>Encapsulates the DOM &lt;form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides
4494  * input field management, validation, submission, and form loading services.</p>
4495  * <p>By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}.
4496  * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.</p>
4497  * <p><b><u>File Uploads</u></b></p>
4498  * <p>{@link #fileUpload File uploads} are not performed using Ajax submission, that
4499  * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
4500  * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
4501  * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
4502  * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
4503  * but removed after the return data has been gathered.</p>
4504  * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
4505  * server is using JSON to send the return object, then the
4506  * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
4507  * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
4508  * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
4509  * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
4510  * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
4511  * is created containing a <tt>responseText</tt> property in order to conform to the
4512  * requirements of event handlers and callbacks.</p>
4513  * <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>
4514  * and some server technologies (notably JEE) may require some custom processing in order to
4515  * retrieve parameter names and parameter values from the packet content.</p>
4516  * @constructor
4517  * @param {Mixed} el The form element or its id
4518  * @param {Object} config Configuration options
4519  */
4520 Ext.form.BasicForm = Ext.extend(Ext.util.Observable, {
4521     
4522     constructor: function(el, config){
4523         Ext.apply(this, config);
4524         if(Ext.isString(this.paramOrder)){
4525             this.paramOrder = this.paramOrder.split(/[\s,|]/);
4526         }
4527         /**
4528          * A {@link Ext.util.MixedCollection MixedCollection} containing all the Ext.form.Fields in this form.
4529          * @type MixedCollection
4530          * @property items
4531          */
4532         this.items = new Ext.util.MixedCollection(false, function(o){
4533             return o.getItemId();
4534         });
4535         this.addEvents(
4536             /**
4537              * @event beforeaction
4538              * Fires before any action is performed. Return false to cancel the action.
4539              * @param {Form} this
4540              * @param {Action} action The {@link Ext.form.Action} to be performed
4541              */
4542             'beforeaction',
4543             /**
4544              * @event actionfailed
4545              * Fires when an action fails.
4546              * @param {Form} this
4547              * @param {Action} action The {@link Ext.form.Action} that failed
4548              */
4549             'actionfailed',
4550             /**
4551              * @event actioncomplete
4552              * Fires when an action is completed.
4553              * @param {Form} this
4554              * @param {Action} action The {@link Ext.form.Action} that completed
4555              */
4556             'actioncomplete'
4557         );
4558
4559         if(el){
4560             this.initEl(el);
4561         }
4562         Ext.form.BasicForm.superclass.constructor.call(this);    
4563     },
4564     
4565     /**
4566      * @cfg {String} method
4567      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4568      */
4569     /**
4570      * @cfg {DataReader} reader
4571      * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read
4572      * data when executing 'load' actions. This is optional as there is built-in
4573      * support for processing JSON.  For additional information on using an XMLReader
4574      * see the example provided in examples/form/xml-form.html.
4575      */
4576     /**
4577      * @cfg {DataReader} errorReader
4578      * <p>An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to
4579      * read field error messages returned from 'submit' actions. This is optional
4580      * as there is built-in support for processing JSON.</p>
4581      * <p>The Records which provide messages for the invalid Fields must use the
4582      * Field name (or id) as the Record ID, and must contain a field called 'msg'
4583      * which contains the error message.</p>
4584      * <p>The errorReader does not have to be a full-blown implementation of a
4585      * DataReader. It simply needs to implement a <tt>read(xhr)</tt> function
4586      * which returns an Array of Records in an object with the following
4587      * structure:</p><pre><code>
4588 {
4589     records: recordArray
4590 }
4591 </code></pre>
4592      */
4593     /**
4594      * @cfg {String} url
4595      * The URL to use for form actions if one isn't supplied in the
4596      * <code>{@link #doAction doAction} options</code>.
4597      */
4598     /**
4599      * @cfg {Boolean} fileUpload
4600      * Set to true if this form is a file upload.
4601      * <p>File uploads are not performed using normal 'Ajax' techniques, that is they are <b>not</b>
4602      * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
4603      * DOM <tt>&lt;form></tt> element temporarily modified to have its
4604      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
4605      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
4606      * but removed after the return data has been gathered.</p>
4607      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
4608      * server is using JSON to send the return object, then the
4609      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
4610      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
4611      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
4612      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
4613      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
4614      * is created containing a <tt>responseText</tt> property in order to conform to the
4615      * requirements of event handlers and callbacks.</p>
4616      * <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>
4617      * and some server technologies (notably JEE) may require some custom processing in order to
4618      * retrieve parameter names and parameter values from the packet content.</p>
4619      */
4620     /**
4621      * @cfg {Object} baseParams
4622      * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
4623      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
4624      */
4625     /**
4626      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4627      */
4628     timeout: 30,
4629
4630     /**
4631      * @cfg {Object} api (Optional) If specified load and submit actions will be handled
4632      * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}.
4633      * Methods which have been imported by Ext.Direct can be specified here to load and submit
4634      * forms.
4635      * Such as the following:<pre><code>
4636 api: {
4637     load: App.ss.MyProfile.load,
4638     submit: App.ss.MyProfile.submit
4639 }
4640 </code></pre>
4641      * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
4642      * to customize how the load method is invoked.
4643      * Submit actions will always use a standard form submit. The formHandler configuration must
4644      * be set on the associated server-side method which has been imported by Ext.Direct</p>
4645      */
4646
4647     /**
4648      * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
4649      * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
4650      * <code>load</code> configuration.</p>
4651      * <br><p>Specify the params in the order in which they must be executed on the
4652      * server-side as either (1) an Array of String values, or (2) a String of params
4653      * delimited by either whitespace, comma, or pipe. For example,
4654      * any of the following would be acceptable:</p><pre><code>
4655 paramOrder: ['param1','param2','param3']
4656 paramOrder: 'param1 param2 param3'
4657 paramOrder: 'param1,param2,param3'
4658 paramOrder: 'param1|param2|param'
4659      </code></pre>
4660      */
4661     paramOrder: undefined,
4662
4663     /**
4664      * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
4665      * <code>load</code> configuration. Send parameters as a collection of named
4666      * arguments (defaults to <tt>false</tt>). Providing a
4667      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
4668      */
4669     paramsAsHash: false,
4670     
4671     /**
4672      * @cfg {String} waitTitle
4673      * The default title to show for the waiting message box (defaults to <tt>'Please Wait...'</tt>)
4674      */
4675     waitTitle: 'Please Wait...',
4676
4677     // private
4678     activeAction : null,
4679
4680     /**
4681      * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
4682      * or {@link #setValues}() data instead of when the form was first created.  Defaults to <tt>false</tt>.
4683      */
4684     trackResetOnLoad : false,
4685
4686     /**
4687      * @cfg {Boolean} standardSubmit
4688      * <p>If set to <tt>true</tt>, standard HTML form submits are used instead
4689      * of XHR (Ajax) style form submissions. Defaults to <tt>false</tt>.</p>
4690      * <br><p><b>Note:</b> When using <code>standardSubmit</code>, the
4691      * <code>options</code> to <code>{@link #submit}</code> are ignored because
4692      * Ext's Ajax infrastracture is bypassed. To pass extra parameters (e.g.
4693      * <code>baseParams</code> and <code>params</code>), utilize hidden fields
4694      * to submit extra data, for example:</p>
4695      * <pre><code>
4696 new Ext.FormPanel({
4697     standardSubmit: true,
4698     baseParams: {
4699         foo: 'bar'
4700     },
4701     {@link url}: 'myProcess.php',
4702     items: [{
4703         xtype: 'textfield',
4704         name: 'userName'
4705     }],
4706     buttons: [{
4707         text: 'Save',
4708         handler: function(){
4709             var fp = this.ownerCt.ownerCt,
4710                 form = fp.getForm();
4711             if (form.isValid()) {
4712                 // check if there are baseParams and if
4713                 // hiddent items have been added already
4714                 if (fp.baseParams && !fp.paramsAdded) {
4715                     // add hidden items for all baseParams
4716                     for (i in fp.baseParams) {
4717                         fp.add({
4718                             xtype: 'hidden',
4719                             name: i,
4720                             value: fp.baseParams[i]
4721                         });
4722                     }
4723                     fp.doLayout();
4724                     // set a custom flag to prevent re-adding
4725                     fp.paramsAdded = true;
4726                 }
4727                 form.{@link #submit}();
4728             }
4729         }
4730     }]
4731 });
4732      * </code></pre>
4733      */
4734     /**
4735      * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
4736      * element by passing it or its id or mask the form itself by passing in true.
4737      * @type Mixed
4738      * @property waitMsgTarget
4739      */
4740
4741     // private
4742     initEl : function(el){
4743         this.el = Ext.get(el);
4744         this.id = this.el.id || Ext.id();
4745         if(!this.standardSubmit){
4746             this.el.on('submit', this.onSubmit, this);
4747         }
4748         this.el.addClass('x-form');
4749     },
4750
4751     /**
4752      * Get the HTML form Element
4753      * @return Ext.Element
4754      */
4755     getEl: function(){
4756         return this.el;
4757     },
4758
4759     // private
4760     onSubmit : function(e){
4761         e.stopEvent();
4762     },
4763
4764     // private
4765     destroy: function() {
4766         this.items.each(function(f){
4767             Ext.destroy(f);
4768         });
4769         if(this.el){
4770             this.el.removeAllListeners();
4771             this.el.remove();
4772         }
4773         this.purgeListeners();
4774     },
4775
4776     /**
4777      * Returns true if client-side validation on the form is successful.
4778      * @return Boolean
4779      */
4780     isValid : function(){
4781         var valid = true;
4782         this.items.each(function(f){
4783            if(!f.validate()){
4784                valid = false;
4785            }
4786         });
4787         return valid;
4788     },
4789
4790     /**
4791      * <p>Returns true if any fields in this form have changed from their original values.</p>
4792      * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
4793      * Fields' <i>original values</i> are updated when the values are loaded by {@link #setValues}
4794      * or {@link #loadRecord}.</p>
4795      * @return Boolean
4796      */
4797     isDirty : function(){
4798         var dirty = false;
4799         this.items.each(function(f){
4800            if(f.isDirty()){
4801                dirty = true;
4802                return false;
4803            }
4804         });
4805         return dirty;
4806     },
4807
4808     /**
4809      * Performs a predefined action ({@link Ext.form.Action.Submit} or
4810      * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action}
4811      * to perform application-specific processing.
4812      * @param {String/Object} actionName The name of the predefined action type,
4813      * or instance of {@link Ext.form.Action} to perform.
4814      * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}.
4815      * All of the config options listed below are supported by both the
4816      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
4817      * actions unless otherwise noted (custom actions could also accept
4818      * other config options):<ul>
4819      *
4820      * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
4821      * to the form's {@link #url}.)</div></li>
4822      *
4823      * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
4824      * to the form's method, or POST if not defined)</div></li>
4825      *
4826      * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
4827      * (defaults to the form's baseParams, or none if not defined)</p>
4828      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
4829      *
4830      * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action
4831      * (defaults to the form's default headers)</div></li>
4832      *
4833      * <li><b>success</b> : Function<div class="sub-desc">The callback that will
4834      * be invoked after a successful response (see top of
4835      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
4836      * for a description of what constitutes a successful response).
4837      * The function is passed the following parameters:<ul>
4838      * <li><tt>form</tt> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
4839      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
4840      * <div class="sub-desc">The action object contains these properties of interest:<ul>
4841      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
4842      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
4843      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
4844      * </ul></div></li></ul></div></li>
4845      *
4846      * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
4847      * failed transaction attempt. The function is passed the following parameters:<ul>
4848      * <li><tt>form</tt> : The {@link Ext.form.BasicForm} that requested the action.</li>
4849      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
4850      * <div class="sub-desc">The action object contains these properties of interest:<ul>
4851      * <li><tt>{@link Ext.form.Action#failureType failureType}</tt></li>
4852      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
4853      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
4854      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
4855      * </ul></div></li></ul></div></li>
4856      *
4857      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
4858      * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
4859      *
4860      * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
4861      * Determines whether a Form's fields are validated in a final call to
4862      * {@link Ext.form.BasicForm#isValid isValid} prior to submission. Set to <tt>false</tt>
4863      * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
4864      *
4865      * @return {BasicForm} this
4866      */
4867     doAction : function(action, options){
4868         if(Ext.isString(action)){
4869             action = new Ext.form.Action.ACTION_TYPES[action](this, options);
4870         }
4871         if(this.fireEvent('beforeaction', this, action) !== false){
4872             this.beforeAction(action);
4873             action.run.defer(100, action);
4874         }
4875         return this;
4876     },
4877
4878     /**
4879      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Submit submit action}.
4880      * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
4881      * <p><b>Note:</b> this is ignored when using the {@link #standardSubmit} option.</p>
4882      * <p>The following code:</p><pre><code>
4883 myFormPanel.getForm().submit({
4884     clientValidation: true,
4885     url: 'updateConsignment.php',
4886     params: {
4887         newStatus: 'delivered'
4888     },
4889     success: function(form, action) {
4890        Ext.Msg.alert('Success', action.result.msg);
4891     },
4892     failure: function(form, action) {
4893         switch (action.failureType) {
4894             case Ext.form.Action.CLIENT_INVALID:
4895                 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
4896                 break;
4897             case Ext.form.Action.CONNECT_FAILURE:
4898                 Ext.Msg.alert('Failure', 'Ajax communication failed');
4899                 break;
4900             case Ext.form.Action.SERVER_INVALID:
4901                Ext.Msg.alert('Failure', action.result.msg);
4902        }
4903     }
4904 });
4905 </code></pre>
4906      * would process the following server response for a successful submission:<pre><code>
4907 {
4908     "success":true, // note this is Boolean, not string
4909     "msg":"Consignment updated"
4910 }
4911 </code></pre>
4912      * and the following server response for a failed submission:<pre><code>
4913 {
4914     "success":false, // note this is Boolean, not string
4915     "msg":"You do not have permission to perform this operation"
4916 }
4917 </code></pre>
4918      * @return {BasicForm} this
4919      */
4920     submit : function(options){
4921         if(this.standardSubmit){
4922             var v = this.isValid();
4923             if(v){
4924                 var el = this.el.dom;
4925                 if(this.url && Ext.isEmpty(el.action)){
4926                     el.action = this.url;
4927                 }
4928                 el.submit();
4929             }
4930             return v;
4931         }
4932         var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
4933         this.doAction(submitAction, options);
4934         return this;
4935     },
4936
4937     /**
4938      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
4939      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
4940      * @return {BasicForm} this
4941      */
4942     load : function(options){
4943         var loadAction = String.format('{0}load', this.api ? 'direct' : '');
4944         this.doAction(loadAction, options);
4945         return this;
4946     },
4947
4948     /**
4949      * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
4950      * @param {Record} record The record to edit
4951      * @return {BasicForm} this
4952      */
4953     updateRecord : function(record){
4954         record.beginEdit();
4955         var fs = record.fields;
4956         fs.each(function(f){
4957             var field = this.findField(f.name);
4958             if(field){
4959                 record.set(f.name, field.getValue());
4960             }
4961         }, this);
4962         record.endEdit();
4963         return this;
4964     },
4965
4966     /**
4967      * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
4968      * {@link Ext.data.Record#data record data}.
4969      * See also {@link #trackResetOnLoad}.
4970      * @param {Record} record The record to load
4971      * @return {BasicForm} this
4972      */
4973     loadRecord : function(record){
4974         this.setValues(record.data);
4975         return this;
4976     },
4977
4978     // private
4979     beforeAction : function(action){
4980         var o = action.options;
4981         if(o.waitMsg){
4982             if(this.waitMsgTarget === true){
4983                 this.el.mask(o.waitMsg, 'x-mask-loading');
4984             }else if(this.waitMsgTarget){
4985                 this.waitMsgTarget = Ext.get(this.waitMsgTarget);
4986                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
4987             }else{
4988                 Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
4989             }
4990         }
4991     },
4992
4993     // private
4994     afterAction : function(action, success){
4995         this.activeAction = null;
4996         var o = action.options;
4997         if(o.waitMsg){
4998             if(this.waitMsgTarget === true){
4999                 this.el.unmask();
5000             }else if(this.waitMsgTarget){
5001                 this.waitMsgTarget.unmask();
5002             }else{
5003                 Ext.MessageBox.updateProgress(1);
5004                 Ext.MessageBox.hide();
5005             }
5006         }
5007         if(success){
5008             if(o.reset){
5009                 this.reset();
5010             }
5011             Ext.callback(o.success, o.scope, [this, action]);
5012             this.fireEvent('actioncomplete', this, action);
5013         }else{
5014             Ext.callback(o.failure, o.scope, [this, action]);
5015             this.fireEvent('actionfailed', this, action);
5016         }
5017     },
5018
5019     /**
5020      * Find a {@link Ext.form.Field} in this form.
5021      * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
5022      * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
5023      * @return Field
5024      */
5025     findField : function(id){
5026         var field = this.items.get(id);
5027         if(!Ext.isObject(field)){
5028             this.items.each(function(f){
5029                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5030                     field = f;
5031                     return false;
5032                 }
5033             });
5034         }
5035         return field || null;
5036     },
5037
5038
5039     /**
5040      * Mark fields in this form invalid in bulk.
5041      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5042      * @return {BasicForm} this
5043      */
5044     markInvalid : function(errors){
5045         if(Ext.isArray(errors)){
5046             for(var i = 0, len = errors.length; i < len; i++){
5047                 var fieldError = errors[i];
5048                 var f = this.findField(fieldError.id);
5049                 if(f){
5050                     f.markInvalid(fieldError.msg);
5051                 }
5052             }
5053         }else{
5054             var field, id;
5055             for(id in errors){
5056                 if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
5057                     field.markInvalid(errors[id]);
5058                 }
5059             }
5060         }
5061         return this;
5062     },
5063
5064     /**
5065      * Set values for fields in this form in bulk.
5066      * @param {Array/Object} values Either an array in the form:<pre><code>
5067 [{id:'clientName', value:'Fred. Olsen Lines'},
5068  {id:'portOfLoading', value:'FXT'},
5069  {id:'portOfDischarge', value:'OSL'} ]</code></pre>
5070      * or an object hash of the form:<pre><code>
5071 {
5072     clientName: 'Fred. Olsen Lines',
5073     portOfLoading: 'FXT',
5074     portOfDischarge: 'OSL'
5075 }</code></pre>
5076      * @return {BasicForm} this
5077      */
5078     setValues : function(values){
5079         if(Ext.isArray(values)){ // array of objects
5080             for(var i = 0, len = values.length; i < len; i++){
5081                 var v = values[i];
5082                 var f = this.findField(v.id);
5083                 if(f){
5084                     f.setValue(v.value);
5085                     if(this.trackResetOnLoad){
5086                         f.originalValue = f.getValue();
5087                     }
5088                 }
5089             }
5090         }else{ // object hash
5091             var field, id;
5092             for(id in values){
5093                 if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
5094                     field.setValue(values[id]);
5095                     if(this.trackResetOnLoad){
5096                         field.originalValue = field.getValue();
5097                     }
5098                 }
5099             }
5100         }
5101         return this;
5102     },
5103
5104     /**
5105      * <p>Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit.
5106      * If multiple fields exist with the same name they are returned as an array.</p>
5107      * <p><b>Note:</b> The values are collected from all enabled HTML input elements within the form, <u>not</u> from
5108      * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the
5109      * value can potentially be the emptyText of a field.</p>
5110      * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object)
5111      * @return {String/Object}
5112      */
5113     getValues : function(asString){
5114         var fs = Ext.lib.Ajax.serializeForm(this.el.dom);
5115         if(asString === true){
5116             return fs;
5117         }
5118         return Ext.urlDecode(fs);
5119     },
5120
5121     /**
5122      * Retrieves the fields in the form as a set of key/value pairs, using the {@link Ext.form.Field#getValue getValue()} method.
5123      * If multiple fields exist with the same name they are returned as an array.
5124      * @param {Boolean} dirtyOnly (optional) True to return only fields that are dirty.
5125      * @return {Object} The values in the form
5126      */
5127     getFieldValues : function(dirtyOnly){
5128         var o = {},
5129             n,
5130             key,
5131             val;
5132         this.items.each(function(f){
5133             if(dirtyOnly !== true || f.isDirty()){
5134                 n = f.getName();
5135                 key = o[n];
5136                 val = f.getValue();
5137                 
5138                 if(Ext.isDefined(key)){
5139                     if(Ext.isArray(key)){
5140                         o[n].push(val);
5141                     }else{
5142                         o[n] = [key, val];
5143                     }
5144                 }else{
5145                     o[n] = val;
5146                 }
5147             }
5148         });
5149         return o;
5150     },
5151
5152     /**
5153      * Clears all invalid messages in this form.
5154      * @return {BasicForm} this
5155      */
5156     clearInvalid : function(){
5157         this.items.each(function(f){
5158            f.clearInvalid();
5159         });
5160         return this;
5161     },
5162
5163     /**
5164      * Resets this form.
5165      * @return {BasicForm} this
5166      */
5167     reset : function(){
5168         this.items.each(function(f){
5169             f.reset();
5170         });
5171         return this;
5172     },
5173
5174     /**
5175      * Add Ext.form Components to this form's Collection. This does not result in rendering of
5176      * the passed Component, it just enables the form to validate Fields, and distribute values to
5177      * Fields.
5178      * <p><b>You will not usually call this function. In order to be rendered, a Field must be added
5179      * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}.
5180      * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's
5181      * collection.</b></p>
5182      * @param {Field} field1
5183      * @param {Field} field2 (optional)
5184      * @param {Field} etc (optional)
5185      * @return {BasicForm} this
5186      */
5187     add : function(){
5188         this.items.addAll(Array.prototype.slice.call(arguments, 0));
5189         return this;
5190     },
5191
5192
5193     /**
5194      * Removes a field from the items collection (does NOT remove its markup).
5195      * @param {Field} field
5196      * @return {BasicForm} this
5197      */
5198     remove : function(field){
5199         this.items.remove(field);
5200         return this;
5201     },
5202
5203     /**
5204      * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm,
5205      * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id.
5206      * @return {BasicForm} this
5207      */
5208     render : function(){
5209         this.items.each(function(f){
5210             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
5211                 f.applyToMarkup(f.id);
5212             }
5213         });
5214         return this;
5215     },
5216
5217     /**
5218      * Calls {@link Ext#apply} for all fields in this form with the passed object.
5219      * @param {Object} values
5220      * @return {BasicForm} this
5221      */
5222     applyToFields : function(o){
5223         this.items.each(function(f){
5224            Ext.apply(f, o);
5225         });
5226         return this;
5227     },
5228
5229     /**
5230      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
5231      * @param {Object} values
5232      * @return {BasicForm} this
5233      */
5234     applyIfToFields : function(o){
5235         this.items.each(function(f){
5236            Ext.applyIf(f, o);
5237         });
5238         return this;
5239     },
5240
5241     callFieldMethod : function(fnName, args){
5242         args = args || [];
5243         this.items.each(function(f){
5244             if(Ext.isFunction(f[fnName])){
5245                 f[fnName].apply(f, args);
5246             }
5247         });
5248         return this;
5249     }
5250 });
5251
5252 // back compat
5253 Ext.BasicForm = Ext.form.BasicForm;/**
5254  * @class Ext.form.FormPanel
5255  * @extends Ext.Panel
5256  * <p>Standard form container.</p>
5257  *
5258  * <p><b><u>Layout</u></b></p>
5259  * <p>By default, FormPanel is configured with <tt>layout:'form'</tt> to use an {@link Ext.layout.FormLayout}
5260  * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers
5261  * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the
5262  * {@link Ext.layout.FormLayout} layout manager.</p>
5263  *
5264  * <p><b><u>BasicForm</u></b></p>
5265  * <p>Although <b>not listed</b> as configuration options of FormPanel, the FormPanel class accepts all
5266  * of the config options required to configure its internal {@link Ext.form.BasicForm} for:
5267  * <div class="mdetail-params"><ul>
5268  * <li>{@link Ext.form.BasicForm#fileUpload file uploads}</li>
5269  * <li>functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form</li>
5270  * </ul></div>
5271  *
5272  * <p><b>Note</b>: If subclassing FormPanel, any configuration options for the BasicForm must be applied to
5273  * the <tt><b>initialConfig</b></tt> property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm}
5274  * configuration settings to <b><tt>this</tt></b> will <b>not</b> affect the BasicForm's configuration.</p>
5275  *
5276  * <p><b><u>Form Validation</u></b></p>
5277  * <p>For information on form validation see the following:</p>
5278  * <div class="mdetail-params"><ul>
5279  * <li>{@link Ext.form.TextField}</li>
5280  * <li>{@link Ext.form.VTypes}</li>
5281  * <li>{@link Ext.form.BasicForm#doAction BasicForm.doAction <b>clientValidation</b> notes}</li>
5282  * <li><tt>{@link Ext.form.FormPanel#monitorValid monitorValid}</tt></li>
5283  * </ul></div>
5284  *
5285  * <p><b><u>Form Submission</u></b></p>
5286  * <p>By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser
5287  * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the
5288  * <tt><b>{@link Ext.form.BasicForm#standardSubmit standardSubmit}</b></tt> option.</p>
5289  *
5290  * @constructor
5291  * @param {Object} config Configuration options
5292  * @xtype form
5293  */
5294 Ext.FormPanel = Ext.extend(Ext.Panel, {
5295     /**
5296      * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).
5297      */
5298     /**
5299      * @cfg {Boolean} hideLabels
5300      * <p><tt>true</tt> to hide field labels by default (sets <tt>display:none</tt>). Defaults to
5301      * <tt>false</tt>.</p>
5302      * <p>Also see {@link Ext.Component}.<tt>{@link Ext.Component#hideLabel hideLabel}</tt>.
5303      */
5304     /**
5305      * @cfg {Number} labelPad
5306      * The default padding in pixels for field labels (defaults to <tt>5</tt>). <tt>labelPad</tt> only
5307      * applies if <tt>{@link #labelWidth}</tt> is also specified, otherwise it will be ignored.
5308      */
5309     /**
5310      * @cfg {String} labelSeparator
5311      * See {@link Ext.Component}.<tt>{@link Ext.Component#labelSeparator labelSeparator}</tt>
5312      */
5313     /**
5314      * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
5315      * and can be overridden on any child container (e.g., a fieldset can specify a different <tt>labelWidth</tt>
5316      * for its fields) (defaults to <tt>100</tt>).
5317      */
5318     /**
5319      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
5320      */
5321     /**
5322      * @cfg {Array} buttons
5323      * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.<br>
5324      * <p>Buttons in the footer of a FormPanel may be configured with the option <tt>formBind: true</tt>. This causes
5325      * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on
5326      * the form's valid/invalid state.</p>
5327      */
5328
5329
5330     /**
5331      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to <tt>75</tt>).
5332      */
5333     minButtonWidth : 75,
5334
5335     /**
5336      * @cfg {String} labelAlign The label alignment value used for the <tt>text-align</tt> specification
5337      * for the <b>container</b>. Valid values are <tt>"left</tt>", <tt>"top"</tt> or <tt>"right"</tt>
5338      * (defaults to <tt>"left"</tt>). This property cascades to child <b>containers</b> and can be
5339      * overridden on any child <b>container</b> (e.g., a fieldset can specify a different <tt>labelAlign</tt>
5340      * for its fields).
5341      */
5342     labelAlign : 'left',
5343
5344     /**
5345      * @cfg {Boolean} monitorValid If <tt>true</tt>, the form monitors its valid state <b>client-side</b> and
5346      * regularly fires the {@link #clientvalidation} event passing that state.<br>
5347      * <p>When monitoring valid state, the FormPanel enables/disables any of its configured
5348      * {@link #buttons} which have been configured with <code>formBind: true</code> depending
5349      * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to <tt>false</tt></p>
5350      */
5351     monitorValid : false,
5352
5353     /**
5354      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
5355      */
5356     monitorPoll : 200,
5357
5358     /**
5359      * @cfg {String} layout Defaults to <tt>'form'</tt>.  Normally this configuration property should not be altered.
5360      * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}.
5361      */
5362     layout : 'form',
5363
5364     // private
5365     initComponent : function(){
5366         this.form = this.createForm();
5367         Ext.FormPanel.superclass.initComponent.call(this);
5368
5369         this.bodyCfg = {
5370             tag: 'form',
5371             cls: this.baseCls + '-body',
5372             method : this.method || 'POST',
5373             id : this.formId || Ext.id()
5374         };
5375         if(this.fileUpload) {
5376             this.bodyCfg.enctype = 'multipart/form-data';
5377         }
5378         this.initItems();
5379
5380         this.addEvents(
5381             /**
5382              * @event clientvalidation
5383              * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5384              * @param {Ext.form.FormPanel} this
5385              * @param {Boolean} valid true if the form has passed client-side validation
5386              */
5387             'clientvalidation'
5388         );
5389
5390         this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']);
5391     },
5392
5393     // private
5394     createForm : function(){
5395         var config = Ext.applyIf({listeners: {}}, this.initialConfig);
5396         return new Ext.form.BasicForm(null, config);
5397     },
5398
5399     // private
5400     initFields : function(){
5401         var f = this.form;
5402         var formPanel = this;
5403         var fn = function(c){
5404             if(formPanel.isField(c)){
5405                 f.add(c);
5406             }else if(c.findBy && c != formPanel){
5407                 formPanel.applySettings(c);
5408                 //each check required for check/radio groups.
5409                 if(c.items && c.items.each){
5410                     c.items.each(fn, this);
5411                 }
5412             }
5413         };
5414         this.items.each(fn, this);
5415     },
5416
5417     // private
5418     applySettings: function(c){
5419         var ct = c.ownerCt;
5420         Ext.applyIf(c, {
5421             labelAlign: ct.labelAlign,
5422             labelWidth: ct.labelWidth,
5423             itemCls: ct.itemCls
5424         });
5425     },
5426
5427     // private
5428     getLayoutTarget : function(){
5429         return this.form.el;
5430     },
5431
5432     /**
5433      * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains.
5434      * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains.
5435      */
5436     getForm : function(){
5437         return this.form;
5438     },
5439
5440     // private
5441     onRender : function(ct, position){
5442         this.initFields();
5443         Ext.FormPanel.superclass.onRender.call(this, ct, position);
5444         this.form.initEl(this.body);
5445     },
5446
5447     // private
5448     beforeDestroy : function(){
5449         this.stopMonitoring();
5450         /*
5451          * Don't move this behaviour to BasicForm because it can be used
5452          * on it's own.
5453          */
5454         Ext.destroy(this.form);
5455         this.form.items.clear();
5456         Ext.FormPanel.superclass.beforeDestroy.call(this);
5457     },
5458
5459     // Determine if a Component is usable as a form Field.
5460     isField : function(c) {
5461         return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
5462     },
5463
5464     // private
5465     initEvents : function(){
5466         Ext.FormPanel.superclass.initEvents.call(this);
5467         // Listeners are required here to catch bubbling events from children.
5468         this.on({
5469             scope: this,
5470             add: this.onAddEvent,
5471             remove: this.onRemoveEvent
5472         });
5473         if(this.monitorValid){ // initialize after render
5474             this.startMonitoring();
5475         }
5476     },
5477
5478     // private
5479     onAdd: function(c){
5480         Ext.FormPanel.superclass.onAdd.call(this, c);
5481         this.processAdd(c);
5482     },
5483
5484     // private
5485     onAddEvent: function(ct, c){
5486         if(ct !== this){
5487             this.processAdd(c);
5488         }
5489     },
5490
5491     // private
5492     processAdd : function(c){
5493         // If a single form Field, add it
5494         if(this.isField(c)){
5495             this.form.add(c);
5496         // If a Container, add any Fields it might contain
5497         }else if(c.findBy){
5498             this.applySettings(c);
5499             this.form.add.apply(this.form, c.findBy(this.isField));
5500         }
5501     },
5502
5503     // private
5504     onRemove: function(c){
5505         Ext.FormPanel.superclass.onRemove.call(this, c);
5506         this.processRemove(c);
5507     },
5508
5509     onRemoveEvent: function(ct, c){
5510         if(ct !== this){
5511             this.processRemove(c);
5512         }
5513     },
5514
5515     // private
5516     processRemove : function(c){
5517         // If a single form Field, remove it
5518         if(this.isField(c)){
5519             this.form.remove(c);
5520         // If a Container, its already destroyed by the time it gets here.  Remove any references to destroyed fields.
5521         }else if(c.findBy){
5522             var isDestroyed = function(o) {
5523                 return !!o.isDestroyed;
5524             }
5525             this.form.items.filterBy(isDestroyed, this.form).each(this.form.remove, this.form);
5526         }
5527     },
5528
5529     /**
5530      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
5531      * option "monitorValid"
5532      */
5533     startMonitoring : function(){
5534         if(!this.validTask){
5535             this.validTask = new Ext.util.TaskRunner();
5536             this.validTask.start({
5537                 run : this.bindHandler,
5538                 interval : this.monitorPoll || 200,
5539                 scope: this
5540             });
5541         }
5542     },
5543
5544     /**
5545      * Stops monitoring of the valid state of this form
5546      */
5547     stopMonitoring : function(){
5548         if(this.validTask){
5549             this.validTask.stopAll();
5550             this.validTask = null;
5551         }
5552     },
5553
5554     /**
5555      * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.
5556      * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)
5557      */
5558     load : function(){
5559         this.form.load.apply(this.form, arguments);
5560     },
5561
5562     // private
5563     onDisable : function(){
5564         Ext.FormPanel.superclass.onDisable.call(this);
5565         if(this.form){
5566             this.form.items.each(function(){
5567                  this.disable();
5568             });
5569         }
5570     },
5571
5572     // private
5573     onEnable : function(){
5574         Ext.FormPanel.superclass.onEnable.call(this);
5575         if(this.form){
5576             this.form.items.each(function(){
5577                  this.enable();
5578             });
5579         }
5580     },
5581
5582     // private
5583     bindHandler : function(){
5584         var valid = true;
5585         this.form.items.each(function(f){
5586             if(!f.isValid(true)){
5587                 valid = false;
5588                 return false;
5589             }
5590         });
5591         if(this.fbar){
5592             var fitems = this.fbar.items.items;
5593             for(var i = 0, len = fitems.length; i < len; i++){
5594                 var btn = fitems[i];
5595                 if(btn.formBind === true && btn.disabled === valid){
5596                     btn.setDisabled(!valid);
5597                 }
5598             }
5599         }
5600         this.fireEvent('clientvalidation', this, valid);
5601     }
5602 });
5603 Ext.reg('form', Ext.FormPanel);
5604
5605 Ext.form.FormPanel = Ext.FormPanel;
5606 /**\r
5607  * @class Ext.form.FieldSet\r
5608  * @extends Ext.Panel\r
5609  * Standard container used for grouping items within a {@link Ext.form.FormPanel form}.\r
5610  * <pre><code>\r
5611 var form = new Ext.FormPanel({\r
5612     title: 'Simple Form with FieldSets',\r
5613     labelWidth: 75, // label settings here cascade unless overridden\r
5614     url: 'save-form.php',\r
5615     frame:true,\r
5616     bodyStyle:'padding:5px 5px 0',\r
5617     width: 700,\r
5618     renderTo: document.body,\r
5619     layout:'column', // arrange items in columns\r
5620     defaults: {      // defaults applied to items\r
5621         layout: 'form',\r
5622         border: false,\r
5623         bodyStyle: 'padding:4px'\r
5624     },\r
5625     items: [{\r
5626         // Fieldset in Column 1\r
5627         xtype:'fieldset',\r
5628         columnWidth: 0.5,\r
5629         title: 'Fieldset 1',\r
5630         collapsible: true,\r
5631         autoHeight:true,\r
5632         defaults: {\r
5633             anchor: '-20' // leave room for error icon\r
5634         },\r
5635         defaultType: 'textfield',\r
5636         items :[{\r
5637                 fieldLabel: 'Field 1'\r
5638             }, {\r
5639                 fieldLabel: 'Field 2'\r
5640             }, {\r
5641                 fieldLabel: 'Field 3'\r
5642             }\r
5643         ]\r
5644     },{\r
5645         // Fieldset in Column 2 - Panel inside\r
5646         xtype:'fieldset',\r
5647         title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header\r
5648         autoHeight:true,\r
5649         columnWidth: 0.5,\r
5650         checkboxToggle: true,\r
5651         collapsed: true, // fieldset initially collapsed\r
5652         layout:'anchor',\r
5653         items :[{\r
5654             xtype: 'panel',\r
5655             anchor: '100%',\r
5656             title: 'Panel inside a fieldset',\r
5657             frame: true,\r
5658             height: 100\r
5659         }]\r
5660     }]\r
5661 });\r
5662  * </code></pre>\r
5663  * @constructor\r
5664  * @param {Object} config Configuration options\r
5665  * @xtype fieldset\r
5666  */\r
5667 Ext.form.FieldSet = Ext.extend(Ext.Panel, {\r
5668     /**\r
5669      * @cfg {Mixed} checkboxToggle <tt>true</tt> to render a checkbox into the fieldset frame just\r
5670      * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults\r
5671      * to <tt>false</tt>).\r
5672      * <p>A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox.\r
5673      * If <tt>true</tt> is specified, the default DomHelper config object used to create the element\r
5674      * is:</p><pre><code>\r
5675      * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}\r
5676      * </code></pre>   \r
5677      */\r
5678     /**\r
5679      * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if <tt>{@link #checkboxToggle} = true</tt>\r
5680      * (defaults to <tt>'[checkbox id]-checkbox'</tt>).\r
5681      */\r
5682     /**\r
5683      * @cfg {Boolean} collapsible\r
5684      * <tt>true</tt> to make the fieldset collapsible and have the expand/collapse toggle button automatically\r
5685      * rendered into the legend element, <tt>false</tt> to keep the fieldset statically sized with no collapse\r
5686      * button (defaults to <tt>false</tt>). Another option is to configure <tt>{@link #checkboxToggle}</tt>.\r
5687      */\r
5688     /**\r
5689      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.\r
5690      */\r
5691     /**\r
5692      * @cfg {String} itemCls A css class to apply to the <tt>x-form-item</tt> of fields (see \r
5693      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).\r
5694      * This property cascades to child containers.\r
5695      */\r
5696     /**\r
5697      * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to <tt>'x-fieldset'</tt>).\r
5698      */\r
5699     baseCls : 'x-fieldset',\r
5700     /**\r
5701      * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to <tt>'form'</tt>).\r
5702      */\r
5703     layout : 'form',\r
5704     /**\r
5705      * @cfg {Boolean} animCollapse\r
5706      * <tt>true</tt> to animate the transition when the panel is collapsed, <tt>false</tt> to skip the\r
5707      * animation (defaults to <tt>false</tt>).\r
5708      */\r
5709     animCollapse : false,\r
5710 \r
5711     // private\r
5712     onRender : function(ct, position){\r
5713         if(!this.el){\r
5714             this.el = document.createElement('fieldset');\r
5715             this.el.id = this.id;\r
5716             if (this.title || this.header || this.checkboxToggle) {\r
5717                 this.el.appendChild(document.createElement('legend')).className = this.baseCls + '-header';\r
5718             }\r
5719         }\r
5720 \r
5721         Ext.form.FieldSet.superclass.onRender.call(this, ct, position);\r
5722 \r
5723         if(this.checkboxToggle){\r
5724             var o = typeof this.checkboxToggle == 'object' ?\r
5725                     this.checkboxToggle :\r
5726                     {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};\r
5727             this.checkbox = this.header.insertFirst(o);\r
5728             this.checkbox.dom.checked = !this.collapsed;\r
5729             this.mon(this.checkbox, 'click', this.onCheckClick, this);\r
5730         }\r
5731     },\r
5732 \r
5733     // private\r
5734     onCollapse : function(doAnim, animArg){\r
5735         if(this.checkbox){\r
5736             this.checkbox.dom.checked = false;\r
5737         }\r
5738         Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);\r
5739 \r
5740     },\r
5741 \r
5742     // private\r
5743     onExpand : function(doAnim, animArg){\r
5744         if(this.checkbox){\r
5745             this.checkbox.dom.checked = true;\r
5746         }\r
5747         Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);\r
5748     },\r
5749 \r
5750     /**\r
5751      * This function is called by the fieldset's checkbox when it is toggled (only applies when\r
5752      * checkboxToggle = true).  This method should never be called externally, but can be\r
5753      * overridden to provide custom behavior when the checkbox is toggled if needed.\r
5754      */\r
5755     onCheckClick : function(){\r
5756         this[this.checkbox.dom.checked ? 'expand' : 'collapse']();\r
5757     }\r
5758 \r
5759     /**\r
5760      * @cfg {String/Number} activeItem\r
5761      * @hide\r
5762      */\r
5763     /**\r
5764      * @cfg {Mixed} applyTo\r
5765      * @hide\r
5766      */\r
5767     /**\r
5768      * @cfg {Boolean} bodyBorder\r
5769      * @hide\r
5770      */\r
5771     /**\r
5772      * @cfg {Boolean} border\r
5773      * @hide\r
5774      */\r
5775     /**\r
5776      * @cfg {Boolean/Number} bufferResize\r
5777      * @hide\r
5778      */\r
5779     /**\r
5780      * @cfg {Boolean} collapseFirst\r
5781      * @hide\r
5782      */\r
5783     /**\r
5784      * @cfg {String} defaultType\r
5785      * @hide\r
5786      */\r
5787     /**\r
5788      * @cfg {String} disabledClass\r
5789      * @hide\r
5790      */\r
5791     /**\r
5792      * @cfg {String} elements\r
5793      * @hide\r
5794      */\r
5795     /**\r
5796      * @cfg {Boolean} floating\r
5797      * @hide\r
5798      */\r
5799     /**\r
5800      * @cfg {Boolean} footer\r
5801      * @hide\r
5802      */\r
5803     /**\r
5804      * @cfg {Boolean} frame\r
5805      * @hide\r
5806      */\r
5807     /**\r
5808      * @cfg {Boolean} header\r
5809      * @hide\r
5810      */\r
5811     /**\r
5812      * @cfg {Boolean} headerAsText\r
5813      * @hide\r
5814      */\r
5815     /**\r
5816      * @cfg {Boolean} hideCollapseTool\r
5817      * @hide\r
5818      */\r
5819     /**\r
5820      * @cfg {String} iconCls\r
5821      * @hide\r
5822      */\r
5823     /**\r
5824      * @cfg {Boolean/String} shadow\r
5825      * @hide\r
5826      */\r
5827     /**\r
5828      * @cfg {Number} shadowOffset\r
5829      * @hide\r
5830      */\r
5831     /**\r
5832      * @cfg {Boolean} shim\r
5833      * @hide\r
5834      */\r
5835     /**\r
5836      * @cfg {Object/Array} tbar\r
5837      * @hide\r
5838      */\r
5839     /**\r
5840      * @cfg {Array} tools\r
5841      * @hide\r
5842      */\r
5843     /**\r
5844      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate\r
5845      * @hide\r
5846      */\r
5847     /**\r
5848      * @cfg {String} xtype\r
5849      * @hide\r
5850      */\r
5851     /**\r
5852      * @property header\r
5853      * @hide\r
5854      */\r
5855     /**\r
5856      * @property footer\r
5857      * @hide\r
5858      */\r
5859     /**\r
5860      * @method focus\r
5861      * @hide\r
5862      */\r
5863     /**\r
5864      * @method getBottomToolbar\r
5865      * @hide\r
5866      */\r
5867     /**\r
5868      * @method getTopToolbar\r
5869      * @hide\r
5870      */\r
5871     /**\r
5872      * @method setIconClass\r
5873      * @hide\r
5874      */\r
5875     /**\r
5876      * @event activate\r
5877      * @hide\r
5878      */\r
5879     /**\r
5880      * @event beforeclose\r
5881      * @hide\r
5882      */\r
5883     /**\r
5884      * @event bodyresize\r
5885      * @hide\r
5886      */\r
5887     /**\r
5888      * @event close\r
5889      * @hide\r
5890      */\r
5891     /**\r
5892      * @event deactivate\r
5893      * @hide\r
5894      */\r
5895 });\r
5896 Ext.reg('fieldset', Ext.form.FieldSet);\r
5897 /**
5898  * @class Ext.form.HtmlEditor
5899  * @extends Ext.form.Field
5900  * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
5901  * automatically hidden when needed.  These are noted in the config options where appropriate.
5902  * <br><br>The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
5903  * enabled by default unless the global {@link Ext.QuickTips} singleton is {@link Ext.QuickTips#init initialized}.
5904  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
5905  * supported by this editor.</b>
5906  * <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
5907  * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.
5908  * <br><br>Example usage:
5909  * <pre><code>
5910 // Simple example rendered with default options:
5911 Ext.QuickTips.init();  // enable tooltips
5912 new Ext.form.HtmlEditor({
5913     renderTo: Ext.getBody(),
5914     width: 800,
5915     height: 300
5916 });
5917
5918 // Passed via xtype into a container and with custom options:
5919 Ext.QuickTips.init();  // enable tooltips
5920 new Ext.Panel({
5921     title: 'HTML Editor',
5922     renderTo: Ext.getBody(),
5923     width: 600,
5924     height: 300,
5925     frame: true,
5926     layout: 'fit',
5927     items: {
5928         xtype: 'htmleditor',
5929         enableColors: false,
5930         enableAlignments: false
5931     }
5932 });
5933 </code></pre>
5934  * @constructor
5935  * Create a new HtmlEditor
5936  * @param {Object} config
5937  * @xtype htmleditor
5938  */
5939
5940 Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
5941     /**
5942      * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
5943      */
5944     enableFormat : true,
5945     /**
5946      * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
5947      */
5948     enableFontSize : true,
5949     /**
5950      * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
5951      */
5952     enableColors : true,
5953     /**
5954      * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
5955      */
5956     enableAlignments : true,
5957     /**
5958      * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
5959      */
5960     enableLists : true,
5961     /**
5962      * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
5963      */
5964     enableSourceEdit : true,
5965     /**
5966      * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
5967      */
5968     enableLinks : true,
5969     /**
5970      * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
5971      */
5972     enableFont : true,
5973     /**
5974      * @cfg {String} createLinkText The default text for the create link prompt
5975      */
5976     createLinkText : 'Please enter the URL for the link:',
5977     /**
5978      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
5979      */
5980     defaultLinkValue : 'http:/'+'/',
5981     /**
5982      * @cfg {Array} fontFamilies An array of available font families
5983      */
5984     fontFamilies : [
5985         'Arial',
5986         'Courier New',
5987         'Tahoma',
5988         'Times New Roman',
5989         'Verdana'
5990     ],
5991     defaultFont: 'tahoma',
5992     /**
5993      * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to &#160; (Non-breaking space) in Opera and IE6, &#8203; (Zero-width space) in all other browsers).
5994      */
5995     defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',
5996
5997     // private properties
5998     actionMode: 'wrap',
5999     validationEvent : false,
6000     deferHeight: true,
6001     initialized : false,
6002     activated : false,
6003     sourceEditMode : false,
6004     onFocus : Ext.emptyFn,
6005     iframePad:3,
6006     hideMode:'offsets',
6007     defaultAutoCreate : {
6008         tag: "textarea",
6009         style:"width:500px;height:300px;",
6010         autocomplete: "off"
6011     },
6012
6013     // private
6014     initComponent : function(){
6015         this.addEvents(
6016             /**
6017              * @event initialize
6018              * Fires when the editor is fully initialized (including the iframe)
6019              * @param {HtmlEditor} this
6020              */
6021             'initialize',
6022             /**
6023              * @event activate
6024              * Fires when the editor is first receives the focus. Any insertion must wait
6025              * until after this event.
6026              * @param {HtmlEditor} this
6027              */
6028             'activate',
6029              /**
6030              * @event beforesync
6031              * Fires before the textarea is updated with content from the editor iframe. Return false
6032              * to cancel the sync.
6033              * @param {HtmlEditor} this
6034              * @param {String} html
6035              */
6036             'beforesync',
6037              /**
6038              * @event beforepush
6039              * Fires before the iframe editor is updated with content from the textarea. Return false
6040              * to cancel the push.
6041              * @param {HtmlEditor} this
6042              * @param {String} html
6043              */
6044             'beforepush',
6045              /**
6046              * @event sync
6047              * Fires when the textarea is updated with content from the editor iframe.
6048              * @param {HtmlEditor} this
6049              * @param {String} html
6050              */
6051             'sync',
6052              /**
6053              * @event push
6054              * Fires when the iframe editor is updated with content from the textarea.
6055              * @param {HtmlEditor} this
6056              * @param {String} html
6057              */
6058             'push',
6059              /**
6060              * @event editmodechange
6061              * Fires when the editor switches edit modes
6062              * @param {HtmlEditor} this
6063              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
6064              */
6065             'editmodechange'
6066         )
6067     },
6068
6069     // private
6070     createFontOptions : function(){
6071         var buf = [], fs = this.fontFamilies, ff, lc;
6072         for(var i = 0, len = fs.length; i< len; i++){
6073             ff = fs[i];
6074             lc = ff.toLowerCase();
6075             buf.push(
6076                 '<option value="',lc,'" style="font-family:',ff,';"',
6077                     (this.defaultFont == lc ? ' selected="true">' : '>'),
6078                     ff,
6079                 '</option>'
6080             );
6081         }
6082         return buf.join('');
6083     },
6084
6085     /*
6086      * Protected method that will not generally be called directly. It
6087      * is called when the editor creates its toolbar. Override this method if you need to
6088      * add custom toolbar buttons.
6089      * @param {HtmlEditor} editor
6090      */
6091     createToolbar : function(editor){
6092         var items = [];
6093         var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();\r
6094
6095
6096         function btn(id, toggle, handler){
6097             return {
6098                 itemId : id,
6099                 cls : 'x-btn-icon',
6100                 iconCls: 'x-edit-'+id,
6101                 enableToggle:toggle !== false,
6102                 scope: editor,
6103                 handler:handler||editor.relayBtnCmd,
6104                 clickEvent:'mousedown',
6105                 tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
6106                 overflowText: editor.buttonTips[id].title || undefined,
6107                 tabIndex:-1
6108             };
6109         }
6110
6111
6112         if(this.enableFont && !Ext.isSafari2){\r
6113             var fontSelectItem = new Ext.Toolbar.Item({\r
6114                autoEl: {\r
6115                     tag:'select',\r
6116                     cls:'x-font-select',\r
6117                     html: this.createFontOptions()\r
6118                }\r
6119             });
6120
6121             items.push(
6122                 fontSelectItem,
6123                 '-'
6124             );
6125         }
6126
6127         if(this.enableFormat){
6128             items.push(
6129                 btn('bold'),
6130                 btn('italic'),
6131                 btn('underline')
6132             );
6133         }
6134
6135         if(this.enableFontSize){
6136             items.push(
6137                 '-',
6138                 btn('increasefontsize', false, this.adjustFont),
6139                 btn('decreasefontsize', false, this.adjustFont)
6140             );
6141         }
6142
6143         if(this.enableColors){
6144             items.push(
6145                 '-', {
6146                     itemId:'forecolor',
6147                     cls:'x-btn-icon',
6148                     iconCls: 'x-edit-forecolor',
6149                     clickEvent:'mousedown',
6150                     tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,
6151                     tabIndex:-1,
6152                     menu : new Ext.menu.ColorMenu({
6153                         allowReselect: true,
6154                         focus: Ext.emptyFn,
6155                         value:'000000',
6156                         plain:true,
6157                         listeners: {
6158                             scope: this,
6159                             select: function(cp, color){
6160                                 this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
6161                                 this.deferFocus();
6162                             }
6163                         },
6164                         clickEvent:'mousedown'
6165                     })
6166                 }, {
6167                     itemId:'backcolor',
6168                     cls:'x-btn-icon',
6169                     iconCls: 'x-edit-backcolor',
6170                     clickEvent:'mousedown',
6171                     tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,
6172                     tabIndex:-1,
6173                     menu : new Ext.menu.ColorMenu({
6174                         focus: Ext.emptyFn,
6175                         value:'FFFFFF',
6176                         plain:true,
6177                         allowReselect: true,
6178                         listeners: {
6179                             scope: this,
6180                             select: function(cp, color){
6181                                 if(Ext.isGecko){
6182                                     this.execCmd('useCSS', false);
6183                                     this.execCmd('hilitecolor', color);
6184                                     this.execCmd('useCSS', true);
6185                                     this.deferFocus();
6186                                 }else{
6187                                     this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
6188                                     this.deferFocus();
6189                                 }
6190                             }
6191                         },
6192                         clickEvent:'mousedown'
6193                     })
6194                 }
6195             );
6196         }
6197
6198         if(this.enableAlignments){
6199             items.push(
6200                 '-',
6201                 btn('justifyleft'),
6202                 btn('justifycenter'),
6203                 btn('justifyright')
6204             );
6205         }
6206
6207         if(!Ext.isSafari2){
6208             if(this.enableLinks){
6209                 items.push(
6210                     '-',
6211                     btn('createlink', false, this.createLink)
6212                 );
6213             }
6214
6215             if(this.enableLists){
6216                 items.push(
6217                     '-',
6218                     btn('insertorderedlist'),
6219                     btn('insertunorderedlist')
6220                 );
6221             }
6222             if(this.enableSourceEdit){
6223                 items.push(
6224                     '-',
6225                     btn('sourceedit', true, function(btn){
6226                         this.toggleSourceEdit(!this.sourceEditMode);
6227                     })
6228                 );
6229             }
6230         }\r
6231 \r
6232         // build the toolbar\r
6233         var tb = new Ext.Toolbar({\r
6234             renderTo: this.wrap.dom.firstChild,\r
6235             items: items\r
6236         });\r
6237 \r
6238         if (fontSelectItem) {\r
6239             this.fontSelect = fontSelectItem.el;\r
6240 \r
6241             this.mon(this.fontSelect, 'change', function(){\r
6242                 var font = this.fontSelect.dom.value;\r
6243                 this.relayCmd('fontname', font);\r
6244                 this.deferFocus();\r
6245             }, this);\r
6246         }\r
6247 \r
6248 \r
6249         // stop form submits\r
6250         this.mon(tb.el, 'click', function(e){\r
6251             e.preventDefault();\r
6252         });\r
6253
6254         this.tb = tb;
6255     },
6256
6257     onDisable: function(){
6258         this.wrap.mask();
6259         Ext.form.HtmlEditor.superclass.onDisable.call(this);
6260     },
6261
6262     onEnable: function(){
6263         this.wrap.unmask();
6264         Ext.form.HtmlEditor.superclass.onEnable.call(this);
6265     },
6266
6267     setReadOnly: function(readOnly){
6268
6269         Ext.form.HtmlEditor.superclass.setReadOnly.call(this, readOnly);
6270         if(this.initialized){\r
6271             this.setDesignMode(!readOnly);\r
6272             var bd = this.getEditorBody();\r
6273             if(bd){\r
6274                 bd.style.cursor = this.readOnly ? 'default' : 'text';\r
6275             }\r
6276             this.disableItems(readOnly);\r
6277         }\r
6278     },
6279
6280     /**
6281      * Protected method that will not generally be called directly. It
6282      * is called when the editor initializes the iframe with HTML contents. Override this method if you
6283      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
6284      *\r
6285      * Note: IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility\r
6286      */
6287     getDocMarkup : function(){
6288         return '<html><head><meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
6289     },
6290
6291     // private
6292     getEditorBody : function(){
6293         var doc = this.getDoc();
6294         return doc.body || doc.documentElement;
6295     },
6296
6297     // private
6298     getDoc : function(){
6299         return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
6300     },
6301
6302     // private
6303     getWin : function(){
6304         return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
6305     },
6306
6307     // private
6308     onRender : function(ct, position){
6309         Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
6310         this.el.dom.style.border = '0 none';
6311         this.el.dom.setAttribute('tabIndex', -1);
6312         this.el.addClass('x-hidden');
6313         if(Ext.isIE){ // fix IE 1px bogus margin
6314             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
6315         }
6316         this.wrap = this.el.wrap({
6317             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
6318         });
6319
6320         this.createToolbar(this);
6321
6322         this.disableItems(true);
6323
6324         this.tb.doLayout();
6325
6326         this.createIFrame();
6327
6328         if(!this.width){
6329             var sz = this.el.getSize();
6330             this.setSize(sz.width, this.height || sz.height);
6331         }
6332         this.resizeEl = this.positionEl = this.wrap;
6333     },
6334
6335     createIFrame: function(){
6336         var iframe = document.createElement('iframe');
6337         iframe.name = Ext.id();
6338         iframe.frameBorder = '0';
6339         iframe.style.overflow = 'auto';\r
6340
6341         this.wrap.dom.appendChild(iframe);
6342         this.iframe = iframe;
6343
6344         this.monitorTask = Ext.TaskMgr.start({
6345             run: this.checkDesignMode,
6346             scope: this,
6347             interval:100
6348         });
6349     },
6350
6351     initFrame : function(){
6352         Ext.TaskMgr.stop(this.monitorTask);
6353         var doc = this.getDoc();
6354         this.win = this.getWin();
6355
6356         doc.open();
6357         doc.write(this.getDocMarkup());
6358         doc.close();
6359
6360         var task = { // must defer to wait for browser to be ready
6361             run : function(){
6362                 var doc = this.getDoc();
6363                 if(doc.body || doc.readyState == 'complete'){
6364                     Ext.TaskMgr.stop(task);
6365                     this.setDesignMode(true);
6366                     this.initEditor.defer(10, this);
6367                 }
6368             },
6369             interval : 10,
6370             duration:10000,
6371             scope: this
6372         };
6373         Ext.TaskMgr.start(task);
6374     },
6375
6376
6377     checkDesignMode : function(){
6378         if(this.wrap && this.wrap.dom.offsetWidth){
6379             var doc = this.getDoc();
6380             if(!doc){
6381                 return;
6382             }
6383             if(!doc.editorInitialized || this.getDesignMode() != 'on'){
6384                 this.initFrame();
6385             }
6386         }
6387     },
6388 \r
6389     /* private\r
6390      * set current design mode. To enable, mode can be true or 'on', off otherwise\r
6391      */\r
6392     setDesignMode : function(mode){\r
6393         var doc ;\r
6394         if(doc = this.getDoc()){\r
6395             if(this.readOnly){\r
6396                 mode = false;\r
6397             }\r
6398             doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';\r
6399         }\r
6400 \r
6401     },\r
6402 \r
6403     // private\r
6404     getDesignMode : function(){
6405         var doc = this.getDoc();\r
6406         if(!doc){ return ''; }\r
6407         return String(doc.designMode).toLowerCase();\r
6408 \r
6409     },\r
6410 \r
6411     disableItems: function(disabled){
6412         if(this.fontSelect){
6413             this.fontSelect.dom.disabled = disabled;
6414         }
6415         this.tb.items.each(function(item){
6416             if(item.getItemId() != 'sourceedit'){
6417                 item.setDisabled(disabled);
6418             }
6419         });
6420     },
6421
6422     // private
6423     onResize : function(w, h){
6424         Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
6425         if(this.el && this.iframe){
6426             if(Ext.isNumber(w)){
6427                 var aw = w - this.wrap.getFrameWidth('lr');
6428                 this.el.setWidth(aw);
6429                 this.tb.setWidth(aw);
6430                 this.iframe.style.width = Math.max(aw, 0) + 'px';
6431             }
6432             if(Ext.isNumber(h)){
6433                 var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
6434                 this.el.setHeight(ah);
6435                 this.iframe.style.height = Math.max(ah, 0) + 'px';
6436                 var bd = this.getEditorBody();
6437                 if(bd){
6438                     bd.style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
6439                 }
6440             }
6441         }
6442     },
6443
6444     /**
6445      * Toggles the editor between standard and source edit mode.
6446      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
6447      */
6448     toggleSourceEdit : function(sourceEditMode){
6449         if(sourceEditMode === undefined){
6450             sourceEditMode = !this.sourceEditMode;
6451         }
6452         this.sourceEditMode = sourceEditMode === true;
6453         var btn = this.tb.getComponent('sourceedit');
6454
6455         if(btn.pressed !== this.sourceEditMode){
6456             btn.toggle(this.sourceEditMode);
6457             if(!btn.xtbHidden){
6458                 return;
6459             }
6460         }
6461         if(this.sourceEditMode){
6462             this.disableItems(true);
6463             this.syncValue();
6464             this.iframe.className = 'x-hidden';
6465             this.el.removeClass('x-hidden');
6466             this.el.dom.removeAttribute('tabIndex');
6467             this.el.focus();
6468         }else{
6469             if(this.initialized){
6470                 this.disableItems(this.readOnly);
6471             }
6472             this.pushValue();
6473             this.iframe.className = '';
6474             this.el.addClass('x-hidden');
6475             this.el.dom.setAttribute('tabIndex', -1);
6476             this.deferFocus();
6477         }
6478         var lastSize = this.lastSize;
6479         if(lastSize){
6480             delete this.lastSize;
6481             this.setSize(lastSize);
6482         }
6483         this.fireEvent('editmodechange', this, this.sourceEditMode);
6484     },
6485
6486     // private used internally
6487     createLink : function(){
6488         var url = prompt(this.createLinkText, this.defaultLinkValue);
6489         if(url && url != 'http:/'+'/'){
6490             this.relayCmd('createlink', url);
6491         }
6492     },
6493
6494     // private
6495     initEvents : function(){
6496         this.originalValue = this.getValue();
6497     },
6498
6499     /**
6500      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
6501      * @method
6502      */
6503     markInvalid : Ext.emptyFn,
6504
6505     /**
6506      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
6507      * @method
6508      */
6509     clearInvalid : Ext.emptyFn,
6510
6511     // docs inherit from Field
6512     setValue : function(v){
6513         Ext.form.HtmlEditor.superclass.setValue.call(this, v);
6514         this.pushValue();
6515         return this;
6516     },
6517
6518     /**
6519      * Protected method that will not generally be called directly. If you need/want
6520      * custom HTML cleanup, this is the method you should override.
6521      * @param {String} html The HTML to be cleaned
6522      * @return {String} The cleaned HTML
6523      */
6524     cleanHtml: function(html) {
6525         html = String(html);
6526         if(Ext.isWebKit){ // strip safari nonsense
6527             html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
6528         }
6529
6530         /*
6531          * Neat little hack. Strips out all the non-digit characters from the default
6532          * value and compares it to the character code of the first character in the string
6533          * because it can cause encoding issues when posted to the server.
6534          */
6535         if(html.charCodeAt(0) == this.defaultValue.replace(/\D/g, '')){
6536             html = html.substring(1);
6537         }
6538         return html;
6539     },
6540
6541     /**
6542      * Protected method that will not generally be called directly. Syncs the contents
6543      * of the editor iframe with the textarea.
6544      */
6545     syncValue : function(){
6546         if(this.initialized){
6547             var bd = this.getEditorBody();
6548             var html = bd.innerHTML;
6549             if(Ext.isWebKit){
6550                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
6551                 var m = bs.match(/text-align:(.*?);/i);
6552                 if(m && m[1]){
6553                     html = '<div style="'+m[0]+'">' + html + '</div>';
6554                 }
6555             }
6556             html = this.cleanHtml(html);
6557             if(this.fireEvent('beforesync', this, html) !== false){
6558                 this.el.dom.value = html;
6559                 this.fireEvent('sync', this, html);
6560             }
6561         }
6562     },
6563
6564     //docs inherit from Field
6565     getValue : function() {
6566         this[this.sourceEditMode ? 'pushValue' : 'syncValue']();
6567         return Ext.form.HtmlEditor.superclass.getValue.call(this);
6568     },
6569
6570     /**
6571      * Protected method that will not generally be called directly. Pushes the value of the textarea
6572      * into the iframe editor.
6573      */
6574     pushValue : function(){
6575         if(this.initialized){
6576             var v = this.el.dom.value;
6577             if(!this.activated && v.length < 1){
6578                 v = this.defaultValue;
6579             }
6580             if(this.fireEvent('beforepush', this, v) !== false){
6581                 this.getEditorBody().innerHTML = v;
6582                 if(Ext.isGecko){
6583                     // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
6584                     this.setDesignMode(false);  //toggle off first\r
6585 \r
6586                 }
6587                 this.setDesignMode(true);\r
6588                 this.fireEvent('push', this, v);
6589             }
6590 \r
6591         }
6592     },
6593
6594     // private
6595     deferFocus : function(){
6596         this.focus.defer(10, this);
6597     },
6598
6599     // docs inherit from Field
6600     focus : function(){
6601         if(this.win && !this.sourceEditMode){
6602             this.win.focus();
6603         }else{
6604             this.el.focus();
6605         }
6606     },
6607
6608     // private
6609     initEditor : function(){
6610         //Destroying the component during/before initEditor can cause issues.
6611         try{
6612             var dbody = this.getEditorBody(),
6613                 ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat'),
6614                 doc,
6615                 fn;
6616
6617             ss['background-attachment'] = 'fixed'; // w3c
6618             dbody.bgProperties = 'fixed'; // ie
6619
6620             Ext.DomHelper.applyStyles(dbody, ss);
6621
6622             doc = this.getDoc();
6623
6624             if(doc){
6625                 try{
6626                     Ext.EventManager.removeAll(doc);
6627                 }catch(e){}
6628             }
6629
6630             /*
6631              * We need to use createDelegate here, because when using buffer, the delayed task is added
6632              * as a property to the function. When the listener is removed, the task is deleted from the function.
6633              * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors
6634              * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.
6635              */
6636             fn = this.onEditorEvent.createDelegate(this);
6637             Ext.EventManager.on(doc, {
6638                 mousedown: fn,
6639                 dblclick: fn,
6640                 click: fn,
6641                 keyup: fn,
6642                 buffer:100
6643             });
6644
6645             if(Ext.isGecko){
6646                 Ext.EventManager.on(doc, 'keypress', this.applyCommand, this);
6647             }
6648             if(Ext.isIE || Ext.isWebKit || Ext.isOpera){
6649                 Ext.EventManager.on(doc, 'keydown', this.fixKeys, this);
6650             }
6651             doc.editorInitialized = true;
6652             this.initialized = true;
6653             this.pushValue();
6654             this.setReadOnly(this.readOnly);
6655             this.fireEvent('initialize', this);
6656         }catch(e){}
6657     },
6658
6659     // private
6660     onDestroy : function(){
6661         if(this.monitorTask){
6662             Ext.TaskMgr.stop(this.monitorTask);
6663         }
6664         if(this.rendered){
6665             Ext.destroy(this.tb);
6666             var doc = this.getDoc();
6667             if(doc){
6668                 try{
6669                     Ext.EventManager.removeAll(doc);
6670                     for (var prop in doc){
6671                         delete doc[prop];
6672                     }
6673                 }catch(e){}
6674             }
6675             if(this.wrap){
6676                 this.wrap.dom.innerHTML = '';
6677                 this.wrap.remove();
6678             }
6679         }
6680
6681         if(this.el){
6682             this.el.removeAllListeners();
6683             this.el.remove();
6684         }
6685         this.purgeListeners();
6686     },
6687
6688     // private
6689     onFirstFocus : function(){
6690         this.activated = true;
6691         this.disableItems(this.readOnly);
6692         if(Ext.isGecko){ // prevent silly gecko errors
6693             this.win.focus();
6694             var s = this.win.getSelection();
6695             if(!s.focusNode || s.focusNode.nodeType != 3){
6696                 var r = s.getRangeAt(0);
6697                 r.selectNodeContents(this.getEditorBody());
6698                 r.collapse(true);
6699                 this.deferFocus();
6700             }
6701             try{
6702                 this.execCmd('useCSS', true);
6703                 this.execCmd('styleWithCSS', false);
6704             }catch(e){}
6705         }
6706         this.fireEvent('activate', this);
6707     },
6708
6709     // private
6710     adjustFont: function(btn){
6711         var adjust = btn.getItemId() == 'increasefontsize' ? 1 : -1,
6712             doc = this.getDoc(),
6713             v = parseInt(doc.queryCommandValue('FontSize') || 2, 10);
6714         if((Ext.isSafari && !Ext.isSafari2) || Ext.isChrome || Ext.isAir){
6715             // Safari 3 values
6716             // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
6717             if(v <= 10){
6718                 v = 1 + adjust;
6719             }else if(v <= 13){
6720                 v = 2 + adjust;
6721             }else if(v <= 16){
6722                 v = 3 + adjust;
6723             }else if(v <= 18){
6724                 v = 4 + adjust;
6725             }else if(v <= 24){
6726                 v = 5 + adjust;
6727             }else {
6728                 v = 6 + adjust;
6729             }
6730             v = v.constrain(1, 6);
6731         }else{
6732             if(Ext.isSafari){ // safari
6733                 adjust *= 2;
6734             }
6735             v = Math.max(1, v+adjust) + (Ext.isSafari ? 'px' : 0);
6736         }
6737         this.execCmd('FontSize', v);
6738     },
6739
6740     // private
6741     onEditorEvent : function(e){
6742         this.updateToolbar();
6743     },
6744
6745
6746     /**
6747      * Protected method that will not generally be called directly. It triggers
6748      * a toolbar update by reading the markup state of the current selection in the editor.
6749      */
6750     updateToolbar: function(){
6751
6752         if(this.readOnly){
6753             return;
6754         }
6755
6756         if(!this.activated){
6757             this.onFirstFocus();
6758             return;
6759         }
6760
6761         var btns = this.tb.items.map,
6762             doc = this.getDoc();
6763
6764         if(this.enableFont && !Ext.isSafari2){
6765             var name = (doc.queryCommandValue('FontName')||this.defaultFont).toLowerCase();
6766             if(name != this.fontSelect.dom.value){
6767                 this.fontSelect.dom.value = name;
6768             }
6769         }
6770         if(this.enableFormat){
6771             btns.bold.toggle(doc.queryCommandState('bold'));
6772             btns.italic.toggle(doc.queryCommandState('italic'));
6773             btns.underline.toggle(doc.queryCommandState('underline'));
6774         }
6775         if(this.enableAlignments){
6776             btns.justifyleft.toggle(doc.queryCommandState('justifyleft'));
6777             btns.justifycenter.toggle(doc.queryCommandState('justifycenter'));
6778             btns.justifyright.toggle(doc.queryCommandState('justifyright'));
6779         }
6780         if(!Ext.isSafari2 && this.enableLists){
6781             btns.insertorderedlist.toggle(doc.queryCommandState('insertorderedlist'));
6782             btns.insertunorderedlist.toggle(doc.queryCommandState('insertunorderedlist'));
6783         }
6784
6785         Ext.menu.MenuMgr.hideAll();
6786
6787         this.syncValue();
6788     },
6789
6790     // private
6791     relayBtnCmd : function(btn){
6792         this.relayCmd(btn.getItemId());
6793     },
6794
6795     /**
6796      * Executes a Midas editor command on the editor document and performs necessary focus and
6797      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
6798      * @param {String} cmd The Midas command
6799      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
6800      */
6801     relayCmd : function(cmd, value){
6802         (function(){
6803             this.focus();
6804             this.execCmd(cmd, value);
6805             this.updateToolbar();
6806         }).defer(10, this);
6807     },
6808
6809     /**
6810      * Executes a Midas editor command directly on the editor document.
6811      * For visual commands, you should use {@link #relayCmd} instead.
6812      * <b>This should only be called after the editor is initialized.</b>
6813      * @param {String} cmd The Midas command
6814      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
6815      */
6816     execCmd : function(cmd, value){
6817         var doc = this.getDoc();
6818         doc.execCommand(cmd, false, value === undefined ? null : value);
6819         this.syncValue();
6820     },
6821
6822     // private
6823     applyCommand : function(e){
6824         if(e.ctrlKey){
6825             var c = e.getCharCode(), cmd;
6826             if(c > 0){
6827                 c = String.fromCharCode(c);
6828                 switch(c){
6829                     case 'b':
6830                         cmd = 'bold';
6831                     break;
6832                     case 'i':
6833                         cmd = 'italic';
6834                     break;
6835                     case 'u':
6836                         cmd = 'underline';
6837                     break;
6838                 }
6839                 if(cmd){
6840                     this.win.focus();
6841                     this.execCmd(cmd);
6842                     this.deferFocus();
6843                     e.preventDefault();
6844                 }
6845             }
6846         }
6847     },
6848
6849     /**
6850      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
6851      * to insert text.
6852      * @param {String} text
6853      */
6854     insertAtCursor : function(text){
6855         if(!this.activated){
6856             return;
6857         }
6858         if(Ext.isIE){
6859             this.win.focus();
6860             var doc = this.getDoc(),
6861                 r = doc.selection.createRange();
6862             if(r){
6863                 r.pasteHTML(text);
6864                 this.syncValue();
6865                 this.deferFocus();
6866             }
6867         }else{
6868             this.win.focus();
6869             this.execCmd('InsertHTML', text);
6870             this.deferFocus();
6871         }
6872     },
6873
6874     // private
6875     fixKeys : function(){ // load time branching for fastest keydown performance
6876         if(Ext.isIE){
6877             return function(e){
6878                 var k = e.getKey(),
6879                     doc = this.getDoc(),
6880                         r;
6881                 if(k == e.TAB){
6882                     e.stopEvent();
6883                     r = doc.selection.createRange();
6884                     if(r){
6885                         r.collapse(true);
6886                         r.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');
6887                         this.deferFocus();
6888                     }
6889                 }else if(k == e.ENTER){
6890                     r = doc.selection.createRange();
6891                     if(r){
6892                         var target = r.parentElement();
6893                         if(!target || target.tagName.toLowerCase() != 'li'){
6894                             e.stopEvent();
6895                             r.pasteHTML('<br />');
6896                             r.collapse(false);
6897                             r.select();
6898                         }
6899                     }
6900                 }
6901             };
6902         }else if(Ext.isOpera){
6903             return function(e){
6904                 var k = e.getKey();
6905                 if(k == e.TAB){
6906                     e.stopEvent();
6907                     this.win.focus();
6908                     this.execCmd('InsertHTML','&nbsp;&nbsp;&nbsp;&nbsp;');
6909                     this.deferFocus();
6910                 }
6911             };
6912         }else if(Ext.isWebKit){
6913             return function(e){
6914                 var k = e.getKey();
6915                 if(k == e.TAB){
6916                     e.stopEvent();
6917                     this.execCmd('InsertText','\t');
6918                     this.deferFocus();
6919                 }else if(k == e.ENTER){
6920                     e.stopEvent();
6921                     this.execCmd('InsertHtml','<br /><br />');
6922                     this.deferFocus();
6923                 }
6924              };
6925         }
6926     }(),
6927
6928     /**
6929      * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>
6930      * @return {Ext.Toolbar}
6931      */
6932     getToolbar : function(){
6933         return this.tb;
6934     },
6935
6936     /**
6937      * Object collection of toolbar tooltips for the buttons in the editor. The key
6938      * is the command id associated with that button and the value is a valid QuickTips object.
6939      * For example:
6940 <pre><code>
6941 {
6942     bold : {
6943         title: 'Bold (Ctrl+B)',
6944         text: 'Make the selected text bold.',
6945         cls: 'x-html-editor-tip'
6946     },
6947     italic : {
6948         title: 'Italic (Ctrl+I)',
6949         text: 'Make the selected text italic.',
6950         cls: 'x-html-editor-tip'
6951     },
6952     ...
6953 </code></pre>
6954     * @type Object
6955      */
6956     buttonTips : {
6957         bold : {
6958             title: 'Bold (Ctrl+B)',
6959             text: 'Make the selected text bold.',
6960             cls: 'x-html-editor-tip'
6961         },
6962         italic : {
6963             title: 'Italic (Ctrl+I)',
6964             text: 'Make the selected text italic.',
6965             cls: 'x-html-editor-tip'
6966         },
6967         underline : {
6968             title: 'Underline (Ctrl+U)',
6969             text: 'Underline the selected text.',
6970             cls: 'x-html-editor-tip'
6971         },
6972         increasefontsize : {
6973             title: 'Grow Text',
6974             text: 'Increase the font size.',
6975             cls: 'x-html-editor-tip'
6976         },
6977         decreasefontsize : {
6978             title: 'Shrink Text',
6979             text: 'Decrease the font size.',
6980             cls: 'x-html-editor-tip'
6981         },
6982         backcolor : {
6983             title: 'Text Highlight Color',
6984             text: 'Change the background color of the selected text.',
6985             cls: 'x-html-editor-tip'
6986         },
6987         forecolor : {
6988             title: 'Font Color',
6989             text: 'Change the color of the selected text.',
6990             cls: 'x-html-editor-tip'
6991         },
6992         justifyleft : {
6993             title: 'Align Text Left',
6994             text: 'Align text to the left.',
6995             cls: 'x-html-editor-tip'
6996         },
6997         justifycenter : {
6998             title: 'Center Text',
6999             text: 'Center text in the editor.',
7000             cls: 'x-html-editor-tip'
7001         },
7002         justifyright : {
7003             title: 'Align Text Right',
7004             text: 'Align text to the right.',
7005             cls: 'x-html-editor-tip'
7006         },
7007         insertunorderedlist : {
7008             title: 'Bullet List',
7009             text: 'Start a bulleted list.',
7010             cls: 'x-html-editor-tip'
7011         },
7012         insertorderedlist : {
7013             title: 'Numbered List',
7014             text: 'Start a numbered list.',
7015             cls: 'x-html-editor-tip'
7016         },
7017         createlink : {
7018             title: 'Hyperlink',
7019             text: 'Make the selected text a hyperlink.',
7020             cls: 'x-html-editor-tip'
7021         },
7022         sourceedit : {
7023             title: 'Source Edit',
7024             text: 'Switch to source editing mode.',
7025             cls: 'x-html-editor-tip'
7026         }
7027     }
7028
7029     // hide stuff that is not compatible
7030     /**
7031      * @event blur
7032      * @hide
7033      */
7034     /**
7035      * @event change
7036      * @hide
7037      */
7038     /**
7039      * @event focus
7040      * @hide
7041      */
7042     /**
7043      * @event specialkey
7044      * @hide
7045      */
7046     /**
7047      * @cfg {String} fieldClass @hide
7048      */
7049     /**
7050      * @cfg {String} focusClass @hide
7051      */
7052     /**
7053      * @cfg {String} autoCreate @hide
7054      */
7055     /**
7056      * @cfg {String} inputType @hide
7057      */
7058     /**
7059      * @cfg {String} invalidClass @hide
7060      */
7061     /**
7062      * @cfg {String} invalidText @hide
7063      */
7064     /**
7065      * @cfg {String} msgFx @hide
7066      */
7067     /**
7068      * @cfg {String} validateOnBlur @hide
7069      */
7070     /**
7071      * @cfg {Boolean} allowDomMove  @hide
7072      */
7073     /**
7074      * @cfg {String} applyTo @hide
7075      */
7076     /**
7077      * @cfg {String} autoHeight  @hide
7078      */
7079     /**
7080      * @cfg {String} autoWidth  @hide
7081      */
7082     /**
7083      * @cfg {String} cls  @hide
7084      */
7085     /**
7086      * @cfg {String} disabled  @hide
7087      */
7088     /**
7089      * @cfg {String} disabledClass  @hide
7090      */
7091     /**
7092      * @cfg {String} msgTarget  @hide
7093      */
7094     /**
7095      * @cfg {String} readOnly  @hide
7096      */
7097     /**
7098      * @cfg {String} style  @hide
7099      */
7100     /**
7101      * @cfg {String} validationDelay  @hide
7102      */
7103     /**
7104      * @cfg {String} validationEvent  @hide
7105      */
7106     /**
7107      * @cfg {String} tabIndex  @hide
7108      */
7109     /**
7110      * @property disabled
7111      * @hide
7112      */
7113     /**
7114      * @method applyToMarkup
7115      * @hide
7116      */
7117     /**
7118      * @method disable
7119      * @hide
7120      */
7121     /**
7122      * @method enable
7123      * @hide
7124      */
7125     /**
7126      * @method validate
7127      * @hide
7128      */
7129     /**
7130      * @event valid
7131      * @hide
7132      */
7133     /**
7134      * @method setDisabled
7135      * @hide
7136      */
7137     /**
7138      * @cfg keys
7139      * @hide
7140      */
7141 });
7142 Ext.reg('htmleditor', Ext.form.HtmlEditor);/**\r
7143  * @class Ext.form.TimeField\r
7144  * @extends Ext.form.ComboBox\r
7145  * Provides a time input field with a time dropdown and automatic time validation.  Example usage:\r
7146  * <pre><code>\r
7147 new Ext.form.TimeField({\r
7148     minValue: '9:00 AM',\r
7149     maxValue: '6:00 PM',\r
7150     increment: 30\r
7151 });\r
7152 </code></pre>\r
7153  * @constructor\r
7154  * Create a new TimeField\r
7155  * @param {Object} config\r
7156  * @xtype timefield\r
7157  */\r
7158 Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {\r
7159     /**\r
7160      * @cfg {Date/String} minValue\r
7161      * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string \r
7162      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).\r
7163      */\r
7164     minValue : undefined,\r
7165     /**\r
7166      * @cfg {Date/String} maxValue\r
7167      * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string \r
7168      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).\r
7169      */\r
7170     maxValue : undefined,\r
7171     /**\r
7172      * @cfg {String} minText\r
7173      * The error text to display when the date in the cell is before minValue (defaults to\r
7174      * 'The time in this field must be equal to or after {0}').\r
7175      */\r
7176     minText : "The time in this field must be equal to or after {0}",\r
7177     /**\r
7178      * @cfg {String} maxText\r
7179      * The error text to display when the time is after maxValue (defaults to\r
7180      * 'The time in this field must be equal to or before {0}').\r
7181      */\r
7182     maxText : "The time in this field must be equal to or before {0}",\r
7183     /**\r
7184      * @cfg {String} invalidText\r
7185      * The error text to display when the time in the field is invalid (defaults to\r
7186      * '{value} is not a valid time').\r
7187      */\r
7188     invalidText : "{0} is not a valid time",\r
7189     /**\r
7190      * @cfg {String} format\r
7191      * The default time format string which can be overriden for localization support.  The format must be\r
7192      * valid according to {@link Date#parseDate} (defaults to 'g:i A', e.g., '3:15 PM').  For 24-hour time\r
7193      * format try 'H:i' instead.\r
7194      */\r
7195     format : "g:i A",\r
7196     /**\r
7197      * @cfg {String} altFormats\r
7198      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined\r
7199      * 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
7200      */\r
7201     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
7202     /**\r
7203      * @cfg {Number} increment\r
7204      * The number of minutes between each time value in the list (defaults to 15).\r
7205      */\r
7206     increment: 15,\r
7207 \r
7208     // private override\r
7209     mode: 'local',\r
7210     // private override\r
7211     triggerAction: 'all',\r
7212     // private override\r
7213     typeAhead: false,\r
7214     \r
7215     // private - This is the date to use when generating time values in the absence of either minValue\r
7216     // or maxValue.  Using the current date causes DST issues on DST boundary dates, so this is an \r
7217     // arbitrary "safe" date that can be any date aside from DST boundary dates.\r
7218     initDate: '1/1/2008',\r
7219 \r
7220     // private\r
7221     initComponent : function(){\r
7222         if(Ext.isDefined(this.minValue)){\r
7223             this.setMinValue(this.minValue, true);\r
7224         }\r
7225         if(Ext.isDefined(this.maxValue)){\r
7226             this.setMaxValue(this.maxValue, true);\r
7227         }\r
7228         if(!this.store){\r
7229             this.generateStore(true);\r
7230         }\r
7231         Ext.form.TimeField.superclass.initComponent.call(this);\r
7232     },\r
7233     \r
7234     /**\r
7235      * Replaces any existing {@link #minValue} with the new time and refreshes the store.\r
7236      * @param {Date/String} value The minimum time that can be selected\r
7237      */\r
7238     setMinValue: function(value, /* private */ initial){\r
7239         this.setLimit(value, true, initial);\r
7240         return this;\r
7241     },\r
7242 \r
7243     /**\r
7244      * Replaces any existing {@link #maxValue} with the new time and refreshes the store.\r
7245      * @param {Date/String} value The maximum time that can be selected\r
7246      */\r
7247     setMaxValue: function(value, /* private */ initial){\r
7248         this.setLimit(value, false, initial);\r
7249         return this;\r
7250     },\r
7251     \r
7252     // private\r
7253     generateStore: function(initial){\r
7254         var min = this.minValue || new Date(this.initDate).clearTime(),\r
7255             max = this.maxValue || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1),\r
7256             times = [];\r
7257             \r
7258         while(min <= max){\r
7259             times.push(min.dateFormat(this.format));\r
7260             min = min.add('mi', this.increment);\r
7261         }\r
7262         this.bindStore(times, initial);\r
7263     },\r
7264 \r
7265     // private\r
7266     setLimit: function(value, isMin, initial){\r
7267         var d;\r
7268         if(Ext.isString(value)){\r
7269             d = this.parseDate(value);\r
7270         }else if(Ext.isDate(value)){\r
7271             d = value;\r
7272         }\r
7273         if(d){\r
7274             var val = new Date(this.initDate).clearTime();\r
7275             val.setHours(d.getHours(), d.getMinutes(), isMin ? 0 : 59, 0);\r
7276             this[isMin ? 'minValue' : 'maxValue'] = val;\r
7277             if(!initial){\r
7278                 this.generateStore();\r
7279             }\r
7280         }\r
7281     },\r
7282     \r
7283     // inherited docs\r
7284     getValue : function(){\r
7285         var v = Ext.form.TimeField.superclass.getValue.call(this);\r
7286         return this.formatDate(this.parseDate(v)) || '';\r
7287     },\r
7288 \r
7289     // inherited docs\r
7290     setValue : function(value){\r
7291         return Ext.form.TimeField.superclass.setValue.call(this, this.formatDate(this.parseDate(value)));\r
7292     },\r
7293 \r
7294     // private overrides\r
7295     validateValue : Ext.form.DateField.prototype.validateValue,\r
7296     parseDate : Ext.form.DateField.prototype.parseDate,\r
7297     formatDate : Ext.form.DateField.prototype.formatDate,\r
7298 \r
7299     // private\r
7300     beforeBlur : function(){\r
7301         var v = this.parseDate(this.getRawValue());\r
7302         if(v){\r
7303             this.setValue(v.dateFormat(this.format));\r
7304         }\r
7305         Ext.form.TimeField.superclass.beforeBlur.call(this);\r
7306     }\r
7307 \r
7308     /**\r
7309      * @cfg {Boolean} grow @hide\r
7310      */\r
7311     /**\r
7312      * @cfg {Number} growMin @hide\r
7313      */\r
7314     /**\r
7315      * @cfg {Number} growMax @hide\r
7316      */\r
7317     /**\r
7318      * @hide\r
7319      * @method autoSize\r
7320      */\r
7321 });\r
7322 Ext.reg('timefield', Ext.form.TimeField);/**
7323  * @class Ext.form.Label
7324  * @extends Ext.BoxComponent
7325  * Basic Label field.
7326  * @constructor
7327  * Creates a new Label
7328  * @param {Ext.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7329  * 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
7330  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7331  * @xtype label
7332  */
7333 Ext.form.Label = Ext.extend(Ext.BoxComponent, {
7334     /**
7335      * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
7336      * tags within the label's innerHTML, use the {@link #html} config instead.
7337      */
7338     /**
7339      * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
7340      * attribute. If not specified, the attribute will not be added to the label.
7341      */
7342     /**
7343      * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
7344      * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
7345      */
7346
7347     // private
7348     onRender : function(ct, position){
7349         if(!this.el){
7350             this.el = document.createElement('label');
7351             this.el.id = this.getId();
7352             this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
7353             if(this.forId){
7354                 this.el.setAttribute('for', this.forId);
7355             }
7356         }
7357         Ext.form.Label.superclass.onRender.call(this, ct, position);
7358     },
7359
7360     /**
7361      * Updates the label's innerHTML with the specified string.
7362      * @param {String} text The new label text
7363      * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
7364      * to the label (defaults to true which encodes the value). This might be useful if you want to include
7365      * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
7366      * @return {Label} this
7367      */
7368     setText : function(t, encode){
7369         var e = encode === false;
7370         this[!e ? 'text' : 'html'] = t;
7371         delete this[e ? 'text' : 'html'];
7372         if(this.rendered){
7373             this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
7374         }
7375         return this;
7376     }
7377 });
7378
7379 Ext.reg('label', Ext.form.Label);/**
7380  * @class Ext.form.Action
7381  * <p>The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.</p>
7382  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
7383  * the Form needs to perform an action such as submit or load. The Configuration options
7384  * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit},
7385  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}</p>
7386  * <p>The instance of Action which performed the action is passed to the success
7387  * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit},
7388  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}),
7389  * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and
7390  * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.</p>
7391  */
7392 Ext.form.Action = function(form, options){
7393     this.form = form;
7394     this.options = options || {};
7395 };
7396
7397 /**
7398  * Failure type returned when client side validation of the Form fails
7399  * thus aborting a submit action. Client side validation is performed unless
7400  * {@link #clientValidation} is explicitly set to <tt>false</tt>.
7401  * @type {String}
7402  * @static
7403  */
7404 Ext.form.Action.CLIENT_INVALID = 'client';
7405 /**
7406  * <p>Failure type returned when server side processing fails and the {@link #result}'s
7407  * <tt style="font-weight:bold">success</tt> property is set to <tt>false</tt>.</p>
7408  * <p>In the case of a form submission, field-specific error messages may be returned in the
7409  * {@link #result}'s <tt style="font-weight:bold">errors</tt> property.</p>
7410  * @type {String}
7411  * @static
7412  */
7413 Ext.form.Action.SERVER_INVALID = 'server';
7414 /**
7415  * Failure type returned when a communication error happens when attempting
7416  * to send a request to the remote server. The {@link #response} may be examined to
7417  * provide further information.
7418  * @type {String}
7419  * @static
7420  */
7421 Ext.form.Action.CONNECT_FAILURE = 'connect';
7422 /**
7423  * Failure type returned when the response's <tt style="font-weight:bold">success</tt>
7424  * property is set to <tt>false</tt>, or no field values are returned in the response's
7425  * <tt style="font-weight:bold">data</tt> property.
7426  * @type {String}
7427  * @static
7428  */
7429 Ext.form.Action.LOAD_FAILURE = 'load';
7430
7431 Ext.form.Action.prototype = {
7432 /**
7433  * @cfg {String} url The URL that the Action is to invoke.
7434  */
7435 /**
7436  * @cfg {Boolean} reset When set to <tt><b>true</b></tt>, causes the Form to be
7437  * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens
7438  * <b>before</b> the {@link #success} callback is called and before the Form's
7439  * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires.
7440  */
7441 /**
7442  * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the
7443  * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method.
7444  */
7445 /**
7446  * @cfg {Mixed} params <p>Extra parameter values to pass. These are added to the Form's
7447  * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's
7448  * input fields.</p>
7449  * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
7450  */
7451 /**
7452  * @cfg {Number} timeout The number of seconds to wait for a server response before
7453  * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified,
7454  * defaults to the configured <tt>{@link Ext.form.BasicForm#timeout timeout}</tt> of the
7455  * {@link Ext.form.BasicForm form}.
7456  */
7457 /**
7458  * @cfg {Function} success The function to call when a valid success return packet is recieved.
7459  * The function is passed the following parameters:<ul class="mdetail-params">
7460  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
7461  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. The {@link #result}
7462  * property of this object may be examined to perform custom postprocessing.</div></li>
7463  * </ul>
7464  */
7465 /**
7466  * @cfg {Function} failure The function to call when a failure packet was recieved, or when an
7467  * error ocurred in the Ajax communication.
7468  * The function is passed the following parameters:<ul class="mdetail-params">
7469  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
7470  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. If an Ajax
7471  * error ocurred, the failure type will be in {@link #failureType}. The {@link #result}
7472  * property of this object may be examined to perform custom postprocessing.</div></li>
7473  * </ul>
7474  */
7475 /**
7476  * @cfg {Object} scope The scope in which to call the callback functions (The <tt>this</tt> reference
7477  * for the callback functions).
7478  */
7479 /**
7480  * @cfg {String} waitMsg The message to be displayed by a call to {@link Ext.MessageBox#wait}
7481  * during the time the action is being processed.
7482  */
7483 /**
7484  * @cfg {String} waitTitle The title to be displayed by a call to {@link Ext.MessageBox#wait}
7485  * during the time the action is being processed.
7486  */
7487
7488 /**
7489  * The type of action this Action instance performs.
7490  * Currently only "submit" and "load" are supported.
7491  * @type {String}
7492  */
7493     type : 'default',
7494 /**
7495  * The type of failure detected will be one of these: {@link #CLIENT_INVALID},
7496  * {@link #SERVER_INVALID}, {@link #CONNECT_FAILURE}, or {@link #LOAD_FAILURE}.  Usage:
7497  * <pre><code>
7498 var fp = new Ext.form.FormPanel({
7499 ...
7500 buttons: [{
7501     text: 'Save',
7502     formBind: true,
7503     handler: function(){
7504         if(fp.getForm().isValid()){
7505             fp.getForm().submit({
7506                 url: 'form-submit.php',
7507                 waitMsg: 'Submitting your data...',
7508                 success: function(form, action){
7509                     // server responded with success = true
7510                     var result = action.{@link #result};
7511                 },
7512                 failure: function(form, action){
7513                     if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
7514                         Ext.Msg.alert('Error',
7515                             'Status:'+action.{@link #response}.status+': '+
7516                             action.{@link #response}.statusText);
7517                     }
7518                     if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
7519                         // server responded with success = false
7520                         Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
7521                     }
7522                 }
7523             });
7524         }
7525     }
7526 },{
7527     text: 'Reset',
7528     handler: function(){
7529         fp.getForm().reset();
7530     }
7531 }]
7532  * </code></pre>
7533  * @property failureType
7534  * @type {String}
7535  */
7536  /**
7537  * The XMLHttpRequest object used to perform the action.
7538  * @property response
7539  * @type {Object}
7540  */
7541  /**
7542  * The decoded response object containing a boolean <tt style="font-weight:bold">success</tt> property and
7543  * other, action-specific properties.
7544  * @property result
7545  * @type {Object}
7546  */
7547
7548     // interface method
7549     run : function(options){
7550
7551     },
7552
7553     // interface method
7554     success : function(response){
7555
7556     },
7557
7558     // interface method
7559     handleResponse : function(response){
7560
7561     },
7562
7563     // default connection failure
7564     failure : function(response){
7565         this.response = response;
7566         this.failureType = Ext.form.Action.CONNECT_FAILURE;
7567         this.form.afterAction(this, false);
7568     },
7569
7570     // private
7571     // shared code among all Actions to validate that there was a response
7572     // with either responseText or responseXml
7573     processResponse : function(response){
7574         this.response = response;
7575         if(!response.responseText && !response.responseXML){
7576             return true;
7577         }
7578         this.result = this.handleResponse(response);
7579         return this.result;
7580     },
7581
7582     // utility functions used internally
7583     getUrl : function(appendParams){
7584         var url = this.options.url || this.form.url || this.form.el.dom.action;
7585         if(appendParams){
7586             var p = this.getParams();
7587             if(p){
7588                 url = Ext.urlAppend(url, p);
7589             }
7590         }
7591         return url;
7592     },
7593
7594     // private
7595     getMethod : function(){
7596         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7597     },
7598
7599     // private
7600     getParams : function(){
7601         var bp = this.form.baseParams;
7602         var p = this.options.params;
7603         if(p){
7604             if(typeof p == "object"){
7605                 p = Ext.urlEncode(Ext.applyIf(p, bp));
7606             }else if(typeof p == 'string' && bp){
7607                 p += '&' + Ext.urlEncode(bp);
7608             }
7609         }else if(bp){
7610             p = Ext.urlEncode(bp);
7611         }
7612         return p;
7613     },
7614
7615     // private
7616     createCallback : function(opts){
7617         var opts = opts || {};
7618         return {
7619             success: this.success,
7620             failure: this.failure,
7621             scope: this,
7622             timeout: (opts.timeout*1000) || (this.form.timeout*1000),
7623             upload: this.form.fileUpload ? this.success : undefined
7624         };
7625     }
7626 };
7627
7628 /**
7629  * @class Ext.form.Action.Submit
7630  * @extends Ext.form.Action
7631  * <p>A class which handles submission of data from {@link Ext.form.BasicForm Form}s
7632  * and processes the returned response.</p>
7633  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
7634  * {@link Ext.form.BasicForm#submit submit}ting.</p>
7635  * <p><u><b>Response Packet Criteria</b></u></p>
7636  * <p>A response packet may contain:
7637  * <div class="mdetail-params"><ul>
7638  * <li><b><code>success</code></b> property : Boolean
7639  * <div class="sub-desc">The <code>success</code> property is required.</div></li>
7640  * <li><b><code>errors</code></b> property : Object
7641  * <div class="sub-desc"><div class="sub-desc">The <code>errors</code> property,
7642  * which is optional, contains error messages for invalid fields.</div></li>
7643  * </ul></div>
7644  * <p><u><b>JSON Packets</b></u></p>
7645  * <p>By default, response packets are assumed to be JSON, so a typical response
7646  * packet may look like this:</p><pre><code>
7647 {
7648     success: false,
7649     errors: {
7650         clientCode: "Client not found",
7651         portOfLoading: "This field must not be null"
7652     }
7653 }</code></pre>
7654  * <p>Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback
7655  * or event handler methods. The object decoded from this JSON is available in the
7656  * {@link Ext.form.Action#result result} property.</p>
7657  * <p>Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:</p><pre><code>
7658     errorReader: new Ext.data.XmlReader({
7659             record : 'field',
7660             success: '@success'
7661         }, [
7662             'id', 'msg'
7663         ]
7664     )
7665 </code></pre>
7666  * <p>then the results may be sent back in XML format:</p><pre><code>
7667 &lt;?xml version="1.0" encoding="UTF-8"?&gt;
7668 &lt;message success="false"&gt;
7669 &lt;errors&gt;
7670     &lt;field&gt;
7671         &lt;id&gt;clientCode&lt;/id&gt;
7672         &lt;msg&gt;&lt;![CDATA[Code not found. &lt;br /&gt;&lt;i&gt;This is a test validation message from the server &lt;/i&gt;]]&gt;&lt;/msg&gt;
7673     &lt;/field&gt;
7674     &lt;field&gt;
7675         &lt;id&gt;portOfLoading&lt;/id&gt;
7676         &lt;msg&gt;&lt;![CDATA[Port not found. &lt;br /&gt;&lt;i&gt;This is a test validation message from the server &lt;/i&gt;]]&gt;&lt;/msg&gt;
7677     &lt;/field&gt;
7678 &lt;/errors&gt;
7679 &lt;/message&gt;
7680 </code></pre>
7681  * <p>Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback
7682  * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.</p>
7683  */
7684 Ext.form.Action.Submit = function(form, options){
7685     Ext.form.Action.Submit.superclass.constructor.call(this, form, options);
7686 };
7687
7688 Ext.extend(Ext.form.Action.Submit, Ext.form.Action, {
7689     /**
7690      * @cfg {Ext.data.DataReader} errorReader <p><b>Optional. JSON is interpreted with
7691      * no need for an errorReader.</b></p>
7692      * <p>A Reader which reads a single record from the returned data. The DataReader's
7693      * <b>success</b> property specifies how submission success is determined. The Record's
7694      * data provides the error messages to apply to any invalid form Fields.</p>
7695      */
7696     /**
7697      * @cfg {boolean} clientValidation Determines whether a Form's fields are validated
7698      * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission.
7699      * Pass <tt>false</tt> in the Form's submit options to prevent this. If not defined, pre-submission field validation
7700      * is performed.
7701      */
7702     type : 'submit',
7703
7704     // private
7705     run : function(){
7706         var o = this.options;
7707         var method = this.getMethod();
7708         var isGet = method == 'GET';
7709         if(o.clientValidation === false || this.form.isValid()){
7710             Ext.Ajax.request(Ext.apply(this.createCallback(o), {
7711                 form:this.form.el.dom,
7712                 url:this.getUrl(isGet),
7713                 method: method,
7714                 headers: o.headers,
7715                 params:!isGet ? this.getParams() : null,
7716                 isUpload: this.form.fileUpload
7717             }));
7718         }else if (o.clientValidation !== false){ // client validation failed
7719             this.failureType = Ext.form.Action.CLIENT_INVALID;
7720             this.form.afterAction(this, false);
7721         }
7722     },
7723
7724     // private
7725     success : function(response){
7726         var result = this.processResponse(response);
7727         if(result === true || result.success){
7728             this.form.afterAction(this, true);
7729             return;
7730         }
7731         if(result.errors){
7732             this.form.markInvalid(result.errors);
7733         }
7734         this.failureType = Ext.form.Action.SERVER_INVALID;
7735         this.form.afterAction(this, false);
7736     },
7737
7738     // private
7739     handleResponse : function(response){
7740         if(this.form.errorReader){
7741             var rs = this.form.errorReader.read(response);
7742             var errors = [];
7743             if(rs.records){
7744                 for(var i = 0, len = rs.records.length; i < len; i++) {
7745                     var r = rs.records[i];
7746                     errors[i] = r.data;
7747                 }
7748             }
7749             if(errors.length < 1){
7750                 errors = null;
7751             }
7752             return {
7753                 success : rs.success,
7754                 errors : errors
7755             };
7756         }
7757         return Ext.decode(response.responseText);
7758     }
7759 });
7760
7761
7762 /**
7763  * @class Ext.form.Action.Load
7764  * @extends Ext.form.Action
7765  * <p>A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.</p>
7766  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
7767  * {@link Ext.form.BasicForm#load load}ing.</p>
7768  * <p><u><b>Response Packet Criteria</b></u></p>
7769  * <p>A response packet <b>must</b> contain:
7770  * <div class="mdetail-params"><ul>
7771  * <li><b><code>success</code></b> property : Boolean</li>
7772  * <li><b><code>data</code></b> property : Object</li>
7773  * <div class="sub-desc">The <code>data</code> property contains the values of Fields to load.
7774  * The individual value object for each Field is passed to the Field's
7775  * {@link Ext.form.Field#setValue setValue} method.</div></li>
7776  * </ul></div>
7777  * <p><u><b>JSON Packets</b></u></p>
7778  * <p>By default, response packets are assumed to be JSON, so for the following form load call:<pre><code>
7779 var myFormPanel = new Ext.form.FormPanel({
7780     title: 'Client and routing info',
7781     items: [{
7782         fieldLabel: 'Client',
7783         name: 'clientName'
7784     }, {
7785         fieldLabel: 'Port of loading',
7786         name: 'portOfLoading'
7787     }, {
7788         fieldLabel: 'Port of discharge',
7789         name: 'portOfDischarge'
7790     }]
7791 });
7792 myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
7793     url: '/getRoutingInfo.php',
7794     params: {
7795         consignmentRef: myConsignmentRef
7796     },
7797     failure: function(form, action) {
7798         Ext.Msg.alert("Load failed", action.result.errorMessage);
7799     }
7800 });
7801 </code></pre>
7802  * a <b>success response</b> packet may look like this:</p><pre><code>
7803 {
7804     success: true,
7805     data: {
7806         clientName: "Fred. Olsen Lines",
7807         portOfLoading: "FXT",
7808         portOfDischarge: "OSL"
7809     }
7810 }</code></pre>
7811  * while a <b>failure response</b> packet may look like this:</p><pre><code>
7812 {
7813     success: false,
7814     errorMessage: "Consignment reference not found"
7815 }</code></pre>
7816  * <p>Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s
7817  * callback or event handler methods. The object decoded from this JSON is available in the
7818  * {@link Ext.form.Action#result result} property.</p>
7819  */
7820 Ext.form.Action.Load = function(form, options){
7821     Ext.form.Action.Load.superclass.constructor.call(this, form, options);
7822     this.reader = this.form.reader;
7823 };
7824
7825 Ext.extend(Ext.form.Action.Load, Ext.form.Action, {
7826     // private
7827     type : 'load',
7828
7829     // private
7830     run : function(){
7831         Ext.Ajax.request(Ext.apply(
7832                 this.createCallback(this.options), {
7833                     method:this.getMethod(),
7834                     url:this.getUrl(false),
7835                     headers: this.options.headers,
7836                     params:this.getParams()
7837         }));
7838     },
7839
7840     // private
7841     success : function(response){
7842         var result = this.processResponse(response);
7843         if(result === true || !result.success || !result.data){
7844             this.failureType = Ext.form.Action.LOAD_FAILURE;
7845             this.form.afterAction(this, false);
7846             return;
7847         }
7848         this.form.clearInvalid();
7849         this.form.setValues(result.data);
7850         this.form.afterAction(this, true);
7851     },
7852
7853     // private
7854     handleResponse : function(response){
7855         if(this.form.reader){
7856             var rs = this.form.reader.read(response);
7857             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7858             return {
7859                 success : rs.success,
7860                 data : data
7861             };
7862         }
7863         return Ext.decode(response.responseText);
7864     }
7865 });
7866
7867
7868
7869 /**
7870  * @class Ext.form.Action.DirectLoad
7871  * @extends Ext.form.Action.Load
7872  * <p>Provides Ext.direct support for loading form data.</p>
7873  * <p>This example illustrates usage of Ext.Direct to <b>load</b> a form through Ext.Direct.</p>
7874  * <pre><code>
7875 var myFormPanel = new Ext.form.FormPanel({
7876     // configs for FormPanel
7877     title: 'Basic Information',
7878     renderTo: document.body,
7879     width: 300, height: 160,
7880     padding: 10,
7881
7882     // configs apply to child items
7883     defaults: {anchor: '100%'},
7884     defaultType: 'textfield',
7885     items: [{
7886         fieldLabel: 'Name',
7887         name: 'name'
7888     },{
7889         fieldLabel: 'Email',
7890         name: 'email'
7891     },{
7892         fieldLabel: 'Company',
7893         name: 'company'
7894     }],
7895
7896     // configs for BasicForm
7897     api: {
7898         // The server-side method to call for load() requests
7899         load: Profile.getBasicInfo,
7900         // The server-side must mark the submit handler as a 'formHandler'
7901         submit: Profile.updateBasicInfo
7902     },
7903     // specify the order for the passed params
7904     paramOrder: ['uid', 'foo']
7905 });
7906
7907 // load the form
7908 myFormPanel.getForm().load({
7909     // pass 2 arguments to server side getBasicInfo method (len=2)
7910     params: {
7911         foo: 'bar',
7912         uid: 34
7913     }
7914 });
7915  * </code></pre>
7916  * The data packet sent to the server will resemble something like:
7917  * <pre><code>
7918 [
7919     {
7920         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
7921         "data":[34,"bar"] // note the order of the params
7922     }
7923 ]
7924  * </code></pre>
7925  * The form will process a data packet returned by the server that is similar
7926  * to the following format:
7927  * <pre><code>
7928 [
7929     {
7930         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
7931         "result":{
7932             "success":true,
7933             "data":{
7934                 "name":"Fred Flintstone",
7935                 "company":"Slate Rock and Gravel",
7936                 "email":"fred.flintstone@slaterg.com"
7937             }
7938         }
7939     }
7940 ]
7941  * </code></pre>
7942  */
7943 Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
7944     constructor: function(form, opts) {
7945         Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
7946     },
7947     type : 'directload',
7948
7949     run : function(){
7950         var args = this.getParams();
7951         args.push(this.success, this);
7952         this.form.api.load.apply(window, args);
7953     },
7954
7955     getParams : function() {
7956         var buf = [], o = {};
7957         var bp = this.form.baseParams;
7958         var p = this.options.params;
7959         Ext.apply(o, p, bp);
7960         var paramOrder = this.form.paramOrder;
7961         if(paramOrder){
7962             for(var i = 0, len = paramOrder.length; i < len; i++){
7963                 buf.push(o[paramOrder[i]]);
7964             }
7965         }else if(this.form.paramsAsHash){
7966             buf.push(o);
7967         }
7968         return buf;
7969     },
7970     // Direct actions have already been processed and therefore
7971     // we can directly set the result; Direct Actions do not have
7972     // a this.response property.
7973     processResponse : function(result) {
7974         this.result = result;
7975         return result;
7976     },
7977     
7978     success : function(response, trans){
7979         if(trans.type == Ext.Direct.exceptions.SERVER){
7980             response = {};
7981         }
7982         Ext.form.Action.DirectLoad.superclass.success.call(this, response);
7983     }
7984 });
7985
7986 /**
7987  * @class Ext.form.Action.DirectSubmit
7988  * @extends Ext.form.Action.Submit
7989  * <p>Provides Ext.direct support for submitting form data.</p>
7990  * <p>This example illustrates usage of Ext.Direct to <b>submit</b> a form through Ext.Direct.</p>
7991  * <pre><code>
7992 var myFormPanel = new Ext.form.FormPanel({
7993     // configs for FormPanel
7994     title: 'Basic Information',
7995     renderTo: document.body,
7996     width: 300, height: 160,
7997     padding: 10,
7998     buttons:[{
7999         text: 'Submit',
8000         handler: function(){
8001             myFormPanel.getForm().submit({
8002                 params: {
8003                     foo: 'bar',
8004                     uid: 34
8005                 }
8006             });
8007         }
8008     }],
8009
8010     // configs apply to child items
8011     defaults: {anchor: '100%'},
8012     defaultType: 'textfield',
8013     items: [{
8014         fieldLabel: 'Name',
8015         name: 'name'
8016     },{
8017         fieldLabel: 'Email',
8018         name: 'email'
8019     },{
8020         fieldLabel: 'Company',
8021         name: 'company'
8022     }],
8023
8024     // configs for BasicForm
8025     api: {
8026         // The server-side method to call for load() requests
8027         load: Profile.getBasicInfo,
8028         // The server-side must mark the submit handler as a 'formHandler'
8029         submit: Profile.updateBasicInfo
8030     },
8031     // specify the order for the passed params
8032     paramOrder: ['uid', 'foo']
8033 });
8034  * </code></pre>
8035  * The data packet sent to the server will resemble something like:
8036  * <pre><code>
8037 {
8038     "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
8039     "result":{
8040         "success":true,
8041         "id":{
8042             "extAction":"Profile","extMethod":"updateBasicInfo",
8043             "extType":"rpc","extTID":"6","extUpload":"false",
8044             "name":"Aaron Conran","email":"aaron@extjs.com","company":"Ext JS, LLC"
8045         }
8046     }
8047 }
8048  * </code></pre>
8049  * The form will process a data packet returned by the server that is similar
8050  * to the following:
8051  * <pre><code>
8052 // sample success packet (batched requests)
8053 [
8054     {
8055         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,
8056         "result":{
8057             "success":true
8058         }
8059     }
8060 ]
8061
8062 // sample failure packet (one request)
8063 {
8064         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
8065         "result":{
8066             "errors":{
8067                 "email":"already taken"
8068             },
8069             "success":false,
8070             "foo":"bar"
8071         }
8072 }
8073  * </code></pre>
8074  * Also see the discussion in {@link Ext.form.Action.DirectLoad}.
8075  */
8076 Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
8077     constructor : function(form, opts) {
8078         Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
8079     },
8080     type : 'directsubmit',
8081     // override of Submit
8082     run : function(){
8083         var o = this.options;
8084         if(o.clientValidation === false || this.form.isValid()){
8085             // tag on any additional params to be posted in the
8086             // form scope
8087             this.success.params = this.getParams();
8088             this.form.api.submit(this.form.el.dom, this.success, this);
8089         }else if (o.clientValidation !== false){ // client validation failed
8090             this.failureType = Ext.form.Action.CLIENT_INVALID;
8091             this.form.afterAction(this, false);
8092         }
8093     },
8094
8095     getParams : function() {
8096         var o = {};
8097         var bp = this.form.baseParams;
8098         var p = this.options.params;
8099         Ext.apply(o, p, bp);
8100         return o;
8101     },
8102     // Direct actions have already been processed and therefore
8103     // we can directly set the result; Direct Actions do not have
8104     // a this.response property.
8105     processResponse : function(result) {
8106         this.result = result;
8107         return result;
8108     },
8109     
8110     success : function(response, trans){
8111         if(trans.type == Ext.Direct.exceptions.SERVER){
8112             response = {};
8113         }
8114         Ext.form.Action.DirectSubmit.superclass.success.call(this, response);
8115     }
8116 });
8117
8118 Ext.form.Action.ACTION_TYPES = {
8119     'load' : Ext.form.Action.Load,
8120     'submit' : Ext.form.Action.Submit,
8121     'directload' : Ext.form.Action.DirectLoad,
8122     'directsubmit' : Ext.form.Action.DirectSubmit
8123 };
8124 /**
8125  * @class Ext.form.VTypes
8126  * <p>This is a singleton object which contains a set of commonly used field validation functions.
8127  * The validations provided are basic and intended to be easily customizable and extended.</p>
8128  * <p>To add custom VTypes specify the <code>{@link Ext.form.TextField#vtype vtype}</code> validation
8129  * test function, and optionally specify any corresponding error text to display and any keystroke
8130  * filtering mask to apply. For example:</p>
8131  * <pre><code>
8132 // custom Vtype for vtype:'time'
8133 var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
8134 Ext.apply(Ext.form.VTypes, {
8135     //  vtype validation function
8136     time: function(val, field) {
8137         return timeTest.test(val);
8138     },
8139     // vtype Text property: The error text to display when the validation function returns false
8140     timeText: 'Not a valid time.  Must be in the format "12:34 PM".',
8141     // vtype Mask property: The keystroke filter mask
8142     timeMask: /[\d\s:amp]/i
8143 });
8144  * </code></pre>
8145  * Another example: 
8146  * <pre><code>
8147 // custom Vtype for vtype:'IPAddress'
8148 Ext.apply(Ext.form.VTypes, {
8149     IPAddress:  function(v) {
8150         return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
8151     },
8152     IPAddressText: 'Must be a numeric IP address',
8153     IPAddressMask: /[\d\.]/i
8154 });
8155  * </code></pre>
8156  * @singleton
8157  */
8158 Ext.form.VTypes = function(){
8159     // closure these in so they are only created once.
8160     var alpha = /^[a-zA-Z_]+$/,
8161         alphanum = /^[a-zA-Z0-9_]+$/,
8162         email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/,
8163         url = /(((^https?)|(^ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8164
8165     // All these messages and functions are configurable
8166     return {
8167         /**
8168          * The function used to validate email addresses.  Note that this is a very basic validation -- complete
8169          * validation per the email RFC specifications is very complex and beyond the scope of this class, although
8170          * this function can be overridden if a more comprehensive validation scheme is desired.  See the validation
8171          * section of the <a href="http://en.wikipedia.org/wiki/E-mail_address">Wikipedia article on email addresses</a> 
8172          * for additional information.  This implementation is intended to validate the following emails:<tt>
8173          * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
8174          * </tt>.
8175          * @param {String} value The email address
8176          * @return {Boolean} true if the RegExp test passed, and false if not.
8177          */
8178         'email' : function(v){
8179             return email.test(v);
8180         },
8181         /**
8182          * The error text to display when the email validation function returns false.  Defaults to:
8183          * <tt>'This field should be an e-mail address in the format "user@example.com"'</tt>
8184          * @type String
8185          */
8186         'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
8187         /**
8188          * The keystroke filter mask to be applied on email input.  See the {@link #email} method for 
8189          * information about more complex email validation. Defaults to:
8190          * <tt>/[a-z0-9_\.\-@]/i</tt>
8191          * @type RegExp
8192          */
8193         'emailMask' : /[a-z0-9_\.\-@]/i,
8194
8195         /**
8196          * The function used to validate URLs
8197          * @param {String} value The URL
8198          * @return {Boolean} true if the RegExp test passed, and false if not.
8199          */
8200         'url' : function(v){
8201             return url.test(v);
8202         },
8203         /**
8204          * The error text to display when the url validation function returns false.  Defaults to:
8205          * <tt>'This field should be a URL in the format "http:/'+'/www.example.com"'</tt>
8206          * @type String
8207          */
8208         'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
8209         
8210         /**
8211          * The function used to validate alpha values
8212          * @param {String} value The value
8213          * @return {Boolean} true if the RegExp test passed, and false if not.
8214          */
8215         'alpha' : function(v){
8216             return alpha.test(v);
8217         },
8218         /**
8219          * The error text to display when the alpha validation function returns false.  Defaults to:
8220          * <tt>'This field should only contain letters and _'</tt>
8221          * @type String
8222          */
8223         'alphaText' : 'This field should only contain letters and _',
8224         /**
8225          * The keystroke filter mask to be applied on alpha input.  Defaults to:
8226          * <tt>/[a-z_]/i</tt>
8227          * @type RegExp
8228          */
8229         'alphaMask' : /[a-z_]/i,
8230
8231         /**
8232          * The function used to validate alphanumeric values
8233          * @param {String} value The value
8234          * @return {Boolean} true if the RegExp test passed, and false if not.
8235          */
8236         'alphanum' : function(v){
8237             return alphanum.test(v);
8238         },
8239         /**
8240          * The error text to display when the alphanumeric validation function returns false.  Defaults to:
8241          * <tt>'This field should only contain letters, numbers and _'</tt>
8242          * @type String
8243          */
8244         'alphanumText' : 'This field should only contain letters, numbers and _',
8245         /**
8246          * The keystroke filter mask to be applied on alphanumeric input.  Defaults to:
8247          * <tt>/[a-z0-9_]/i</tt>
8248          * @type RegExp
8249          */
8250         'alphanumMask' : /[a-z0-9_]/i
8251     };
8252 }();