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