3 * Copyright(c) 2006-2009 Ext JS, LLC
5 * http://www.extjs.com/license
8 * @class Ext.form.Field
9 * @extends Ext.BoxComponent
10 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
13 * @param {Object} config Configuration options
16 Ext.form.Field = Ext.extend(Ext.BoxComponent, {
18 * @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.
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).
28 * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
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()}.
36 * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to '').
40 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to 'x-form-invalid')
42 invalidClass : 'x-form-invalid',
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')
47 invalidText : 'The value in this field is invalid',
49 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to 'x-form-focus')
51 focusClass : 'x-form-focus',
53 * @cfg {Boolean} preventMark
54 * <tt>true</tt> to disable {@link #markInvalid marking the field invalid}.
55 * Defaults to <tt>false</tt>.
58 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
59 automatic validation (defaults to 'keyup').
61 validationEvent : 'keyup',
63 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
65 validateOnBlur : true,
67 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation
68 * is initiated (defaults to 250)
70 validationDelay : 250,
72 * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
73 * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
74 * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details. Defaults to:</p>
75 * <pre><code>{tag: 'input', type: 'text', size: '20', autocomplete: 'off'}</code></pre>
77 defaultAutoCreate : {tag: 'input', type: 'text', size: '20', autocomplete: 'off'},
79 * @cfg {String} fieldClass The default CSS class for the field (defaults to 'x-form-field')
81 fieldClass : 'x-form-field',
83 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values
84 * (defaults to 'qtip'):
87 ----------- ----------------------------------------------------------------------
88 qtip Display a quick tip when the user hovers over the field
89 title Display a default browser title attribute popup
90 under Add a block div beneath the field containing the error text
91 side Add an error icon to the right of the field with a popup on hover
92 [element id] Add the error text directly to the innerHTML of the specified element
97 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field
98 * (defaults to 'normal').
102 * @cfg {Boolean} readOnly <tt>true</tt> to mark the field as readOnly in HTML
103 * (defaults to <tt>false</tt>).
104 * <br><p><b>Note</b>: this only sets the element's readOnly DOM attribute.
105 * Setting <code>readOnly=true</code>, for example, will not disable triggering a
106 * ComboBox or DateField; it gives you the option of forcing the user to choose
107 * via the trigger without typing in the text box. To hide the trigger use
108 * <code>{@link Ext.form.TriggerField#hideTrigger hideTrigger}</code>.</p>
112 * @cfg {Boolean} disabled True to disable the field (defaults to false).
113 * <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>,
114 * disabled Fields will not be {@link Ext.form.BasicForm#submit submitted}.</p>
128 initComponent : function(){
129 Ext.form.Field.superclass.initComponent.call(this);
133 * Fires when this field receives input focus.
134 * @param {Ext.form.Field} this
139 * Fires when this field loses input focus.
140 * @param {Ext.form.Field} this
145 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.
146 * To handle other keys see {@link Ext.Panel#keys} or {@link Ext.KeyMap}.
147 * You can check {@link Ext.EventObject#getKey} to determine which key was pressed.
148 * For example: <pre><code>
149 var form = new Ext.form.FormPanel({
152 fieldLabel: 'Field 1',
156 fieldLabel: 'Field 2',
159 specialkey: function(field, e){
160 // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
161 // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
162 if (e.{@link Ext.EventObject#getKey getKey()} == e.ENTER) {
163 var form = field.ownerCt.getForm();
173 * @param {Ext.form.Field} this
174 * @param {Ext.EventObject} e The event object
179 * Fires just before the field blurs if the field value has changed.
180 * @param {Ext.form.Field} this
181 * @param {Mixed} newValue The new value
182 * @param {Mixed} oldValue The original value
187 * Fires after the field has been marked as invalid.
188 * @param {Ext.form.Field} this
189 * @param {String} msg The validation message
194 * Fires after the field has been validated with no errors.
195 * @param {Ext.form.Field} this
202 * Returns the {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
203 * attribute of the field if available.
204 * @return {String} name The field {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
206 getName : function(){
207 return this.rendered && this.el.dom.name ? this.el.dom.name : this.name || this.id || '';
211 onRender : function(ct, position){
213 var cfg = this.getAutoCreate();
216 cfg.name = this.name || this.id;
219 cfg.type = this.inputType;
223 Ext.form.Field.superclass.onRender.call(this, ct, position);
225 var type = this.el.dom.type;
227 if(type == 'password'){
230 this.el.addClass('x-form-'+type);
233 this.el.dom.readOnly = true;
235 if(this.tabIndex !== undefined){
236 this.el.dom.setAttribute('tabIndex', this.tabIndex);
239 this.el.addClass([this.fieldClass, this.cls]);
243 getItemCt : function(){
248 initValue : function(){
249 if(this.value !== undefined){
250 this.setValue(this.value);
251 }else if(!Ext.isEmpty(this.el.dom.value) && this.el.dom.value != this.emptyText){
252 this.setValue(this.el.dom.value);
255 * The original value of the field as configured in the {@link #value} configuration, or
256 * as loaded by the last form load operation if the form's {@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
257 * setting is <code>true</code>.
259 * @property originalValue
261 this.originalValue = this.getValue();
265 * <p>Returns true if the value of this Field has been changed from its original value.
266 * Will return false if the field is disabled or has not been rendered yet.</p>
267 * <p>Note that if the owning {@link Ext.form.BasicForm form} was configured with
268 * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
269 * then the <i>original value</i> is updated when the values are loaded by
270 * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#setValues setValues}.</p>
271 * @return {Boolean} True if this field has been changed from its original value (and
272 * is not disabled), false otherwise.
274 isDirty : function() {
275 if(this.disabled || !this.rendered) {
278 return String(this.getValue()) !== String(this.originalValue);
282 afterRender : function(){
283 Ext.form.Field.superclass.afterRender.call(this);
289 fireKey : function(e){
290 if(e.isSpecialKey()){
291 this.fireEvent('specialkey', this, e);
296 * Resets the current field value to the originally loaded value and clears any validation messages.
297 * See {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
300 this.setValue(this.originalValue);
305 initEvents : function(){
306 this.mon(this.el, Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.fireKey, this);
307 this.mon(this.el, 'focus', this.onFocus, this);
309 // standardise buffer across all browsers + OS-es for consistent event order.
310 // (the 10ms buffer for Editors fixes a weird FF/Win editor issue when changing OS window focus)
311 this.mon(this.el, 'blur', this.onBlur, this, this.inEditor ? {buffer:10} : null);
315 preFocus: Ext.emptyFn,
318 onFocus : function(){
321 this.el.addClass(this.focusClass);
324 this.hasFocus = true;
325 this.startValue = this.getValue();
326 this.fireEvent('focus', this);
331 beforeBlur : Ext.emptyFn,
337 this.el.removeClass(this.focusClass);
339 this.hasFocus = false;
340 if(this.validationEvent !== false && (this.validateOnBlur || this.validationEvent != 'blur')){
343 var v = this.getValue();
344 if(String(v) !== String(this.startValue)){
345 this.fireEvent('change', this, v, this.startValue);
347 this.fireEvent('blur', this);
352 postBlur : Ext.emptyFn,
355 * Returns whether or not the field value is currently valid by
356 * {@link #validateValue validating} the {@link #processValue processed value}
357 * of the field. <b>Note</b>: {@link #disabled} fields are ignored.
358 * @param {Boolean} preventMark True to disable marking the field invalid
359 * @return {Boolean} True if the value is valid, else false
361 isValid : function(preventMark){
365 var restore = this.preventMark;
366 this.preventMark = preventMark === true;
367 var v = this.validateValue(this.processValue(this.getRawValue()));
368 this.preventMark = restore;
373 * Validates the field value
374 * @return {Boolean} True if the value is valid, else false
376 validate : function(){
377 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
385 * This method should only be overridden if necessary to prepare raw values
386 * for validation (see {@link #validate} and {@link #isValid}). This method
387 * is expected to return the processed value for the field which will
388 * be used for validation (see validateValue method).
389 * @param {Mixed} value
391 processValue : function(value){
397 * Subclasses should provide the validation implementation by overriding this
398 * @param {Mixed} value
400 validateValue : function(value){
405 * Mark this field as invalid, using {@link #msgTarget} to determine how to
406 * display the error and applying {@link #invalidClass} to the field's element.
407 * <b>Note</b>: this method does not actually make the field
408 * {@link #isValid invalid}.
409 * @param {String} msg (optional) The validation message (defaults to {@link #invalidText})
411 markInvalid : function(msg){
412 if(!this.rendered || this.preventMark){ // not rendered
415 msg = msg || this.invalidText;
417 var mt = this.getMessageHandler();
420 }else if(this.msgTarget){
421 this.el.addClass(this.invalidClass);
422 var t = Ext.getDom(this.msgTarget);
425 t.style.display = this.msgDisplay;
428 this.fireEvent('invalid', this, msg);
432 * Clear any invalid styles/messages for this field
434 clearInvalid : function(){
435 if(!this.rendered || this.preventMark){ // not rendered
438 this.el.removeClass(this.invalidClass);
439 var mt = this.getMessageHandler();
442 }else if(this.msgTarget){
443 this.el.removeClass(this.invalidClass);
444 var t = Ext.getDom(this.msgTarget);
447 t.style.display = 'none';
450 this.fireEvent('valid', this);
454 getMessageHandler : function(){
455 return Ext.form.MessageTargets[this.msgTarget];
459 getErrorCt : function(){
460 return this.el.findParent('.x-form-element', 5, true) || // use form element wrap if available
461 this.el.findParent('.x-form-field-wrap', 5, true); // else direct field wrap
465 alignErrorIcon : function(){
466 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
470 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
471 * @return {Mixed} value The field value
473 getRawValue : function(){
474 var v = this.rendered ? this.el.getValue() : Ext.value(this.value, '');
475 if(v === this.emptyText){
482 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
483 * @return {Mixed} value The field value
485 getValue : function(){
489 var v = this.el.getValue();
490 if(v === this.emptyText || v === undefined){
497 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
498 * @param {Mixed} value The value to set
499 * @return {Mixed} value The field value that is set
501 setRawValue : function(v){
502 return this.rendered ? (this.el.dom.value = (Ext.isEmpty(v) ? '' : v)) : '';
506 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
507 * @param {Mixed} value The value to set
508 * @return {Ext.form.Field} this
510 setValue : function(v){
513 this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
519 // private, does not work for all fields
520 append : function(v){
521 this.setValue([this.getValue(), v].join(''));
525 * @cfg {Boolean} autoWidth @hide
528 * @cfg {Boolean} autoHeight @hide
532 * @cfg {String} autoEl @hide
537 Ext.form.MessageTargets = {
539 mark: function(field, msg){
540 field.el.addClass(field.invalidClass);
541 field.el.dom.qtip = msg;
542 field.el.dom.qclass = 'x-form-invalid-tip';
543 if(Ext.QuickTips){ // fix for floating editors interacting with DND
544 Ext.QuickTips.enable();
547 clear: function(field){
548 field.el.removeClass(field.invalidClass);
549 field.el.dom.qtip = '';
553 mark: function(field, msg){
554 field.el.addClass(field.invalidClass);
555 field.el.dom.title = msg;
557 clear: function(field){
558 field.el.dom.title = '';
562 mark: function(field, msg){
563 field.el.addClass(field.invalidClass);
565 var elp = field.getErrorCt();
566 if(!elp){ // field has no container el
567 field.el.dom.title = msg;
570 field.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
571 field.errorEl.setWidth(elp.getWidth(true)-20);
573 field.errorEl.update(msg);
574 Ext.form.Field.msgFx[field.msgFx].show(field.errorEl, field);
576 clear: function(field){
577 field.el.removeClass(field.invalidClass);
579 Ext.form.Field.msgFx[field.msgFx].hide(field.errorEl, field);
581 field.el.dom.title = '';
586 mark: function(field, msg){
587 field.el.addClass(field.invalidClass);
588 if(!field.errorIcon){
589 var elp = field.getErrorCt();
590 if(!elp){ // field has no container el
591 field.el.dom.title = msg;
594 field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
596 field.alignErrorIcon();
597 field.errorIcon.dom.qtip = msg;
598 field.errorIcon.dom.qclass = 'x-form-invalid-tip';
599 field.errorIcon.show();
600 field.on('resize', field.alignErrorIcon, field);
602 clear: function(field){
603 field.el.removeClass(field.invalidClass);
605 field.errorIcon.dom.qtip = '';
606 field.errorIcon.hide();
607 field.un('resize', field.alignErrorIcon, field);
609 field.el.dom.title = '';
615 // anything other than normal should be considered experimental
616 Ext.form.Field.msgFx = {
618 show: function(msgEl, f){
619 msgEl.setDisplayed('block');
622 hide : function(msgEl, f){
623 msgEl.setDisplayed(false).update('');
628 show: function(msgEl, f){
629 msgEl.slideIn('t', {stopFx:true});
632 hide : function(msgEl, f){
633 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
638 show: function(msgEl, f){
640 msgEl.alignTo(f.el, 'tl-tr');
641 msgEl.slideIn('l', {stopFx:true});
644 hide : function(msgEl, f){
645 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
649 Ext.reg('field', Ext.form.Field);
651 * @class Ext.form.TextField
652 * @extends Ext.form.Field
653 * <p>Basic text field. Can be used as a direct replacement for traditional text inputs,
654 * or as the base class for more sophisticated input controls (like {@link Ext.form.TextArea}
655 * and {@link Ext.form.ComboBox}).</p>
656 * <p><b><u>Validation</u></b></p>
657 * <p>The validation procedure is described in the documentation for {@link #validateValue}.</p>
658 * <p><b><u>Alter Validation Behavior</u></b></p>
659 * <p>Validation behavior for each field can be configured:</p>
660 * <div class="mdetail-params"><ul>
661 * <li><code>{@link Ext.form.TextField#invalidText invalidText}</code> : the default validation message to
662 * show if any validation step above does not provide a message when invalid</li>
663 * <li><code>{@link Ext.form.TextField#maskRe maskRe}</code> : filter out keystrokes before any validation occurs</li>
664 * <li><code>{@link Ext.form.TextField#stripCharsRe stripCharsRe}</code> : filter characters after being typed in,
665 * but before being validated</li>
666 * <li><code>{@link Ext.form.Field#invalidClass invalidClass}</code> : alternate style when invalid</li>
667 * <li><code>{@link Ext.form.Field#validateOnBlur validateOnBlur}</code>,
668 * <code>{@link Ext.form.Field#validationDelay validationDelay}</code>, and
669 * <code>{@link Ext.form.Field#validationEvent validationEvent}</code> : modify how/when validation is triggered</li>
672 * @constructor Creates a new TextField
673 * @param {Object} config Configuration options
677 Ext.form.TextField = Ext.extend(Ext.form.Field, {
679 * @cfg {String} vtypeText A custom error message to display in place of the default message provided
680 * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>''</tt>). <b>Note</b>:
681 * only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
684 * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
685 * before validation (defaults to <tt>null</tt>).
688 * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
689 * (defaults to <tt>false</tt>)
693 * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
698 * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
703 * @cfg {String} vtype A validation type name as defined in {@link Ext.form.VTypes} (defaults to <tt>null</tt>)
707 * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
708 * not match (defaults to <tt>null</tt>)
712 * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
715 disableKeyFilter : false,
717 * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
722 * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
726 * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
727 * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
728 * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
729 * entered into the field use <tt><b>{@link Ext.form.Field#autoCreate autoCreate}</b></tt> to add
730 * any attributes you want to a field, for example:<pre><code>
731 var myField = new Ext.form.NumberField({
734 fieldLabel: 'Mobile',
735 maxLength: 16, // for validation
736 autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete: 'off', maxlength: '10'}
740 maxLength : Number.MAX_VALUE,
742 * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
743 * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
745 minLengthText : 'The minimum length for this field is {0}',
747 * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
748 * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
750 maxLengthText : 'The maximum length for this field is {0}',
752 * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
753 * receives input focus (defaults to <tt>false</tt>)
755 selectOnFocus : false,
757 * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
758 * fails (defaults to <tt>'This field is required'</tt>)
760 blankText : 'This field is required',
762 * @cfg {Function} validator
763 * <p>A custom validation function to be called during field validation ({@link #validateValue})
764 * (defaults to <tt>null</tt>). If specified, this function will be called first, allowing the
765 * developer to override the default validation process.</p>
766 * <br><p>This function will be passed the following Parameters:</p>
767 * <div class="mdetail-params"><ul>
768 * <li><code>value</code>: <i>Mixed</i>
769 * <div class="sub-desc">The current field value</div></li>
771 * <br><p>This function is to Return:</p>
772 * <div class="mdetail-params"><ul>
773 * <li><code>true</code>: <i>Boolean</i>
774 * <div class="sub-desc"><code>true</code> if the value is valid</div></li>
775 * <li><code>msg</code>: <i>String</i>
776 * <div class="sub-desc">An error message if the value is invalid</div></li>
781 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
782 * (defaults to <tt>null</tt>). If the test fails, the field will be marked invalid using
783 * <b><tt>{@link #regexText}</tt></b>.
787 * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
788 * test fails during validation (defaults to <tt>''</tt>)
792 * @cfg {String} emptyText The default text to place into an empty field (defaults to <tt>null</tt>).
793 * <b>Note</b>: that this value will be submitted to the server if this field is enabled and configured
794 * with a {@link #name}.
798 * @cfg {String} emptyClass The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
799 * (defaults to <tt>'x-form-empty-field'</tt>). This class is automatically added and removed as needed
800 * depending on the current field value.
802 emptyClass : 'x-form-empty-field',
805 * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input
806 * field (defaults to <tt>false</tt>)
809 initComponent : function(){
810 Ext.form.TextField.superclass.initComponent.call(this);
814 * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered. The field may or
815 * may not have actually changed size according to the default logic, but this event provides
816 * a hook for the developer to apply additional logic at runtime to resize the field if needed.
817 * @param {Ext.form.Field} this This text field
818 * @param {Number} width The new field width
824 * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
826 * @param {Ext.form.TextField} this This text field
827 * @param {Ext.EventObject} e
832 * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
834 * @param {Ext.form.TextField} this This text field
835 * @param {Ext.EventObject} e
840 * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
842 * @param {Ext.form.TextField} this This text field
843 * @param {Ext.EventObject} e
850 initEvents : function(){
851 Ext.form.TextField.superclass.initEvents.call(this);
852 if(this.validationEvent == 'keyup'){
853 this.validationTask = new Ext.util.DelayedTask(this.validate, this);
854 this.mon(this.el, 'keyup', this.filterValidation, this);
856 else if(this.validationEvent !== false && this.validationEvent != 'blur'){
857 this.mon(this.el, this.validationEvent, this.validate, this, {buffer: this.validationDelay});
859 if(this.selectOnFocus || this.emptyText){
860 this.mon(this.el, 'mousedown', this.onMouseDown, this);
863 this.applyEmptyText();
866 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Ext.form.VTypes[this.vtype+'Mask']))){
867 this.mon(this.el, 'keypress', this.filterKeys, this);
870 this.mon(this.el, 'keyup', this.onKeyUpBuffered, this, {buffer: 50});
871 this.mon(this.el, 'click', this.autoSize, this);
873 if(this.enableKeyEvents){
877 keydown: this.onKeyDown,
878 keypress: this.onKeyPress
883 onMouseDown: function(e){
885 this.mon(this.el, 'mouseup', Ext.emptyFn, this, { single: true, preventDefault: true });
889 processValue : function(value){
890 if(this.stripCharsRe){
891 var newValue = value.replace(this.stripCharsRe, '');
892 if(newValue !== value){
893 this.setRawValue(newValue);
900 filterValidation : function(e){
901 if(!e.isNavKeyPress()){
902 this.validationTask.delay(this.validationDelay);
907 onDisable: function(){
908 Ext.form.TextField.superclass.onDisable.call(this);
910 this.el.dom.unselectable = 'on';
915 onEnable: function(){
916 Ext.form.TextField.superclass.onEnable.call(this);
918 this.el.dom.unselectable = '';
923 onKeyUpBuffered : function(e){
924 if(!e.isNavKeyPress()){
930 onKeyUp : function(e){
931 this.fireEvent('keyup', this, e);
935 onKeyDown : function(e){
936 this.fireEvent('keydown', this, e);
940 onKeyPress : function(e){
941 this.fireEvent('keypress', this, e);
945 * Resets the current field value to the originally-loaded value and clears any validation messages.
946 * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyClass}</b></tt> if the
947 * original value was blank.
950 Ext.form.TextField.superclass.reset.call(this);
951 this.applyEmptyText();
954 applyEmptyText : function(){
955 if(this.rendered && this.emptyText && this.getRawValue().length < 1 && !this.hasFocus){
956 this.setRawValue(this.emptyText);
957 this.el.addClass(this.emptyClass);
962 preFocus : function(){
965 if(el.dom.value == this.emptyText){
966 this.setRawValue('');
968 el.removeClass(this.emptyClass);
970 if(this.selectOnFocus){
976 postBlur : function(){
977 this.applyEmptyText();
981 filterKeys : function(e){
982 // special keys don't generate charCodes, so leave them alone
983 if(e.ctrlKey || e.isSpecialKey()){
987 if(!this.maskRe.test(String.fromCharCode(e.getCharCode()))){
992 setValue : function(v){
993 if(this.emptyText && this.el && !Ext.isEmpty(v)){
994 this.el.removeClass(this.emptyClass);
996 Ext.form.TextField.superclass.setValue.apply(this, arguments);
997 this.applyEmptyText();
1003 * <p>Validates a value according to the field's validation rules and marks the field as invalid
1004 * if the validation fails. Validation rules are processed in the following order:</p>
1005 * <div class="mdetail-params"><ul>
1007 * <li><b>1. Field specific validator</b>
1008 * <div class="sub-desc">
1009 * <p>A validator offers a way to customize and reuse a validation specification.
1010 * If a field is configured with a <code>{@link #validator}</code>
1011 * function, it will be passed the current field value. The <code>{@link #validator}</code>
1012 * function is expected to return either:
1013 * <div class="mdetail-params"><ul>
1014 * <li>Boolean <tt>true</tt> if the value is valid (validation continues).</li>
1015 * <li>a String to represent the invalid message if invalid (validation halts).</li>
1019 * <li><b>2. Basic Validation</b>
1020 * <div class="sub-desc">
1021 * <p>If the <code>{@link #validator}</code> has not halted validation,
1022 * basic validation proceeds as follows:</p>
1024 * <div class="mdetail-params"><ul>
1026 * <li><code>{@link #allowBlank}</code> : (Invalid message =
1027 * <code>{@link #emptyText}</code>)<div class="sub-desc">
1028 * Depending on the configuration of <code>{@link #allowBlank}</code>, a
1029 * blank field will cause validation to halt at this step and return
1030 * Boolean true or false accordingly.
1033 * <li><code>{@link #minLength}</code> : (Invalid message =
1034 * <code>{@link #minLengthText}</code>)<div class="sub-desc">
1035 * If the passed value does not satisfy the <code>{@link #minLength}</code>
1036 * specified, validation halts.
1039 * <li><code>{@link #maxLength}</code> : (Invalid message =
1040 * <code>{@link #maxLengthText}</code>)<div class="sub-desc">
1041 * If the passed value does not satisfy the <code>{@link #maxLength}</code>
1042 * specified, validation halts.
1048 * <li><b>3. Preconfigured Validation Types (VTypes)</b>
1049 * <div class="sub-desc">
1050 * <p>If none of the prior validation steps halts validation, a field
1051 * configured with a <code>{@link #vtype}</code> will utilize the
1052 * corresponding {@link Ext.form.VTypes VTypes} validation function.
1053 * If invalid, either the field's <code>{@link #vtypeText}</code> or
1054 * the VTypes vtype Text property will be used for the invalid message.
1055 * Keystrokes on the field will be filtered according to the VTypes
1056 * vtype Mask property.</p>
1059 * <li><b>4. Field specific regex test</b>
1060 * <div class="sub-desc">
1061 * <p>If none of the prior validation steps halts validation, a field's
1062 * configured <code>{@link #regex}</code> test will be processed.
1063 * The invalid message for this test is configured with
1064 * <code>{@link #regexText}</code>.</p>
1067 * @param {Mixed} value The value to validate
1068 * @return {Boolean} True if the value is valid, else false
1070 validateValue : function(value){
1071 if(Ext.isFunction(this.validator)){
1072 var msg = this.validator(value);
1074 this.markInvalid(msg);
1078 if(value.length < 1 || value === this.emptyText){ // if it's blank
1079 if(this.allowBlank){
1080 this.clearInvalid();
1083 this.markInvalid(this.blankText);
1087 if(value.length < this.minLength){
1088 this.markInvalid(String.format(this.minLengthText, this.minLength));
1091 if(value.length > this.maxLength){
1092 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
1096 var vt = Ext.form.VTypes;
1097 if(!vt[this.vtype](value, this)){
1098 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
1102 if(this.regex && !this.regex.test(value)){
1103 this.markInvalid(this.regexText);
1110 * Selects text in this field
1111 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
1112 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
1114 selectText : function(start, end){
1115 var v = this.getRawValue();
1116 var doFocus = false;
1118 start = start === undefined ? 0 : start;
1119 end = end === undefined ? v.length : end;
1120 var d = this.el.dom;
1121 if(d.setSelectionRange){
1122 d.setSelectionRange(start, end);
1123 }else if(d.createTextRange){
1124 var range = d.createTextRange();
1125 range.moveStart('character', start);
1126 range.moveEnd('character', end-v.length);
1129 doFocus = Ext.isGecko || Ext.isOpera;
1139 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
1140 * This only takes effect if <tt><b>{@link #grow}</b> = true</tt>, and fires the {@link #autosize} event.
1142 autoSize : function(){
1143 if(!this.grow || !this.rendered){
1147 this.metrics = Ext.util.TextMetrics.createInstance(this.el);
1150 var v = el.dom.value;
1151 var d = document.createElement('div');
1152 d.appendChild(document.createTextNode(v));
1157 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
1158 this.el.setWidth(w);
1159 this.fireEvent('autosize', this, w);
1162 onDestroy: function(){
1163 if(this.validationTask){
1164 this.validationTask.cancel();
1165 this.validationTask = null;
1167 Ext.form.TextField.superclass.onDestroy.call(this);
1170 Ext.reg('textfield', Ext.form.TextField);
1172 * @class Ext.form.TriggerField
\r
1173 * @extends Ext.form.TextField
\r
1174 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
\r
1175 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
\r
1176 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
\r
1177 * for which you can provide a custom implementation. For example:
\r
1179 var trigger = new Ext.form.TriggerField();
\r
1180 trigger.onTriggerClick = myTriggerFn;
\r
1181 trigger.applyToMarkup('my-field');
\r
1184 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
\r
1185 * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
\r
1188 * Create a new TriggerField.
\r
1189 * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
\r
1190 * to the base TextField)
\r
1193 Ext.form.TriggerField = Ext.extend(Ext.form.TextField, {
\r
1195 * @cfg {String} triggerClass
\r
1196 * An additional CSS class used to style the trigger button. The trigger will always get the
\r
1197 * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
\r
1200 * @cfg {Mixed} triggerConfig
\r
1201 * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the
\r
1202 * trigger element for this Field. (Optional).</p>
\r
1203 * <p>Specify this when you need a customized element to act as the trigger button for a TriggerField.</p>
\r
1204 * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning
\r
1205 * and appearance of the trigger. Defaults to:</p>
\r
1206 * <pre><code>{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}</code></pre>
\r
1209 * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
\r
1210 * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
\r
1211 * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details. Defaults to:</p>
\r
1212 * <pre><code>{tag: "input", type: "text", size: "16", autocomplete: "off"}</code></pre>
\r
1214 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
\r
1216 * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base
\r
1217 * text field (defaults to <tt>false</tt>)
\r
1219 hideTrigger:false,
\r
1221 * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field,
\r
1222 * the field will only respond to a click on the trigger to set the value. (defaults to <tt>true</tt>)
\r
1226 * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
\r
1227 * <tt>x-trigger-wrap-focus</tt>.
\r
1229 wrapFocusClass: 'x-trigger-wrap-focus',
\r
1232 * @method autoSize
\r
1234 autoSize: Ext.emptyFn,
\r
1236 monitorTab : true,
\r
1238 deferHeight : true,
\r
1242 actionMode: 'wrap',
\r
1244 defaultTriggerWidth: 17,
\r
1247 onResize : function(w, h){
\r
1248 Ext.form.TriggerField.superclass.onResize.call(this, w, h);
\r
1249 var tw = this.getTriggerWidth();
\r
1250 if(Ext.isNumber(w)){
\r
1251 this.el.setWidth(w - tw);
\r
1253 this.wrap.setWidth(this.el.getWidth() + tw);
\r
1256 getTriggerWidth: function(){
\r
1257 var tw = this.trigger.getWidth();
\r
1258 if(!this.hideTrigger && tw === 0){
\r
1259 tw = this.defaultTriggerWidth;
\r
1265 alignErrorIcon : function(){
\r
1267 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
\r
1272 onRender : function(ct, position){
\r
1273 this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
\r
1274 Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
\r
1276 this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
\r
1277 this.trigger = this.wrap.createChild(this.triggerConfig ||
\r
1278 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
\r
1279 if(this.hideTrigger){
\r
1280 this.trigger.setDisplayed(false);
\r
1282 this.initTrigger();
\r
1284 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
\r
1286 if(!this.editable){
\r
1287 this.editable = true;
\r
1288 this.setEditable(false);
\r
1290 this.resizeEl = this.positionEl = this.wrap;
\r
1293 afterRender : function(){
\r
1294 Ext.form.TriggerField.superclass.afterRender.call(this);
\r
1298 initTrigger : function(){
\r
1299 this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
\r
1300 this.trigger.addClassOnOver('x-form-trigger-over');
\r
1301 this.trigger.addClassOnClick('x-form-trigger-click');
\r
1305 onDestroy : function(){
\r
1306 Ext.destroy(this.trigger, this.wrap);
\r
1307 if (this.mimicing){
\r
1308 this.doc.un('mousedown', this.mimicBlur, this);
\r
1310 Ext.form.TriggerField.superclass.onDestroy.call(this);
\r
1314 onFocus : function(){
\r
1315 Ext.form.TriggerField.superclass.onFocus.call(this);
\r
1316 if(!this.mimicing){
\r
1317 this.wrap.addClass(this.wrapFocusClass);
\r
1318 this.mimicing = true;
\r
1319 this.doc.on('mousedown', this.mimicBlur, this, {delay: 10});
\r
1320 if(this.monitorTab){
\r
1321 this.on('specialkey', this.checkTab, this);
\r
1327 checkTab : function(me, e){
\r
1328 if(e.getKey() == e.TAB){
\r
1329 this.triggerBlur();
\r
1334 onBlur : Ext.emptyFn,
\r
1337 mimicBlur : function(e){
\r
1338 if(!this.isDestroyed && !this.wrap.contains(e.target) && this.validateBlur(e)){
\r
1339 this.triggerBlur();
\r
1344 triggerBlur : function(){
\r
1345 this.mimicing = false;
\r
1346 this.doc.un('mousedown', this.mimicBlur, this);
\r
1347 if(this.monitorTab && this.el){
\r
1348 this.un('specialkey', this.checkTab, this);
\r
1350 Ext.form.TriggerField.superclass.onBlur.call(this);
\r
1352 this.wrap.removeClass(this.wrapFocusClass);
\r
1356 beforeBlur : Ext.emptyFn,
\r
1359 * Allow or prevent the user from directly editing the field text. If false is passed,
\r
1360 * the user will only be able to modify the field using the trigger. This method
\r
1361 * is the runtime equivalent of setting the 'editable' config option at config time.
\r
1362 * @param {Boolean} value True to allow the user to directly edit the field text
\r
1364 setEditable : function(value){
\r
1365 if(value == this.editable){
\r
1368 this.editable = value;
\r
1370 this.el.addClass('x-trigger-noedit').on('click', this.onTriggerClick, this).dom.setAttribute('readOnly', true);
\r
1372 this.el.removeClass('x-trigger-noedit').un('click', this.onTriggerClick, this).dom.removeAttribute('readOnly');
\r
1377 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
\r
1378 validateBlur : function(e){
\r
1383 * The function that should handle the trigger's click event. This method does nothing by default
\r
1384 * until overridden by an implementing function. See Ext.form.ComboBox and Ext.form.DateField for
\r
1385 * sample implementations.
\r
1387 * @param {EventObject} e
\r
1389 onTriggerClick : Ext.emptyFn
\r
1392 * @cfg {Boolean} grow @hide
\r
1395 * @cfg {Number} growMin @hide
\r
1398 * @cfg {Number} growMax @hide
\r
1403 * @class Ext.form.TwinTriggerField
\r
1404 * @extends Ext.form.TriggerField
\r
1405 * TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
\r
1406 * to be extended by an implementing class. For an example of implementing this class, see the custom
\r
1407 * SearchField implementation here:
\r
1408 * <a href="http://extjs.com/deploy/ext/examples/form/custom.html">http://extjs.com/deploy/ext/examples/form/custom.html</a>
\r
1410 Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
\r
1412 * @cfg {Mixed} triggerConfig
\r
1413 * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements
\r
1414 * for this Field. (Optional).</p>
\r
1415 * <p>Specify this when you need a customized element to contain the two trigger elements for this Field.
\r
1416 * Each trigger element must be marked by the CSS class <tt>x-form-trigger</tt> (also see
\r
1417 * <tt>{@link #trigger1Class}</tt> and <tt>{@link #trigger2Class}</tt>).</p>
\r
1418 * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing,
\r
1419 * positioning and appearance of the triggers.</p>
\r
1422 * @cfg {String} trigger1Class
\r
1423 * An additional CSS class used to style the trigger button. The trigger will always get the
\r
1424 * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
\r
1427 * @cfg {String} trigger2Class
\r
1428 * An additional CSS class used to style the trigger button. The trigger will always get the
\r
1429 * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
\r
1432 initComponent : function(){
\r
1433 Ext.form.TwinTriggerField.superclass.initComponent.call(this);
\r
1435 this.triggerConfig = {
\r
1436 tag:'span', cls:'x-form-twin-triggers', cn:[
\r
1437 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
\r
1438 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
\r
1442 getTrigger : function(index){
\r
1443 return this.triggers[index];
\r
1446 initTrigger : function(){
\r
1447 var ts = this.trigger.select('.x-form-trigger', true);
\r
1448 var triggerField = this;
\r
1449 ts.each(function(t, all, index){
\r
1450 var triggerIndex = 'Trigger'+(index+1);
\r
1451 t.hide = function(){
\r
1452 var w = triggerField.wrap.getWidth();
\r
1453 this.dom.style.display = 'none';
\r
1454 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
\r
1455 this['hidden' + triggerIndex] = true;
\r
1457 t.show = function(){
\r
1458 var w = triggerField.wrap.getWidth();
\r
1459 this.dom.style.display = '';
\r
1460 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
\r
1461 this['hidden' + triggerIndex] = false;
\r
1464 if(this['hide'+triggerIndex]){
\r
1465 t.dom.style.display = 'none';
\r
1466 this['hidden' + triggerIndex] = true;
\r
1468 this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true});
\r
1469 t.addClassOnOver('x-form-trigger-over');
\r
1470 t.addClassOnClick('x-form-trigger-click');
\r
1472 this.triggers = ts.elements;
\r
1475 getTriggerWidth: function(){
\r
1477 Ext.each(this.triggers, function(t, index){
\r
1478 var triggerIndex = 'Trigger' + (index + 1),
\r
1480 if(w === 0 && !this['hidden' + triggerIndex]){
\r
1481 tw += this.defaultTriggerWidth;
\r
1490 onDestroy : function() {
\r
1491 Ext.destroy(this.triggers);
\r
1492 Ext.form.TwinTriggerField.superclass.onDestroy.call(this);
\r
1496 * The function that should handle the trigger's click event. This method does nothing by default
\r
1497 * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
\r
1498 * for additional information.
\r
1500 * @param {EventObject} e
\r
1502 onTrigger1Click : Ext.emptyFn,
\r
1504 * The function that should handle the trigger's click event. This method does nothing by default
\r
1505 * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
\r
1506 * for additional information.
\r
1508 * @param {EventObject} e
\r
1510 onTrigger2Click : Ext.emptyFn
\r
1512 Ext.reg('trigger', Ext.form.TriggerField);/**
1513 * @class Ext.form.TextArea
1514 * @extends Ext.form.TextField
1515 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
1516 * support for auto-sizing.
1518 * Creates a new TextArea
1519 * @param {Object} config Configuration options
1522 Ext.form.TextArea = Ext.extend(Ext.form.TextField, {
1524 * @cfg {Number} growMin The minimum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
1525 * (defaults to <tt>60</tt>)
1529 * @cfg {Number} growMax The maximum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
1530 * (defaults to <tt>1000</tt>)
1533 growAppend : ' \n ',
1534 growPad : Ext.isWebKit ? -6 : 0,
1536 enterIsSpecial : false,
1539 * @cfg {Boolean} preventScrollbars <tt>true</tt> to prevent scrollbars from appearing regardless of how much text is
1540 * in the field. This option is only relevant when {@link #grow} is <tt>true</tt>. Equivalent to setting overflow: hidden, defaults to
1543 preventScrollbars: false,
1545 * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
1546 * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
1547 * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details. Defaults to:</p>
1548 * <pre><code>{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}</code></pre>
1552 onRender : function(ct, position){
1554 this.defaultAutoCreate = {
1556 style:"width:100px;height:60px;",
1560 Ext.form.TextArea.superclass.onRender.call(this, ct, position);
1562 this.textSizeEl = Ext.DomHelper.append(document.body, {
1563 tag: "pre", cls: "x-form-grow-sizer"
1565 if(this.preventScrollbars){
1566 this.el.setStyle("overflow", "hidden");
1568 this.el.setHeight(this.growMin);
1572 onDestroy : function(){
1573 Ext.destroy(this.textSizeEl);
1574 Ext.form.TextArea.superclass.onDestroy.call(this);
1577 fireKey : function(e){
1578 if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
1579 this.fireEvent("specialkey", this, e);
1584 onKeyUp : function(e){
1585 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
1588 Ext.form.TextArea.superclass.onKeyUp.call(this, e);
1592 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
1593 * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
1595 autoSize: function(){
1596 if(!this.grow || !this.textSizeEl){
1600 var v = el.dom.value;
1601 var ts = this.textSizeEl;
1603 ts.appendChild(document.createTextNode(v));
1605 Ext.fly(ts).setWidth(this.el.getWidth());
1609 v += this.growAppend;
1611 v = v.replace(/\n/g, '<br />');
1615 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin) + this.growPad);
1616 if(h != this.lastHeight){
1617 this.lastHeight = h;
1618 this.el.setHeight(h);
1619 this.fireEvent("autosize", this, h);
1623 Ext.reg('textarea', Ext.form.TextArea);/**
1624 * @class Ext.form.NumberField
1625 * @extends Ext.form.TextField
1626 * Numeric text field that provides automatic keystroke filtering and numeric validation.
1628 * Creates a new NumberField
1629 * @param {Object} config Configuration options
1630 * @xtype numberfield
1632 Ext.form.NumberField = Ext.extend(Ext.form.TextField, {
1634 * @cfg {RegExp} stripCharsRe @hide
1637 * @cfg {RegExp} maskRe @hide
1640 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
1642 fieldClass: "x-form-field x-form-num-field",
1644 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
1646 allowDecimals : true,
1648 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
1650 decimalSeparator : ".",
1652 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
1654 decimalPrecision : 2,
1656 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
1658 allowNegative : true,
1660 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
1662 minValue : Number.NEGATIVE_INFINITY,
1664 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
1666 maxValue : Number.MAX_VALUE,
1668 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
1670 minText : "The minimum value for this field is {0}",
1672 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
1674 maxText : "The maximum value for this field is {0}",
1676 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
1677 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
1679 nanText : "{0} is not a valid number",
1681 * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
1683 baseChars : "0123456789",
1686 initEvents : function(){
1687 var allowed = this.baseChars + '';
1688 if (this.allowDecimals) {
1689 allowed += this.decimalSeparator;
1691 if (this.allowNegative) {
1694 this.maskRe = new RegExp('[' + Ext.escapeRe(allowed) + ']');
1695 Ext.form.NumberField.superclass.initEvents.call(this);
1699 validateValue : function(value){
1700 if(!Ext.form.NumberField.superclass.validateValue.call(this, value)){
1703 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
1706 value = String(value).replace(this.decimalSeparator, ".");
1708 this.markInvalid(String.format(this.nanText, value));
1711 var num = this.parseValue(value);
1712 if(num < this.minValue){
1713 this.markInvalid(String.format(this.minText, this.minValue));
1716 if(num > this.maxValue){
1717 this.markInvalid(String.format(this.maxText, this.maxValue));
1723 getValue : function(){
1724 return this.fixPrecision(this.parseValue(Ext.form.NumberField.superclass.getValue.call(this)));
1727 setValue : function(v){
1728 v = typeof v == 'number' ? v : parseFloat(String(v).replace(this.decimalSeparator, "."));
1729 v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
1730 return Ext.form.NumberField.superclass.setValue.call(this, v);
1734 parseValue : function(value){
1735 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
1736 return isNaN(value) ? '' : value;
1740 fixPrecision : function(value){
1741 var nan = isNaN(value);
1742 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
1743 return nan ? '' : value;
1745 return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
1748 beforeBlur : function(){
1749 var v = this.parseValue(this.getRawValue());
1750 if(!Ext.isEmpty(v)){
1751 this.setValue(this.fixPrecision(v));
1755 Ext.reg('numberfield', Ext.form.NumberField);/**
1756 * @class Ext.form.DateField
1757 * @extends Ext.form.TriggerField
1758 * Provides a date input field with a {@link Ext.DatePicker} dropdown and automatic date validation.
1760 * Create a new DateField
1761 * @param {Object} config
1764 Ext.form.DateField = Ext.extend(Ext.form.TriggerField, {
1766 * @cfg {String} format
1767 * The default date format string which can be overriden for localization support. The format must be
1768 * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/Y'</tt>).
1772 * @cfg {String} altFormats
1773 * Multiple date formats separated by "<tt>|</tt>" to try when parsing a user input value and it
1774 * does not match the defined format (defaults to
1775 * <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>).
1777 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",
1779 * @cfg {String} disabledDaysText
1780 * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)
1782 disabledDaysText : "Disabled",
1784 * @cfg {String} disabledDatesText
1785 * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)
1787 disabledDatesText : "Disabled",
1789 * @cfg {String} minText
1790 * The error text to display when the date in the cell is before <tt>{@link #minValue}</tt> (defaults to
1791 * <tt>'The date in this field must be after {minValue}'</tt>).
1793 minText : "The date in this field must be equal to or after {0}",
1795 * @cfg {String} maxText
1796 * The error text to display when the date in the cell is after <tt>{@link #maxValue}</tt> (defaults to
1797 * <tt>'The date in this field must be before {maxValue}'</tt>).
1799 maxText : "The date in this field must be equal to or before {0}",
1801 * @cfg {String} invalidText
1802 * The error text to display when the date in the field is invalid (defaults to
1803 * <tt>'{value} is not a valid date - it must be in the format {format}'</tt>).
1805 invalidText : "{0} is not a valid date - it must be in the format {1}",
1807 * @cfg {String} triggerClass
1808 * An additional CSS class used to style the trigger button. The trigger will always get the
1809 * class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
1810 * (defaults to <tt>'x-form-date-trigger'</tt> which displays a calendar icon).
1812 triggerClass : 'x-form-date-trigger',
1814 * @cfg {Boolean} showToday
1815 * <tt>false</tt> to hide the footer area of the DatePicker containing the Today button and disable
1816 * the keyboard handler for spacebar that selects the current date (defaults to <tt>true</tt>).
1820 * @cfg {Date/String} minValue
1821 * The minimum allowed date. Can be either a Javascript date object or a string date in a
1822 * valid format (defaults to null).
1825 * @cfg {Date/String} maxValue
1826 * The maximum allowed date. Can be either a Javascript date object or a string date in a
1827 * valid format (defaults to null).
1830 * @cfg {Array} disabledDays
1831 * An array of days to disable, 0 based (defaults to null). Some examples:<pre><code>
1832 // disable Sunday and Saturday:
1833 disabledDays: [0, 6]
1834 // disable weekdays:
1835 disabledDays: [1,2,3,4,5]
1839 * @cfg {Array} disabledDates
1840 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
1841 * expression so they are very powerful. Some examples:<pre><code>
1842 // disable these exact dates:
1843 disabledDates: ["03/08/2003", "09/16/2003"]
1844 // disable these days for every year:
1845 disabledDates: ["03/08", "09/16"]
1846 // only match the beginning (useful if you are using short years):
1847 disabledDates: ["^03/08"]
1848 // disable every day in March 2006:
1849 disabledDates: ["03/../2006"]
1850 // disable every day in every March:
1851 disabledDates: ["^03"]
1853 * Note that the format of the dates included in the array should exactly match the {@link #format} config.
1854 * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
1855 * it, you will have to escape the dot when restricting dates. For example: <tt>["03\\.08\\.03"]</tt>.
1858 * @cfg {String/Object} autoCreate
1859 * A {@link Ext.DomHelper DomHelper element specification object}, or <tt>true</tt> for the default element
1860 * specification object:<pre><code>
1861 * autoCreate: {tag: "input", type: "text", size: "10", autocomplete: "off"}
1866 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
1868 initComponent : function(){
1869 Ext.form.DateField.superclass.initComponent.call(this);
1874 * Fires when a date is selected via the date picker.
1875 * @param {Ext.form.DateField} this
1876 * @param {Date} date The date that was selected
1881 if(Ext.isString(this.minValue)){
1882 this.minValue = this.parseDate(this.minValue);
1884 if(Ext.isString(this.maxValue)){
1885 this.maxValue = this.parseDate(this.maxValue);
1887 this.disabledDatesRE = null;
1888 this.initDisabledDays();
1892 initDisabledDays : function(){
1893 if(this.disabledDates){
1894 var dd = this.disabledDates,
1895 len = dd.length - 1,
1898 Ext.each(dd, function(d, i){
1899 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
1904 this.disabledDatesRE = new RegExp(re + ')');
1909 * Replaces any existing disabled dates with new values and refreshes the DatePicker.
1910 * @param {Array} disabledDates An array of date strings (see the <tt>{@link #disabledDates}</tt> config
1911 * for details on supported values) used to disable a pattern of dates.
1913 setDisabledDates : function(dd){
1914 this.disabledDates = dd;
1915 this.initDisabledDays();
1917 this.menu.picker.setDisabledDates(this.disabledDatesRE);
1922 * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
1923 * @param {Array} disabledDays An array of disabled day indexes. See the <tt>{@link #disabledDays}</tt>
1924 * config for details on supported values.
1926 setDisabledDays : function(dd){
1927 this.disabledDays = dd;
1929 this.menu.picker.setDisabledDays(dd);
1934 * Replaces any existing <tt>{@link #minValue}</tt> with the new value and refreshes the DatePicker.
1935 * @param {Date} value The minimum date that can be selected
1937 setMinValue : function(dt){
1938 this.minValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
1940 this.menu.picker.setMinDate(this.minValue);
1945 * Replaces any existing <tt>{@link #maxValue}</tt> with the new value and refreshes the DatePicker.
1946 * @param {Date} value The maximum date that can be selected
1948 setMaxValue : function(dt){
1949 this.maxValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
1951 this.menu.picker.setMaxDate(this.maxValue);
1956 validateValue : function(value){
1957 value = this.formatDate(value);
1958 if(!Ext.form.DateField.superclass.validateValue.call(this, value)){
1961 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
1965 value = this.parseDate(value);
1967 this.markInvalid(String.format(this.invalidText, svalue, this.format));
1970 var time = value.getTime();
1971 if(this.minValue && time < this.minValue.getTime()){
1972 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
1975 if(this.maxValue && time > this.maxValue.getTime()){
1976 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
1979 if(this.disabledDays){
1980 var day = value.getDay();
1981 for(var i = 0; i < this.disabledDays.length; i++) {
1982 if(day === this.disabledDays[i]){
1983 this.markInvalid(this.disabledDaysText);
1988 var fvalue = this.formatDate(value);
1989 if(this.disabledDatesRE && this.disabledDatesRE.test(fvalue)){
1990 this.markInvalid(String.format(this.disabledDatesText, fvalue));
1997 // Provides logic to override the default TriggerField.validateBlur which just returns true
1998 validateBlur : function(){
1999 return !this.menu || !this.menu.isVisible();
2003 * Returns the current date value of the date field.
2004 * @return {Date} The date value
2006 getValue : function(){
2007 return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";
2011 * Sets the value of the date field. You can pass a date object or any string that can be
2012 * parsed into a valid date, using <tt>{@link #format}</tt> as the date format, according
2013 * to the same rules as {@link Date#parseDate} (the default format used is <tt>"m/d/Y"</tt>).
2016 //All of these calls set the same date value (May 4, 2006)
2018 //Pass a date object:
2019 var dt = new Date('5/4/2006');
2020 dateField.setValue(dt);
2022 //Pass a date string (default format):
2023 dateField.setValue('05/04/2006');
2025 //Pass a date string (custom format):
2026 dateField.format = 'Y-m-d';
2027 dateField.setValue('2006-05-04');
2029 * @param {String/Date} date The date or valid date string
2030 * @return {Ext.form.Field} this
2032 setValue : function(date){
2033 return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
2037 parseDate : function(value){
2038 if(!value || Ext.isDate(value)){
2041 var v = Date.parseDate(value, this.format);
2042 if(!v && this.altFormats){
2043 if(!this.altFormatsArray){
2044 this.altFormatsArray = this.altFormats.split("|");
2046 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
2047 v = Date.parseDate(value, this.altFormatsArray[i]);
2054 onDestroy : function(){
2055 Ext.destroy(this.menu);
2056 Ext.form.DateField.superclass.onDestroy.call(this);
2060 formatDate : function(date){
2061 return Ext.isDate(date) ? date.dateFormat(this.format) : date;
2065 * @method onTriggerClick
2069 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
2070 onTriggerClick : function(){
2074 if(this.menu == null){
2075 this.menu = new Ext.menu.DateMenu({
2080 Ext.apply(this.menu.picker, {
2081 minDate : this.minValue,
2082 maxDate : this.maxValue,
2083 disabledDatesRE : this.disabledDatesRE,
2084 disabledDatesText : this.disabledDatesText,
2085 disabledDays : this.disabledDays,
2086 disabledDaysText : this.disabledDaysText,
2087 format : this.format,
2088 showToday : this.showToday,
2089 minText : String.format(this.minText, this.formatDate(this.minValue)),
2090 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
2092 this.menu.picker.setValue(this.getValue() || new Date());
2093 this.menu.show(this.el, "tl-bl?");
2094 this.menuEvents('on');
2098 menuEvents: function(method){
2099 this.menu[method]('select', this.onSelect, this);
2100 this.menu[method]('hide', this.onMenuHide, this);
2101 this.menu[method]('show', this.onFocus, this);
2104 onSelect: function(m, d){
2106 this.fireEvent('select', this, d);
2110 onMenuHide: function(){
2111 this.focus(false, 60);
2112 this.menuEvents('un');
2116 beforeBlur : function(){
2117 var v = this.parseDate(this.getRawValue());
2124 * @cfg {Boolean} grow @hide
2127 * @cfg {Number} growMin @hide
2130 * @cfg {Number} growMax @hide
2137 Ext.reg('datefield', Ext.form.DateField);/**
\r
2138 * @class Ext.form.DisplayField
\r
2139 * @extends Ext.form.Field
\r
2140 * A display-only text field which is not validated and not submitted.
\r
2142 * Creates a new DisplayField.
\r
2143 * @param {Object} config Configuration options
\r
2144 * @xtype displayfield
\r
2146 Ext.form.DisplayField = Ext.extend(Ext.form.Field, {
\r
2147 validationEvent : false,
\r
2148 validateOnBlur : false,
\r
2149 defaultAutoCreate : {tag: "div"},
\r
2151 * @cfg {String} fieldClass The default CSS class for the field (defaults to <tt>"x-form-display-field"</tt>)
\r
2153 fieldClass : "x-form-display-field",
\r
2155 * @cfg {Boolean} htmlEncode <tt>false</tt> to skip HTML-encoding the text when rendering it (defaults to
\r
2156 * <tt>false</tt>). This might be useful if you want to include tags in the field's innerHTML rather than
\r
2157 * rendering them as string literals per the default logic.
\r
2159 htmlEncode: false,
\r
2162 initEvents : Ext.emptyFn,
\r
2164 isValid : function(){
\r
2168 validate : function(){
\r
2172 getRawValue : function(){
\r
2173 var v = this.rendered ? this.el.dom.innerHTML : Ext.value(this.value, '');
\r
2174 if(v === this.emptyText){
\r
2177 if(this.htmlEncode){
\r
2178 v = Ext.util.Format.htmlDecode(v);
\r
2183 getValue : function(){
\r
2184 return this.getRawValue();
\r
2187 getName: function() {
\r
2191 setRawValue : function(v){
\r
2192 if(this.htmlEncode){
\r
2193 v = Ext.util.Format.htmlEncode(v);
\r
2195 return this.rendered ? (this.el.dom.innerHTML = (Ext.isEmpty(v) ? '' : v)) : (this.value = v);
\r
2198 setValue : function(v){
\r
2199 this.setRawValue(v);
\r
2203 * @cfg {String} inputType
\r
2207 * @cfg {Boolean} disabled
\r
2211 * @cfg {Boolean} readOnly
\r
2215 * @cfg {Boolean} validateOnBlur
\r
2219 * @cfg {Number} validationDelay
\r
2223 * @cfg {String/Boolean} validationEvent
\r
2228 Ext.reg('displayfield', Ext.form.DisplayField);
\r
2230 * @class Ext.form.ComboBox
\r
2231 * @extends Ext.form.TriggerField
\r
2232 * <p>A combobox control with support for autocomplete, remote-loading, paging and many other features.</p>
\r
2233 * <p>A ComboBox works in a similar manner to a traditional HTML <select> field. The difference is
\r
2234 * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input
\r
2235 * field to hold the value of the valueField. The <i>{@link #displayField}</i> is shown in the text field
\r
2236 * which is named according to the {@link #name}.</p>
\r
2237 * <p><b><u>Events</u></b></p>
\r
2238 * <p>To do something when something in ComboBox is selected, configure the select event:<pre><code>
\r
2239 var cb = new Ext.form.ComboBox({
\r
2240 // all of your config options
\r
2243 'select': yourFunction
\r
2247 // Alternatively, you can assign events after the object is created:
\r
2248 var cb = new Ext.form.ComboBox(yourOptions);
\r
2249 cb.on('select', yourFunction, yourScope);
\r
2250 * </code></pre></p>
\r
2252 * <p><b><u>ComboBox in Grid</u></b></p>
\r
2253 * <p>If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer}
\r
2254 * will be needed to show the displayField when the editor is not active. Set up the renderer manually, or implement
\r
2255 * a reusable render, for example:<pre><code>
\r
2256 // create reusable renderer
\r
2257 Ext.util.Format.comboRenderer = function(combo){
\r
2258 return function(value){
\r
2259 var record = combo.findRecord(combo.{@link #valueField}, value);
\r
2260 return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
\r
2264 // create the combo instance
\r
2265 var combo = new Ext.form.ComboBox({
\r
2266 {@link #typeAhead}: true,
\r
2267 {@link #triggerAction}: 'all',
\r
2268 {@link #lazyRender}:true,
\r
2269 {@link #mode}: 'local',
\r
2270 {@link #store}: new Ext.data.ArrayStore({
\r
2276 data: [[1, 'item1'], [2, 'item2']]
\r
2278 {@link #valueField}: 'myId',
\r
2279 {@link #displayField}: 'displayText'
\r
2282 // snippet of column model used within grid
\r
2283 var cm = new Ext.grid.ColumnModel([{
\r
2286 header: "Some Header",
\r
2287 dataIndex: 'whatever',
\r
2289 editor: combo, // specify reference to combo instance
\r
2290 renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
\r
2294 * </code></pre></p>
\r
2296 * <p><b><u>Filtering</u></b></p>
\r
2297 * <p>A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox
\r
2298 * store manually see <tt>{@link #lastQuery}</tt>.</p>
\r
2300 * Create a new ComboBox.
\r
2301 * @param {Object} config Configuration options
\r
2304 Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, {
\r
2306 * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox.
\r
2307 * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or
\r
2308 * {@link Ext.form.FormPanel}, you must also set <tt>{@link #lazyRender} = true</tt>.
\r
2311 * @cfg {Boolean} lazyRender <tt>true</tt> to prevent the ComboBox from rendering until requested
\r
2312 * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}),
\r
2313 * defaults to <tt>false</tt>).
\r
2316 * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or <tt>true</tt> for a default
\r
2317 * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
\r
2318 * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details. Defaults to:</p>
\r
2319 * <pre><code>{tag: "input", type: "text", size: "24", autocomplete: "off"}</code></pre>
\r
2322 * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).
\r
2323 * Acceptable values for this property are:
\r
2324 * <div class="mdetail-params"><ul>
\r
2325 * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
\r
2326 * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally,
\r
2327 * automatically generating {@link Ext.data.Field#name field names} to work with all data components.
\r
2328 * <div class="mdetail-params"><ul>
\r
2329 * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
\r
2330 * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
\r
2331 * {@link #valueField} and {@link #displayField})</div></li>
\r
2332 * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
\r
2333 * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
\r
2334 * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
\r
2335 * </div></li></ul></div></li></ul></div>
\r
2336 * <p>See also <tt>{@link #mode}</tt>.</p>
\r
2339 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
\r
2340 * the dropdown list (defaults to undefined, with no header element)
\r
2344 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
\r
2346 * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown
\r
2347 * list (defaults to the width of the ComboBox field). See also <tt>{@link #minListWidth}
\r
2350 * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
\r
2351 * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field1'</tt> if
\r
2352 * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
\r
2353 * the store configuration}).
\r
2354 * <p>See also <tt>{@link #valueField}</tt>.</p>
\r
2355 * <p><b>Note</b>: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a
\r
2356 * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not
\r
2360 * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this
\r
2361 * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field2'</tt> if
\r
2362 * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
\r
2363 * the store configuration}).
\r
2364 * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be
\r
2365 * mapped. See also <tt>{@link #hiddenName}</tt>, <tt>{@link #hiddenValue}</tt>, and <tt>{@link #displayField}</tt>.</p>
\r
2368 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
\r
2369 * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically
\r
2370 * post during a form submission. See also {@link #valueField}.
\r
2371 * <p><b>Note</b>: the hidden field's id will also default to this name if {@link #hiddenId} is not specified.
\r
2372 * The ComboBox {@link Ext.Component#id id} and the <tt>{@link #hiddenId}</tt> <b>should be different</b>, since
\r
2373 * no two DOM nodes should share the same id. So, if the ComboBox <tt>{@link Ext.form.Field#name name}</tt> and
\r
2374 * <tt>hiddenName</tt> are the same, you should specify a unique <tt>{@link #hiddenId}</tt>.</p>
\r
2377 * @cfg {String} hiddenId If <tt>{@link #hiddenName}</tt> is specified, <tt>hiddenId</tt> can also be provided
\r
2378 * to give the hidden field a unique id (defaults to the <tt>{@link #hiddenName}</tt>). The <tt>hiddenId</tt>
\r
2379 * and combo {@link Ext.Component#id id} should be different, since no two DOM
\r
2380 * nodes should share the same id.
\r
2383 * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is
\r
2384 * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured
\r
2385 * <tt>{@link Ext.form.Field#value value}</tt>.
\r
2388 * @cfg {String} listClass The CSS class to add to the predefined <tt>'x-combo-list'</tt> class
\r
2389 * applied the dropdown list element (defaults to '').
\r
2393 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list
\r
2394 * (defaults to <tt>'x-combo-selected'</tt>)
\r
2396 selectedClass : 'x-combo-selected',
\r
2398 * @cfg {String} listEmptyText The empty text to display in the data view if no items are found.
\r
2399 * (defaults to '')
\r
2401 listEmptyText: '',
\r
2403 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always
\r
2404 * get the class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
\r
2405 * (defaults to <tt>'x-form-arrow-trigger'</tt> which displays a downward arrow icon).
\r
2407 triggerClass : 'x-form-arrow-trigger',
\r
2409 * @cfg {Boolean/String} shadow <tt>true</tt> or <tt>"sides"</tt> for the default effect, <tt>"frame"</tt> for
\r
2410 * 4-way shadow, and <tt>"drop"</tt> for bottom-right
\r
2414 * @cfg {String} listAlign A valid anchor position value. See <tt>{@link Ext.Element#alignTo}</tt> for details
\r
2415 * on supported anchor positions (defaults to <tt>'tl-bl?'</tt>)
\r
2417 listAlign : 'tl-bl?',
\r
2419 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown
\r
2420 * (defaults to <tt>300</tt>)
\r
2424 * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its
\r
2425 * distance to the viewport edges (defaults to <tt>90</tt>)
\r
2429 * @cfg {String} triggerAction The action to execute when the trigger is clicked.
\r
2430 * <div class="mdetail-params"><ul>
\r
2431 * <li><b><tt>'query'</tt></b> : <b>Default</b>
\r
2432 * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.</p></li>
\r
2433 * <li><b><tt>'all'</tt></b> :
\r
2434 * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>
\r
2436 * <p>See also <code>{@link #queryParam}</code>.</p>
\r
2438 triggerAction : 'query',
\r
2440 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
\r
2441 * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #mode} = 'remote'</tt> or <tt>0</tt> if
\r
2442 * <tt>{@link #mode} = 'local'</tt>, does not apply if
\r
2443 * <tt>{@link Ext.form.TriggerField#editable editable} = false</tt>).
\r
2447 * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being
\r
2448 * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
\r
2449 * to <tt>false</tt>)
\r
2451 typeAhead : false,
\r
2453 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
\r
2454 * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #mode} = 'remote'</tt>
\r
2455 * or <tt>10</tt> if <tt>{@link #mode} = 'local'</tt>)
\r
2459 * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.PagingToolbar} is displayed in the
\r
2460 * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
\r
2461 * {@link Ext.PagingToolbar#pageSize limit} parameters. Only applies when <tt>{@link #mode} = 'remote'</tt>
\r
2462 * (defaults to <tt>0</tt>).
\r
2466 * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.
\r
2467 * Only applies when <tt>{@link Ext.form.TriggerField#editable editable} = true</tt> (defaults to
\r
2468 * <tt>false</tt>).
\r
2470 selectOnFocus : false,
\r
2472 * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)
\r
2473 * as it will be passed on the querystring (defaults to <tt>'query'</tt>)
\r
2475 queryParam : 'query',
\r
2477 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
\r
2478 * when <tt>{@link #mode} = 'remote'</tt> (defaults to <tt>'Loading...'</tt>)
\r
2480 loadingText : 'Loading...',
\r
2482 * @cfg {Boolean} resizable <tt>true</tt> to add a resize handle to the bottom of the dropdown list
\r
2483 * (creates an {@link Ext.Resizable} with 'se' {@link Ext.Resizable#pinned pinned} handles).
\r
2484 * Defaults to <tt>false</tt>.
\r
2486 resizable : false,
\r
2488 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if
\r
2489 * <tt>{@link #resizable} = true</tt> (defaults to <tt>8</tt>)
\r
2493 * @cfg {String} allQuery The text query to send to the server to return all records for the list
\r
2494 * with no filtering (defaults to '')
\r
2498 * @cfg {String} mode Acceptable values are:
\r
2499 * <div class="mdetail-params"><ul>
\r
2500 * <li><b><tt>'remote'</tt></b> : <b>Default</b>
\r
2501 * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger
\r
2502 * is clicked. If you do not want the store to be automatically loaded the first time the trigger is
\r
2503 * clicked, set to <tt>'local'</tt> and manually load the store. To force a requery of the store
\r
2504 * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>
\r
2505 * <li><b><tt>'local'</tt></b> :
\r
2506 * <p class="sub-desc">ComboBox loads local data</p>
\r
2508 var combo = new Ext.form.ComboBox({
\r
2509 renderTo: document.body,
\r
2511 store: new Ext.data.ArrayStore({
\r
2514 'myId', // numeric value is the key
\r
2517 data: [[1, 'item1'], [2, 'item2']] // data is local
\r
2519 valueField: 'myId',
\r
2520 displayField: 'displayText',
\r
2521 triggerAction: 'all'
\r
2523 * </code></pre></li>
\r
2528 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to <tt>70</tt>, will
\r
2529 * be ignored if <tt>{@link #listWidth}</tt> has a higher value)
\r
2531 minListWidth : 70,
\r
2533 * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,
\r
2534 * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)
\r
2536 forceSelection : false,
\r
2538 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
\r
2539 * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)
\r
2541 typeAheadDelay : 250,
\r
2543 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
\r
2544 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
\r
2545 * default text is used, it means there is no value set and no validation will occur on this field.
\r
2549 * @cfg {Boolean} lazyInit <tt>true</tt> to not initialize the list for this combo until the field is focused
\r
2550 * (defaults to <tt>true</tt>)
\r
2555 * The value of the match string used to filter the store. Delete this property to force a requery.
\r
2558 var combo = new Ext.form.ComboBox({
\r
2563 // delete the previous query in the beforequery event or set
\r
2564 // combo.lastQuery = null (this will reload the store the next time it expands)
\r
2565 beforequery: function(qe){
\r
2566 delete qe.combo.lastQuery;
\r
2571 * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
\r
2572 * configure the combo with <tt>lastQuery=''</tt>. Example use:
\r
2574 var combo = new Ext.form.ComboBox({
\r
2577 triggerAction: 'all',
\r
2581 * @property lastQuery
\r
2586 initComponent : function(){
\r
2587 Ext.form.ComboBox.superclass.initComponent.call(this);
\r
2591 * Fires when the dropdown list is expanded
\r
2592 * @param {Ext.form.ComboBox} combo This combo box
\r
2597 * Fires when the dropdown list is collapsed
\r
2598 * @param {Ext.form.ComboBox} combo This combo box
\r
2602 * @event beforeselect
\r
2603 * Fires before a list item is selected. Return false to cancel the selection.
\r
2604 * @param {Ext.form.ComboBox} combo This combo box
\r
2605 * @param {Ext.data.Record} record The data record returned from the underlying store
\r
2606 * @param {Number} index The index of the selected item in the dropdown list
\r
2611 * Fires when a list item is selected
\r
2612 * @param {Ext.form.ComboBox} combo This combo box
\r
2613 * @param {Ext.data.Record} record The data record returned from the underlying store
\r
2614 * @param {Number} index The index of the selected item in the dropdown list
\r
2618 * @event beforequery
\r
2619 * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
\r
2620 * cancel property to true.
\r
2621 * @param {Object} queryEvent An object that has these properties:<ul>
\r
2622 * <li><code>combo</code> : Ext.form.ComboBox <div class="sub-desc">This combo box</div></li>
\r
2623 * <li><code>query</code> : String <div class="sub-desc">The query</div></li>
\r
2624 * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>
\r
2625 * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>
\r
2630 if(this.transform){
\r
2631 var s = Ext.getDom(this.transform);
\r
2632 if(!this.hiddenName){
\r
2633 this.hiddenName = s.name;
\r
2636 this.mode = 'local';
\r
2637 var d = [], opts = s.options;
\r
2638 for(var i = 0, len = opts.length;i < len; i++){
\r
2640 value = (o.hasAttribute ? o.hasAttribute('value') : o.getAttributeNode('value').specified) ? o.value : o.text;
\r
2641 if(o.selected && Ext.isEmpty(this.value, true)) {
\r
2642 this.value = value;
\r
2644 d.push([value, o.text]);
\r
2646 this.store = new Ext.data.ArrayStore({
\r
2648 fields: ['value', 'text'],
\r
2652 this.valueField = 'value';
\r
2653 this.displayField = 'text';
\r
2655 s.name = Ext.id(); // wipe out the name in case somewhere else they have a reference
\r
2656 if(!this.lazyRender){
\r
2657 this.target = true;
\r
2658 this.el = Ext.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
\r
2659 this.render(this.el.parentNode, s);
\r
2660 Ext.removeNode(s); // remove it
\r
2662 Ext.removeNode(s); // remove it
\r
2665 //auto-configure store from local array data
\r
2666 else if(this.store){
\r
2667 this.store = Ext.StoreMgr.lookup(this.store);
\r
2668 if(this.store.autoCreated){
\r
2669 this.displayField = this.valueField = 'field1';
\r
2670 if(!this.store.expandData){
\r
2671 this.displayField = 'field2';
\r
2673 this.mode = 'local';
\r
2677 this.selectedIndex = -1;
\r
2678 if(this.mode == 'local'){
\r
2679 if(!Ext.isDefined(this.initialConfig.queryDelay)){
\r
2680 this.queryDelay = 10;
\r
2682 if(!Ext.isDefined(this.initialConfig.minChars)){
\r
2683 this.minChars = 0;
\r
2689 onRender : function(ct, position){
\r
2690 Ext.form.ComboBox.superclass.onRender.call(this, ct, position);
\r
2691 if(this.hiddenName){
\r
2692 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,
\r
2693 id: (this.hiddenId||this.hiddenName)}, 'before', true);
\r
2695 // prevent input submission
\r
2696 this.el.dom.removeAttribute('name');
\r
2699 this.el.dom.setAttribute('autocomplete', 'off');
\r
2702 if(!this.lazyInit){
\r
2705 this.on('focus', this.initList, this, {single: true});
\r
2710 initValue : function(){
\r
2711 Ext.form.ComboBox.superclass.initValue.call(this);
\r
2712 if(this.hiddenField){
\r
2713 this.hiddenField.value =
\r
2714 Ext.isDefined(this.hiddenValue) ? this.hiddenValue :
\r
2715 Ext.isDefined(this.value) ? this.value : '';
\r
2720 initList : function(){
\r
2722 var cls = 'x-combo-list';
\r
2724 this.list = new Ext.Layer({
\r
2725 parentEl: this.getListParent(),
\r
2726 shadow: this.shadow,
\r
2727 cls: [cls, this.listClass].join(' '),
\r
2731 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
\r
2732 this.list.setSize(lw, 0);
\r
2733 this.list.swallowEvent('mousewheel');
\r
2734 this.assetHeight = 0;
\r
2735 if(this.syncFont !== false){
\r
2736 this.list.setStyle('font-size', this.el.getStyle('font-size'));
\r
2739 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
\r
2740 this.assetHeight += this.header.getHeight();
\r
2743 this.innerList = this.list.createChild({cls:cls+'-inner'});
\r
2744 this.mon(this.innerList, 'mouseover', this.onViewOver, this);
\r
2745 this.mon(this.innerList, 'mousemove', this.onViewMove, this);
\r
2746 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
\r
2748 if(this.pageSize){
\r
2749 this.footer = this.list.createChild({cls:cls+'-ft'});
\r
2750 this.pageTb = new Ext.PagingToolbar({
\r
2751 store: this.store,
\r
2752 pageSize: this.pageSize,
\r
2753 renderTo:this.footer
\r
2755 this.assetHeight += this.footer.getHeight();
\r
2760 * @cfg {String/Ext.XTemplate} tpl <p>The template string, or {@link Ext.XTemplate} instance to
\r
2761 * use to display each item in the dropdown list. The dropdown list is displayed in a
\r
2762 * DataView. See {@link #view}.</p>
\r
2763 * <p>The default template string is:</p><pre><code>
\r
2764 '<tpl for="."><div class="x-combo-list-item">{' + this.displayField + '}</div></tpl>'
\r
2766 * <p>Override the default value to create custom UI layouts for items in the list.
\r
2767 * For example:</p><pre><code>
\r
2768 '<tpl for="."><div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}</div></tpl>'
\r
2770 * <p>The template <b>must</b> contain one or more substitution parameters using field
\r
2771 * names from the Combo's</b> {@link #store Store}. In the example above an
\r
2772 * <pre>ext:qtip</pre> attribute is added to display other fields from the Store.</p>
\r
2773 * <p>To preserve the default visual look of list items, add the CSS class name
\r
2774 * <pre>x-combo-list-item</pre> to the template's container element.</p>
\r
2775 * <p>Also see {@link #itemSelector} for additional details.</p>
\r
2777 this.tpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
\r
2779 * @cfg {String} itemSelector
\r
2780 * <p>A simple CSS selector (e.g. div.some-class or span:first-child) that will be
\r
2781 * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown
\r
2782 * display will be working with.</p>
\r
2783 * <p><b>Note</b>: this setting is <b>required</b> if a custom XTemplate has been
\r
2784 * specified in {@link #tpl} which assigns a class other than <pre>'x-combo-list-item'</pre>
\r
2785 * to dropdown list items</b>
\r
2790 * The {@link Ext.DataView DataView} used to display the ComboBox's options.
\r
2791 * @type Ext.DataView
\r
2793 this.view = new Ext.DataView({
\r
2794 applyTo: this.innerList,
\r
2796 singleSelect: true,
\r
2797 selectedClass: this.selectedClass,
\r
2798 itemSelector: this.itemSelector || '.' + cls + '-item',
\r
2799 emptyText: this.listEmptyText
\r
2802 this.mon(this.view, 'click', this.onViewClick, this);
\r
2804 this.bindStore(this.store, true);
\r
2806 if(this.resizable){
\r
2807 this.resizer = new Ext.Resizable(this.list, {
\r
2808 pinned:true, handles:'se'
\r
2810 this.mon(this.resizer, 'resize', function(r, w, h){
\r
2811 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
\r
2812 this.listWidth = w;
\r
2813 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
\r
2814 this.restrictHeight();
\r
2817 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
\r
2823 * <p>Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.</p>
\r
2824 * A custom implementation may be provided as a configuration option if the floating list needs to be rendered
\r
2825 * to a different Element. An example might be rendering the list inside a Menu so that clicking
\r
2826 * the list does not hide the Menu:<pre><code>
\r
2827 var store = new Ext.data.ArrayStore({
\r
2828 autoDestroy: true,
\r
2829 fields: ['initials', 'fullname'],
\r
2831 ['FF', 'Fred Flintstone'],
\r
2832 ['BR', 'Barney Rubble']
\r
2836 var combo = new Ext.form.ComboBox({
\r
2838 displayField: 'fullname',
\r
2839 emptyText: 'Select a name...',
\r
2840 forceSelection: true,
\r
2841 getListParent: function() {
\r
2842 return this.el.up('.x-menu');
\r
2844 iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
\r
2846 selectOnFocus: true,
\r
2847 triggerAction: 'all',
\r
2852 var menu = new Ext.menu.Menu({
\r
2855 combo // A Field in a Menu
\r
2860 getListParent : function() {
\r
2861 return document.body;
\r
2865 * Returns the store associated with this combo.
\r
2866 * @return {Ext.data.Store} The store
\r
2868 getStore : function(){
\r
2869 return this.store;
\r
2873 bindStore : function(store, initial){
\r
2874 if(this.store && !initial){
\r
2875 if(this.store !== store && this.store.autoDestroy){
\r
2876 this.store.destroy();
\r
2878 this.store.un('beforeload', this.onBeforeLoad, this);
\r
2879 this.store.un('load', this.onLoad, this);
\r
2880 this.store.un('exception', this.collapse, this);
\r
2883 this.store = null;
\r
2885 this.view.bindStore(null);
\r
2888 this.pageTb.bindStore(null);
\r
2894 this.lastQuery = null;
\r
2896 this.pageTb.bindStore(store);
\r
2900 this.store = Ext.StoreMgr.lookup(store);
\r
2903 beforeload: this.onBeforeLoad,
\r
2904 load: this.onLoad,
\r
2905 exception: this.collapse
\r
2909 this.view.bindStore(store);
\r
2915 initEvents : function(){
\r
2916 Ext.form.ComboBox.superclass.initEvents.call(this);
\r
2918 this.keyNav = new Ext.KeyNav(this.el, {
\r
2919 "up" : function(e){
\r
2920 this.inKeyMode = true;
\r
2921 this.selectPrev();
\r
2924 "down" : function(e){
\r
2925 if(!this.isExpanded()){
\r
2926 this.onTriggerClick();
\r
2928 this.inKeyMode = true;
\r
2929 this.selectNext();
\r
2933 "enter" : function(e){
\r
2934 this.onViewClick();
\r
2937 "esc" : function(e){
\r
2941 "tab" : function(e){
\r
2942 this.onViewClick(false);
\r
2948 doRelay : function(e, h, hname){
\r
2949 if(hname == 'down' || this.scope.isExpanded()){
\r
2950 // this MUST be called before ComboBox#fireKey()
\r
2951 var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
\r
2952 if(!Ext.isIE && Ext.EventManager.useKeydown){
\r
2953 // call Combo#fireKey() for browsers which use keydown event (except IE)
\r
2954 this.scope.fireKey(e);
\r
2961 forceKeyDown : true,
\r
2962 defaultEventAction: 'stopEvent'
\r
2964 this.queryDelay = Math.max(this.queryDelay || 10,
\r
2965 this.mode == 'local' ? 10 : 250);
\r
2966 this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
\r
2967 if(this.typeAhead){
\r
2968 this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
\r
2970 if(this.editable !== false && !this.enableKeyEvents){
\r
2971 this.mon(this.el, 'keyup', this.onKeyUp, this);
\r
2976 onDestroy : function(){
\r
2978 this.dqTask.cancel();
\r
2979 this.dqTask = null;
\r
2981 this.bindStore(null);
\r
2988 Ext.form.ComboBox.superclass.onDestroy.call(this);
\r
2992 fireKey : function(e){
\r
2993 if (!this.isExpanded()) {
\r
2994 Ext.form.ComboBox.superclass.fireKey.call(this, e);
\r
2999 onResize : function(w, h){
\r
3000 Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
\r
3001 if(this.isVisible() && this.list){
\r
3004 this.bufferSize = w;
\r
3008 doResize: function(w){
\r
3009 if(!Ext.isDefined(this.listWidth)){
\r
3010 var lw = Math.max(w, this.minListWidth);
\r
3011 this.list.setWidth(lw);
\r
3012 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
\r
3017 onEnable : function(){
\r
3018 Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
\r
3019 if(this.hiddenField){
\r
3020 this.hiddenField.disabled = false;
\r
3025 onDisable : function(){
\r
3026 Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
\r
3027 if(this.hiddenField){
\r
3028 this.hiddenField.disabled = true;
\r
3033 onBeforeLoad : function(){
\r
3034 if(!this.hasFocus){
\r
3037 this.innerList.update(this.loadingText ?
\r
3038 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
\r
3039 this.restrictHeight();
\r
3040 this.selectedIndex = -1;
\r
3044 onLoad : function(){
\r
3045 if(!this.hasFocus){
\r
3048 if(this.store.getCount() > 0 || this.listEmptyText){
\r
3050 this.restrictHeight();
\r
3051 if(this.lastQuery == this.allQuery){
\r
3052 if(this.editable){
\r
3053 this.el.dom.select();
\r
3055 if(!this.selectByValue(this.value, true)){
\r
3056 this.select(0, true);
\r
3059 this.selectNext();
\r
3060 if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){
\r
3061 this.taTask.delay(this.typeAheadDelay);
\r
3065 this.onEmptyResults();
\r
3067 //this.el.focus();
\r
3071 onTypeAhead : function(){
\r
3072 if(this.store.getCount() > 0){
\r
3073 var r = this.store.getAt(0);
\r
3074 var newValue = r.data[this.displayField];
\r
3075 var len = newValue.length;
\r
3076 var selStart = this.getRawValue().length;
\r
3077 if(selStart != len){
\r
3078 this.setRawValue(newValue);
\r
3079 this.selectText(selStart, newValue.length);
\r
3085 onSelect : function(record, index){
\r
3086 if(this.fireEvent('beforeselect', this, record, index) !== false){
\r
3087 this.setValue(record.data[this.valueField || this.displayField]);
\r
3089 this.fireEvent('select', this, record, index);
\r
3094 getName: function(){
\r
3095 var hf = this.hiddenField;
\r
3096 return hf && hf.name ? hf.name : this.hiddenName || Ext.form.ComboBox.superclass.getName.call(this);
\r
3100 * Returns the currently selected field value or empty string if no value is set.
\r
3101 * @return {String} value The selected value
\r
3103 getValue : function(){
\r
3104 if(this.valueField){
\r
3105 return Ext.isDefined(this.value) ? this.value : '';
\r
3107 return Ext.form.ComboBox.superclass.getValue.call(this);
\r
3112 * Clears any text/value currently set in the field
\r
3114 clearValue : function(){
\r
3115 if(this.hiddenField){
\r
3116 this.hiddenField.value = '';
\r
3118 this.setRawValue('');
\r
3119 this.lastSelectionText = '';
\r
3120 this.applyEmptyText();
\r
3125 * Sets the specified value into the field. If the value finds a match, the corresponding record text
\r
3126 * will be displayed in the field. If the value does not match the data value of an existing item,
\r
3127 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
\r
3128 * Otherwise the field will be blank (although the value will still be set).
\r
3129 * @param {String} value The value to match
\r
3130 * @return {Ext.form.Field} this
\r
3132 setValue : function(v){
\r
3134 if(this.valueField){
\r
3135 var r = this.findRecord(this.valueField, v);
\r
3137 text = r.data[this.displayField];
\r
3138 }else if(Ext.isDefined(this.valueNotFoundText)){
\r
3139 text = this.valueNotFoundText;
\r
3142 this.lastSelectionText = text;
\r
3143 if(this.hiddenField){
\r
3144 this.hiddenField.value = v;
\r
3146 Ext.form.ComboBox.superclass.setValue.call(this, text);
\r
3152 findRecord : function(prop, value){
\r
3154 if(this.store.getCount() > 0){
\r
3155 this.store.each(function(r){
\r
3156 if(r.data[prop] == value){
\r
3166 onViewMove : function(e, t){
\r
3167 this.inKeyMode = false;
\r
3171 onViewOver : function(e, t){
\r
3172 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
\r
3175 var item = this.view.findItemFromChild(t);
\r
3177 var index = this.view.indexOf(item);
\r
3178 this.select(index, false);
\r
3183 onViewClick : function(doFocus){
\r
3184 var index = this.view.getSelectedIndexes()[0],
\r
3186 r = s.getAt(index);
\r
3188 this.onSelect(r, index);
\r
3189 }else if(s.getCount() === 0){
\r
3190 this.onEmptyResults();
\r
3192 if(doFocus !== false){
\r
3198 restrictHeight : function(){
\r
3199 this.innerList.dom.style.height = '';
\r
3200 var inner = this.innerList.dom,
\r
3201 pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,
\r
3202 h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
\r
3203 ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
\r
3204 hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
\r
3205 space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;
\r
3207 h = Math.min(h, space, this.maxHeight);
\r
3209 this.innerList.setHeight(h);
\r
3210 this.list.beginUpdate();
\r
3211 this.list.setHeight(h+pad);
\r
3212 this.list.alignTo(this.wrap, this.listAlign);
\r
3213 this.list.endUpdate();
\r
3217 onEmptyResults : function(){
\r
3222 * Returns true if the dropdown list is expanded, else false.
\r
3224 isExpanded : function(){
\r
3225 return this.list && this.list.isVisible();
\r
3229 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
\r
3230 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
\r
3231 * @param {String} value The data value of the item to select
\r
3232 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
\r
3233 * selected item if it is not currently in view (defaults to true)
\r
3234 * @return {Boolean} True if the value matched an item in the list, else false
\r
3236 selectByValue : function(v, scrollIntoView){
\r
3237 if(!Ext.isEmpty(v, true)){
\r
3238 var r = this.findRecord(this.valueField || this.displayField, v);
\r
3240 this.select(this.store.indexOf(r), scrollIntoView);
\r
3248 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
\r
3249 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
\r
3250 * @param {Number} index The zero-based index of the list item to select
\r
3251 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
\r
3252 * selected item if it is not currently in view (defaults to true)
\r
3254 select : function(index, scrollIntoView){
\r
3255 this.selectedIndex = index;
\r
3256 this.view.select(index);
\r
3257 if(scrollIntoView !== false){
\r
3258 var el = this.view.getNode(index);
\r
3260 this.innerList.scrollChildIntoView(el, false);
\r
3266 selectNext : function(){
\r
3267 var ct = this.store.getCount();
\r
3269 if(this.selectedIndex == -1){
\r
3271 }else if(this.selectedIndex < ct-1){
\r
3272 this.select(this.selectedIndex+1);
\r
3278 selectPrev : function(){
\r
3279 var ct = this.store.getCount();
\r
3281 if(this.selectedIndex == -1){
\r
3283 }else if(this.selectedIndex !== 0){
\r
3284 this.select(this.selectedIndex-1);
\r
3290 onKeyUp : function(e){
\r
3291 var k = e.getKey();
\r
3292 if(this.editable !== false && (k == e.BACKSPACE || !e.isSpecialKey())){
\r
3294 this.dqTask.delay(this.queryDelay);
\r
3296 Ext.form.ComboBox.superclass.onKeyUp.call(this, e);
\r
3300 validateBlur : function(){
\r
3301 return !this.list || !this.list.isVisible();
\r
3305 initQuery : function(){
\r
3306 this.doQuery(this.getRawValue());
\r
3310 beforeBlur : function(){
\r
3311 var val = this.getRawValue(),
\r
3312 rec = this.findRecord(this.displayField, val);
\r
3313 if(!rec && this.forceSelection){
\r
3314 if(val.length > 0 && val != this.emptyText){
\r
3315 this.el.dom.value = Ext.isDefined(this.lastSelectionText) ? this.lastSelectionText : '';
\r
3316 this.applyEmptyText();
\r
3318 this.clearValue();
\r
3322 val = rec.get(this.valueField || this.displayField);
\r
3324 this.setValue(val);
\r
3329 * Execute a query to filter the dropdown list. Fires the {@link #beforequery} event prior to performing the
\r
3330 * query allowing the query action to be canceled if needed.
\r
3331 * @param {String} query The SQL query to execute
\r
3332 * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer
\r
3333 * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option. It
\r
3334 * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)
\r
3336 doQuery : function(q, forceAll){
\r
3337 q = Ext.isEmpty(q) ? '' : q;
\r
3340 forceAll: forceAll,
\r
3344 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
\r
3348 forceAll = qe.forceAll;
\r
3349 if(forceAll === true || (q.length >= this.minChars)){
\r
3350 if(this.lastQuery !== q){
\r
3351 this.lastQuery = q;
\r
3352 if(this.mode == 'local'){
\r
3353 this.selectedIndex = -1;
\r
3355 this.store.clearFilter();
\r
3357 this.store.filter(this.displayField, q);
\r
3361 this.store.baseParams[this.queryParam] = q;
\r
3363 params: this.getParams(q)
\r
3368 this.selectedIndex = -1;
\r
3375 getParams : function(q){
\r
3377 //p[this.queryParam] = q;
\r
3378 if(this.pageSize){
\r
3380 p.limit = this.pageSize;
\r
3386 * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.
\r
3388 collapse : function(){
\r
3389 if(!this.isExpanded()){
\r
3393 Ext.getDoc().un('mousewheel', this.collapseIf, this);
\r
3394 Ext.getDoc().un('mousedown', this.collapseIf, this);
\r
3395 this.fireEvent('collapse', this);
\r
3399 collapseIf : function(e){
\r
3400 if(!e.within(this.wrap) && !e.within(this.list)){
\r
3406 * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.
\r
3408 expand : function(){
\r
3409 if(this.isExpanded() || !this.hasFocus){
\r
3412 if(this.bufferSize){
\r
3413 this.doResize(this.bufferSize);
\r
3414 delete this.bufferSize;
\r
3416 this.list.alignTo(this.wrap, this.listAlign);
\r
3419 this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
\r
3423 mousewheel: this.collapseIf,
\r
3424 mousedown: this.collapseIf
\r
3426 this.fireEvent('expand', this);
\r
3430 * @method onTriggerClick
\r
3434 // Implements the default empty TriggerField.onTriggerClick function
\r
3435 onTriggerClick : function(){
\r
3436 if(this.disabled){
\r
3439 if(this.isExpanded()){
\r
3444 if(this.triggerAction == 'all') {
\r
3445 this.doQuery(this.allQuery, true);
\r
3447 this.doQuery(this.getRawValue());
\r
3455 * @method autoSize
\r
3458 * @cfg {Boolean} grow @hide
\r
3461 * @cfg {Number} growMin @hide
\r
3464 * @cfg {Number} growMax @hide
\r
3468 Ext.reg('combo', Ext.form.ComboBox);/**
3469 * @class Ext.form.Checkbox
3470 * @extends Ext.form.Field
3471 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
3473 * Creates a new Checkbox
3474 * @param {Object} config Configuration options
3477 Ext.form.Checkbox = Ext.extend(Ext.form.Field, {
3479 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
3481 focusClass : undefined,
3483 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to 'x-form-field')
3485 fieldClass : 'x-form-field',
3487 * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
3491 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
3492 * {tag: 'input', type: 'checkbox', autocomplete: 'off'})
3494 defaultAutoCreate : { tag: 'input', type: 'checkbox', autocomplete: 'off'},
3496 * @cfg {String} boxLabel The text that appears beside the checkbox
3499 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
3502 * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of
3503 * handling the check event). The handler is passed the following parameters:
3504 * <div class="mdetail-params"><ul>
3505 * <li><b>checkbox</b> : Ext.form.Checkbox<div class="sub-desc">The Checkbox being toggled.</div></li>
3506 * <li><b>checked</b> : Boolean<div class="sub-desc">The new checked state of the checkbox.</div></li>
3510 * @cfg {Object} scope An object to use as the scope ('this' reference) of the {@link #handler} function
3511 * (defaults to this Checkbox).
3515 actionMode : 'wrap',
3518 initComponent : function(){
3519 Ext.form.Checkbox.superclass.initComponent.call(this);
3523 * Fires when the checkbox is checked or unchecked.
3524 * @param {Ext.form.Checkbox} this This checkbox
3525 * @param {Boolean} checked The new checked value
3532 onResize : function(){
3533 Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
3534 if(!this.boxLabel && !this.fieldLabel){
3535 this.el.alignTo(this.wrap, 'c-c');
3540 initEvents : function(){
3541 Ext.form.Checkbox.superclass.initEvents.call(this);
3544 click: this.onClick,
3545 change: this.onClick
3551 * Overridden and disabled. The editor element does not support standard valid/invalid marking.
3554 markInvalid : Ext.emptyFn,
3557 * Overridden and disabled. The editor element does not support standard valid/invalid marking.
3560 clearInvalid : Ext.emptyFn,
3563 onRender : function(ct, position){
3564 Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
3565 if(this.inputValue !== undefined){
3566 this.el.dom.value = this.inputValue;
3568 this.wrap = this.el.wrap({cls: 'x-form-check-wrap'});
3570 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
3573 this.setValue(true);
3575 this.checked = this.el.dom.checked;
3577 // Need to repaint for IE, otherwise positioning is broken
3579 this.wrap.repaint();
3581 this.resizeEl = this.positionEl = this.wrap;
3585 onDestroy : function(){
3586 Ext.destroy(this.wrap);
3587 Ext.form.Checkbox.superclass.onDestroy.call(this);
3591 initValue : function() {
3592 this.originalValue = this.getValue();
3596 * Returns the checked state of the checkbox.
3597 * @return {Boolean} True if checked, else false
3599 getValue : function(){
3601 return this.el.dom.checked;
3603 return this.checked;
3607 onClick : function(){
3608 if(this.el.dom.checked != this.checked){
3609 this.setValue(this.el.dom.checked);
3614 * Sets the checked state of the checkbox, fires the 'check' event, and calls a
3615 * <code>{@link #handler}</code> (if configured).
3616 * @param {Boolean/String} checked The following values will check the checkbox:
3617 * <code>true, 'true', '1', or 'on'</code>. Any other value will uncheck the checkbox.
3618 * @return {Ext.form.Field} this
3620 setValue : function(v){
3621 var checked = this.checked ;
3622 this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
3624 this.el.dom.checked = this.checked;
3625 this.el.dom.defaultChecked = this.checked;
3627 if(checked != this.checked){
3628 this.fireEvent('check', this, this.checked);
3630 this.handler.call(this.scope || this, this, this.checked);
3636 Ext.reg('checkbox', Ext.form.Checkbox);
3638 * @class Ext.form.CheckboxGroup
\r
3639 * @extends Ext.form.Field
\r
3640 * <p>A grouping container for {@link Ext.form.Checkbox} controls.</p>
\r
3641 * <p>Sample usage:</p>
\r
3643 var myCheckboxGroup = new Ext.form.CheckboxGroup({
\r
3645 xtype: 'checkboxgroup',
\r
3646 fieldLabel: 'Single Column',
\r
3647 itemCls: 'x-check-group-alt',
\r
3648 // Put all controls in a single column with width 100%
\r
3651 {boxLabel: 'Item 1', name: 'cb-col-1'},
\r
3652 {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
\r
3653 {boxLabel: 'Item 3', name: 'cb-col-3'}
\r
3658 * Creates a new CheckboxGroup
\r
3659 * @param {Object} config Configuration options
\r
3660 * @xtype checkboxgroup
\r
3662 Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {
\r
3664 * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects
\r
3665 * to arrange in the group.
\r
3668 * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
\r
3669 * checkbox/radio controls using automatic layout. This config can take several types of values:
\r
3670 * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width
\r
3671 * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>
\r
3672 * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be
\r
3673 * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>
\r
3674 * <li><b>Array</b> : Object<p class="sub-desc">You can also specify an array of column widths, mixing integer
\r
3675 * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will
\r
3676 * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float
\r
3677 * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field
\r
3678 * container you should do so.</p></li></ul>
\r
3682 * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column
\r
3683 * top to bottom before starting on the next column. The number of controls in each column will be automatically
\r
3684 * calculated to keep columns as even as possible. The default value is false, so that controls will be added
\r
3685 * to columns one at a time, completely filling each row left to right before starting on the next row.
\r
3689 * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).
\r
3690 * If no items are selected at validation time, {@link @blankText} will be used as the error text.
\r
3692 allowBlank : true,
\r
3694 * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must
\r
3695 * select at least one item in this group")
\r
3697 blankText : "You must select at least one item in this group",
\r
3700 defaultType : 'checkbox',
\r
3703 groupCls : 'x-form-check-group',
\r
3706 initComponent: function(){
\r
3710 * Fires when the state of a child checkbox changes.
\r
3711 * @param {Ext.form.CheckboxGroup} this
\r
3712 * @param {Array} checked An array containing the checked boxes.
\r
3716 Ext.form.CheckboxGroup.superclass.initComponent.call(this);
\r
3720 onRender : function(ct, position){
\r
3724 cls: this.groupCls,
\r
3728 bufferResize: false // Default this to false, since it doesn't really have a proper ownerCt.
\r
3731 defaultType: this.defaultType,
\r
3740 if(this.items[0].items){
\r
3742 // The container has standard ColumnLayout configs, so pass them in directly
\r
3744 Ext.apply(panelCfg, {
\r
3745 layoutConfig: {columns: this.items.length},
\r
3746 defaults: this.defaults,
\r
3749 for(var i=0, len=this.items.length; i<len; i++){
\r
3750 Ext.applyIf(this.items[i], colCfg);
\r
3755 // The container has field item configs, so we have to generate the column
\r
3756 // panels first then move the items into the columns as needed.
\r
3758 var numCols, cols = [];
\r
3760 if(typeof this.columns == 'string'){ // 'auto' so create a col per item
\r
3761 this.columns = this.items.length;
\r
3763 if(!Ext.isArray(this.columns)){
\r
3765 for(var i=0; i<this.columns; i++){
\r
3766 cs.push((100/this.columns)*.01); // distribute by even %
\r
3768 this.columns = cs;
\r
3771 numCols = this.columns.length;
\r
3773 // Generate the column configs with the correct width setting
\r
3774 for(var i=0; i<numCols; i++){
\r
3775 var cc = Ext.apply({items:[]}, colCfg);
\r
3776 cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = this.columns[i];
\r
3777 if(this.defaults){
\r
3778 cc.defaults = Ext.apply(cc.defaults || {}, this.defaults)
\r
3783 // Distribute the original items into the columns
\r
3784 if(this.vertical){
\r
3785 var rows = Math.ceil(this.items.length / numCols), ri = 0;
\r
3786 for(var i=0, len=this.items.length; i<len; i++){
\r
3787 if(i>0 && i%rows==0){
\r
3790 if(this.items[i].fieldLabel){
\r
3791 this.items[i].hideLabel = false;
\r
3793 cols[ri].items.push(this.items[i]);
\r
3796 for(var i=0, len=this.items.length; i<len; i++){
\r
3797 var ci = i % numCols;
\r
3798 if(this.items[i].fieldLabel){
\r
3799 this.items[i].hideLabel = false;
\r
3801 cols[ci].items.push(this.items[i]);
\r
3805 Ext.apply(panelCfg, {
\r
3806 layoutConfig: {columns: numCols},
\r
3811 this.panel = new Ext.Panel(panelCfg);
\r
3812 this.panel.ownerCt = this;
\r
3813 this.el = this.panel.getEl();
\r
3815 if(this.forId && this.itemCls){
\r
3816 var l = this.el.up(this.itemCls).child('label', true);
\r
3818 l.setAttribute('htmlFor', this.forId);
\r
3822 var fields = this.panel.findBy(function(c){
\r
3823 return c.isFormField;
\r
3826 this.items = new Ext.util.MixedCollection();
\r
3827 this.items.addAll(fields);
\r
3829 Ext.form.CheckboxGroup.superclass.onRender.call(this, ct, position);
\r
3832 initValue : function(){
\r
3834 this.setValue.apply(this, this.buffered ? this.value : [this.value]);
\r
3835 delete this.buffered;
\r
3836 delete this.value;
\r
3840 afterRender : function(){
\r
3841 Ext.form.CheckboxGroup.superclass.afterRender.call(this);
\r
3842 this.eachItem(function(item){
\r
3843 item.on('check', this.fireChecked, this);
\r
3844 item.inGroup = true;
\r
3849 doLayout: function(){
\r
3850 //ugly method required to layout hidden items
\r
3851 if(this.rendered){
\r
3852 this.panel.forceLayout = this.ownerCt.forceLayout;
\r
3853 this.panel.doLayout();
\r
3858 fireChecked: function(){
\r
3860 this.eachItem(function(item){
\r
3865 this.fireEvent('change', this, arr);
\r
3869 validateValue : function(value){
\r
3870 if(!this.allowBlank){
\r
3872 this.eachItem(function(f){
\r
3874 return (blank = false);
\r
3878 this.markInvalid(this.blankText);
\r
3886 isDirty: function(){
\r
3887 //override the behaviour to check sub items.
\r
3888 if (this.disabled || !this.rendered) {
\r
3892 var dirty = false;
\r
3893 this.eachItem(function(item){
\r
3894 if(item.isDirty()){
\r
3903 onDisable : function(){
\r
3904 this.eachItem(function(item){
\r
3910 onEnable : function(){
\r
3911 this.eachItem(function(item){
\r
3917 doLayout: function(){
\r
3918 if(this.rendered){
\r
3919 this.panel.forceLayout = this.ownerCt.forceLayout;
\r
3920 this.panel.doLayout();
\r
3925 onResize : function(w, h){
\r
3926 this.panel.setSize(w, h);
\r
3927 this.panel.doLayout();
\r
3930 // inherit docs from Field
\r
3931 reset : function(){
\r
3932 Ext.form.CheckboxGroup.superclass.reset.call(this);
\r
3933 this.eachItem(function(c){
\r
3941 * {@link Ext.form.Checkbox#setValue Set the value(s)} of an item or items
\r
3942 * in the group. Examples illustrating how this method may be called:
\r
3944 // call with name and value
\r
3945 myCheckboxGroup.setValue('cb-col-1', true);
\r
3946 // call with an array of boolean values
\r
3947 myCheckboxGroup.setValue([true, false, false]);
\r
3948 // call with an object literal specifying item:value pairs
\r
3949 myCheckboxGroup.setValue({
\r
3950 'cb-col-2': false,
\r
3953 // use comma separated string to set items with name to true (checked)
\r
3954 myCheckboxGroup.setValue('cb-col-1,cb-col-3');
\r
3956 * See {@link Ext.form.Checkbox#setValue} for additional information.
\r
3957 * @param {Mixed} id The checkbox to check, or as described by example shown.
\r
3958 * @param {Boolean} value (optional) The value to set the item.
\r
3959 * @return {Ext.form.CheckboxGroup} this
\r
3961 setValue: function(){
\r
3962 if(this.rendered){
\r
3963 this.onSetValue.apply(this, arguments);
\r
3965 this.buffered = true;
\r
3966 this.value = arguments;
\r
3971 onSetValue: function(id, value){
\r
3972 if(arguments.length == 1){
\r
3973 if(Ext.isArray(id)){
\r
3974 // an array of boolean values
\r
3975 Ext.each(id, function(val, idx){
\r
3976 var item = this.items.itemAt(idx);
\r
3978 item.setValue(val);
\r
3981 }else if(Ext.isObject(id)){
\r
3982 // set of name/value pairs
\r
3984 var f = this.getBox(i);
\r
3986 f.setValue(id[i]);
\r
3990 this.setValueForItem(id);
\r
3993 var f = this.getBox(id);
\r
3995 f.setValue(value);
\r
4001 onDestroy: function(){
\r
4002 Ext.destroy(this.panel);
\r
4003 Ext.form.CheckboxGroup.superclass.onDestroy.call(this);
\r
4007 setValueForItem : function(val){
\r
4008 val = String(val).split(',');
\r
4009 this.eachItem(function(item){
\r
4010 if(val.indexOf(item.inputValue)> -1){
\r
4011 item.setValue(true);
\r
4017 getBox : function(id){
\r
4019 this.eachItem(function(f){
\r
4020 if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
\r
4029 * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
\r
4030 * @return {Array} An array of the selected checkboxes.
\r
4032 getValue : function(){
\r
4034 this.eachItem(function(item){
\r
4043 eachItem: function(fn){
\r
4044 if(this.items && this.items.each){
\r
4045 this.items.each(fn, this);
\r
4050 * @cfg {String} name
\r
4055 * @method getRawValue
\r
4058 getRawValue : Ext.emptyFn,
\r
4061 * @method setRawValue
\r
4064 setRawValue : Ext.emptyFn
\r
4068 Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
\r
4070 * @class Ext.form.Radio
4071 * @extends Ext.form.Checkbox
4072 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
4073 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
4075 * Creates a new Radio
4076 * @param {Object} config Configuration options
4079 Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
4083 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
4086 markInvalid : Ext.emptyFn,
4088 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
4091 clearInvalid : Ext.emptyFn,
4094 * If this radio is part of a group, it will return the selected value
4097 getGroupValue : function(){
4098 var p = this.el.up('form') || Ext.getBody();
4099 var c = p.child('input[name='+this.el.dom.name+']:checked', true);
4100 return c ? c.value : null;
4104 onClick : function(){
4105 if(this.el.dom.checked != this.checked){
4106 var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
4107 els.each(function(el){
4108 if(el.dom.id == this.id){
4109 this.setValue(true);
4111 Ext.getCmp(el.dom.id).setValue(false);
4118 * Sets either the checked/unchecked status of this Radio, or, if a string value
4119 * is passed, checks a sibling Radio of the same name whose value is the value specified.
4120 * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
4121 * @return {Ext.form.Field} this
4123 setValue : function(v){
4124 if (typeof v == 'boolean') {
4125 Ext.form.Radio.superclass.setValue.call(this, v);
4127 var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
4129 Ext.getCmp(r.id).setValue(true);
4136 getCheckEl: function(){
4138 return this.el.up('.x-form-radio-group')
4140 return this.el.up('form') || Ext.getBody();
4143 Ext.reg('radio', Ext.form.Radio);
4145 * @class Ext.form.RadioGroup
\r
4146 * @extends Ext.form.CheckboxGroup
\r
4147 * A grouping container for {@link Ext.form.Radio} controls.
\r
4149 * Creates a new RadioGroup
\r
4150 * @param {Object} config Configuration options
\r
4151 * @xtype radiogroup
\r
4153 Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {
\r
4155 * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
\r
4156 * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
\r
4157 * be used as the error text.
\r
4159 allowBlank : true,
\r
4161 * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
\r
4162 * (defaults to 'You must select one item in this group')
\r
4164 blankText : 'You must select one item in this group',
\r
4167 defaultType : 'radio',
\r
4170 groupCls : 'x-form-radio-group',
\r
4174 * Fires when the state of a child radio changes.
\r
4175 * @param {Ext.form.RadioGroup} this
\r
4176 * @param {Ext.form.Radio} checked The checked radio
\r
4180 * Gets the selected {@link Ext.form.Radio} in the group, if it exists.
\r
4181 * @return {Ext.form.Radio} The selected radio.
\r
4183 getValue : function(){
\r
4185 this.eachItem(function(item){
\r
4195 * Sets the checked radio in the group.
\r
4196 * @param {String/Ext.form.Radio} id The radio to check.
\r
4197 * @param {Boolean} value The value to set the radio.
\r
4198 * @return {Ext.form.RadioGroup} this
\r
4200 onSetValue : function(id, value){
\r
4201 if(arguments.length > 1){
\r
4202 var f = this.getBox(id);
\r
4204 f.setValue(value);
\r
4206 this.eachItem(function(item){
\r
4208 item.setValue(false);
\r
4214 this.setValueForItem(id);
\r
4218 setValueForItem : function(val){
\r
4219 val = String(val).split(',')[0];
\r
4220 this.eachItem(function(item){
\r
4221 item.setValue(val == item.inputValue);
\r
4226 fireChecked : function(){
\r
4227 if(!this.checkTask){
\r
4228 this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);
\r
4230 this.checkTask.delay(10);
\r
4234 bufferChecked : function(){
\r
4236 this.eachItem(function(item){
\r
4242 this.fireEvent('change', this, out);
\r
4245 onDestroy : function(){
\r
4246 if(this.checkTask){
\r
4247 this.checkTask.cancel();
\r
4248 this.checkTask = null;
\r
4250 Ext.form.RadioGroup.superclass.onDestroy.call(this);
\r
4255 Ext.reg('radiogroup', Ext.form.RadioGroup);
\r
4257 * @class Ext.form.Hidden
\r
4258 * @extends Ext.form.Field
\r
4259 * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.
\r
4261 * Create a new Hidden field.
\r
4262 * @param {Object} config Configuration options
\r
4265 Ext.form.Hidden = Ext.extend(Ext.form.Field, {
\r
4267 inputType : 'hidden',
\r
4270 onRender : function(){
\r
4271 Ext.form.Hidden.superclass.onRender.apply(this, arguments);
\r
4275 initEvents : function(){
\r
4276 this.originalValue = this.getValue();
\r
4279 // These are all private overrides
\r
4280 setSize : Ext.emptyFn,
\r
4281 setWidth : Ext.emptyFn,
\r
4282 setHeight : Ext.emptyFn,
\r
4283 setPosition : Ext.emptyFn,
\r
4284 setPagePosition : Ext.emptyFn,
\r
4285 markInvalid : Ext.emptyFn,
\r
4286 clearInvalid : Ext.emptyFn
\r
4288 Ext.reg('hidden', Ext.form.Hidden);/**
4289 * @class Ext.form.BasicForm
4290 * @extends Ext.util.Observable
4291 * <p>Encapsulates the DOM <form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides
4292 * input field management, validation, submission, and form loading services.</p>
4293 * <p>By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}.
4294 * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.</p>
4295 * <p><b><u>File Uploads</u></b></p>
4296 * <p>{@link #fileUpload File uploads} are not performed using Ajax submission, that
4297 * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
4298 * manner with the DOM <tt><form></tt> element temporarily modified to have its
4299 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
4300 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
4301 * but removed after the return data has been gathered.</p>
4302 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
4303 * server is using JSON to send the return object, then the
4304 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
4305 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
4306 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
4307 * "<" as "&lt;", "&" as "&amp;" etc.</p>
4308 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
4309 * is created containing a <tt>responseText</tt> property in order to conform to the
4310 * requirements of event handlers and callbacks.</p>
4311 * <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>
4312 * and some server technologies (notably JEE) may require some custom processing in order to
4313 * retrieve parameter names and parameter values from the packet content.</p>
4315 * @param {Mixed} el The form element or its id
4316 * @param {Object} config Configuration options
4318 Ext.form.BasicForm = function(el, config){
4319 Ext.apply(this, config);
4320 if(Ext.isString(this.paramOrder)){
4321 this.paramOrder = this.paramOrder.split(/[\s,|]/);
4325 * A {@link Ext.util.MixedCollection MixedCollection) containing all the Ext.form.Fields in this form.
4326 * @type MixedCollection
4328 this.items = new Ext.util.MixedCollection(false, function(o){
4329 return o.getItemId();
4333 * @event beforeaction
4334 * Fires before any action is performed. Return false to cancel the action.
4335 * @param {Form} this
4336 * @param {Action} action The {@link Ext.form.Action} to be performed
4340 * @event actionfailed
4341 * Fires when an action fails.
4342 * @param {Form} this
4343 * @param {Action} action The {@link Ext.form.Action} that failed
4347 * @event actioncomplete
4348 * Fires when an action is completed.
4349 * @param {Form} this
4350 * @param {Action} action The {@link Ext.form.Action} that completed
4358 Ext.form.BasicForm.superclass.constructor.call(this);
4361 Ext.extend(Ext.form.BasicForm, Ext.util.Observable, {
4363 * @cfg {String} method
4364 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4367 * @cfg {DataReader} reader
4368 * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read
4369 * data when executing 'load' actions. This is optional as there is built-in
4370 * support for processing JSON. For additional information on using an XMLReader
4371 * see the example provided in examples/form/xml-form.html.
4374 * @cfg {DataReader} errorReader
4375 * <p>An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to
4376 * read field error messages returned from 'submit' actions. This is optional
4377 * as there is built-in support for processing JSON.</p>
4378 * <p>The Records which provide messages for the invalid Fields must use the
4379 * Field name (or id) as the Record ID, and must contain a field called 'msg'
4380 * which contains the error message.</p>
4381 * <p>The errorReader does not have to be a full-blown implementation of a
4382 * DataReader. It simply needs to implement a <tt>read(xhr)</tt> function
4383 * which returns an Array of Records in an object with the following
4384 * structure:</p><pre><code>
4386 records: recordArray
4392 * The URL to use for form actions if one isn't supplied in the
4393 * <code>{@link #doAction doAction} options</code>.
4396 * @cfg {Boolean} fileUpload
4397 * Set to true if this form is a file upload.
4398 * <p>File uploads are not performed using normal 'Ajax' techniques, that is they are <b>not</b>
4399 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
4400 * DOM <tt><form></tt> element temporarily modified to have its
4401 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
4402 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
4403 * but removed after the return data has been gathered.</p>
4404 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
4405 * server is using JSON to send the return object, then the
4406 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
4407 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
4408 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
4409 * "<" as "&lt;", "&" as "&amp;" etc.</p>
4410 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
4411 * is created containing a <tt>responseText</tt> property in order to conform to the
4412 * requirements of event handlers and callbacks.</p>
4413 * <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>
4414 * and some server technologies (notably JEE) may require some custom processing in order to
4415 * retrieve parameter names and parameter values from the packet content.</p>
4418 * @cfg {Object} baseParams
4419 * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
4420 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
4423 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4428 * @cfg {Object} api (Optional) If specified load and submit actions will be handled
4429 * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}.
4430 * Methods which have been imported by Ext.Direct can be specified here to load and submit
4432 * Such as the following:<pre><code>
4434 load: App.ss.MyProfile.load,
4435 submit: App.ss.MyProfile.submit
4438 * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
4439 * to customize how the load method is invoked.
4440 * Submit actions will always use a standard form submit. The formHandler configuration must
4441 * be set on the associated server-side method which has been imported by Ext.Direct</p>
4445 * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
4446 * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
4447 * <code>load</code> configuration.</p>
4448 * <br><p>Specify the params in the order in which they must be executed on the
4449 * server-side as either (1) an Array of String values, or (2) a String of params
4450 * delimited by either whitespace, comma, or pipe. For example,
4451 * any of the following would be acceptable:</p><pre><code>
4452 paramOrder: ['param1','param2','param3']
4453 paramOrder: 'param1 param2 param3'
4454 paramOrder: 'param1,param2,param3'
4455 paramOrder: 'param1|param2|param'
4458 paramOrder: undefined,
4461 * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
4462 * <code>load</code> configuration. Send parameters as a collection of named
4463 * arguments (defaults to <tt>false</tt>). Providing a
4464 * <tt>{@link #paramOrder}</tt> nullifies this configuration.
4466 paramsAsHash: false,
4469 * @cfg {String} waitTitle
4470 * The default title to show for the waiting message box (defaults to <tt>'Please Wait...'</tt>)
4472 waitTitle: 'Please Wait...',
4475 activeAction : null,
4478 * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
4479 * or {@link #setValues}() data instead of when the form was first created. Defaults to <tt>false</tt>.
4481 trackResetOnLoad : false,
4484 * @cfg {Boolean} standardSubmit
4485 * <p>If set to <tt>true</tt>, standard HTML form submits are used instead
4486 * of XHR (Ajax) style form submissions. Defaults to <tt>false</tt>.</p>
4487 * <br><p><b>Note:</b> When using <code>standardSubmit</code>, the
4488 * <code>options</code> to <code>{@link #submit}</code> are ignored because
4489 * Ext's Ajax infrastracture is bypassed. To pass extra parameters (e.g.
4490 * <code>baseParams</code> and <code>params</code>), utilize hidden fields
4491 * to submit extra data, for example:</p>
4494 standardSubmit: true,
4498 {@link url}: 'myProcess.php',
4505 handler: function(){
4506 var fp = this.ownerCt.ownerCt,
4507 form = fp.getForm();
4508 if (form.isValid()) {
4509 // check if there are baseParams and if
4510 // hiddent items have been added already
4511 if (fp.baseParams && !fp.paramsAdded) {
4512 // add hidden items for all baseParams
4513 for (i in fp.baseParams) {
4517 value: fp.baseParams[i]
4521 // set a custom flag to prevent re-adding
4522 fp.paramsAdded = true;
4524 form.{@link #submit}();
4532 * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
4533 * element by passing it or its id or mask the form itself by passing in true.
4535 * @property waitMsgTarget
4539 initEl : function(el){
4540 this.el = Ext.get(el);
4541 this.id = this.el.id || Ext.id();
4542 if(!this.standardSubmit){
4543 this.el.on('submit', this.onSubmit, this);
4545 this.el.addClass('x-form');
4549 * Get the HTML form Element
4550 * @return Ext.Element
4557 onSubmit : function(e){
4562 destroy: function() {
4563 this.items.each(function(f){
4567 this.el.removeAllListeners();
4570 this.purgeListeners();
4574 * Returns true if client-side validation on the form is successful.
4577 isValid : function(){
4579 this.items.each(function(f){
4588 * <p>Returns true if any fields in this form have changed from their original values.</p>
4589 * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
4590 * Fields' <i>original values</i> are updated when the values are loaded by {@link #setValues}
4591 * or {@link #loadRecord}.</p>
4594 isDirty : function(){
4596 this.items.each(function(f){
4606 * Performs a predefined action ({@link Ext.form.Action.Submit} or
4607 * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action}
4608 * to perform application-specific processing.
4609 * @param {String/Object} actionName The name of the predefined action type,
4610 * or instance of {@link Ext.form.Action} to perform.
4611 * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}.
4612 * All of the config options listed below are supported by both the
4613 * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
4614 * actions unless otherwise noted (custom actions could also accept
4615 * other config options):<ul>
4617 * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
4618 * to the form's {@link #url}.)</div></li>
4620 * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
4621 * to the form's method, or POST if not defined)</div></li>
4623 * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
4624 * (defaults to the form's baseParams, or none if not defined)</p>
4625 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
4627 * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action
4628 * (defaults to the form's default headers)</div></li>
4630 * <li><b>success</b> : Function<div class="sub-desc">The callback that will
4631 * be invoked after a successful response (see top of
4632 * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
4633 * for a description of what constitutes a successful response).
4634 * The function is passed the following parameters:<ul>
4635 * <li><tt>form</tt> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
4636 * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
4637 * <div class="sub-desc">The action object contains these properties of interest:<ul>
4638 * <li><tt>{@link Ext.form.Action#response response}</tt></li>
4639 * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
4640 * <li><tt>{@link Ext.form.Action#type type}</tt></li>
4641 * </ul></div></li></ul></div></li>
4643 * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
4644 * failed transaction attempt. The function is passed the following parameters:<ul>
4645 * <li><tt>form</tt> : The {@link Ext.form.BasicForm} that requested the action.</li>
4646 * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
4647 * <div class="sub-desc">The action object contains these properties of interest:<ul>
4648 * <li><tt>{@link Ext.form.Action#failureType failureType}</tt></li>
4649 * <li><tt>{@link Ext.form.Action#response response}</tt></li>
4650 * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
4651 * <li><tt>{@link Ext.form.Action#type type}</tt></li>
4652 * </ul></div></li></ul></div></li>
4654 * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
4655 * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
4657 * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
4658 * Determines whether a Form's fields are validated in a final call to
4659 * {@link Ext.form.BasicForm#isValid isValid} prior to submission. Set to <tt>false</tt>
4660 * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
4662 * @return {BasicForm} this
4664 doAction : function(action, options){
4665 if(Ext.isString(action)){
4666 action = new Ext.form.Action.ACTION_TYPES[action](this, options);
4668 if(this.fireEvent('beforeaction', this, action) !== false){
4669 this.beforeAction(action);
4670 action.run.defer(100, action);
4676 * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Submit submit action}.
4677 * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
4678 * <p><b>Note:</b> this is ignored when using the {@link #standardSubmit} option.</p>
4679 * <p>The following code:</p><pre><code>
4680 myFormPanel.getForm().submit({
4681 clientValidation: true,
4682 url: 'updateConsignment.php',
4684 newStatus: 'delivered'
4686 success: function(form, action) {
4687 Ext.Msg.alert('Success', action.result.msg);
4689 failure: function(form, action) {
4690 switch (action.failureType) {
4691 case Ext.form.Action.CLIENT_INVALID:
4692 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
4694 case Ext.form.Action.CONNECT_FAILURE:
4695 Ext.Msg.alert('Failure', 'Ajax communication failed');
4697 case Ext.form.Action.SERVER_INVALID:
4698 Ext.Msg.alert('Failure', action.result.msg);
4703 * would process the following server response for a successful submission:<pre><code>
4705 "success":true, // note this is Boolean, not string
4706 "msg":"Consignment updated"
4709 * and the following server response for a failed submission:<pre><code>
4711 "success":false, // note this is Boolean, not string
4712 "msg":"You do not have permission to perform this operation"
4715 * @return {BasicForm} this
4717 submit : function(options){
4718 if(this.standardSubmit){
4719 var v = this.isValid();
4721 var el = this.el.dom;
4722 if(this.url && Ext.isEmpty(el.action)){
4723 el.action = this.url;
4729 var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
4730 this.doAction(submitAction, options);
4735 * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
4736 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
4737 * @return {BasicForm} this
4739 load : function(options){
4740 var loadAction = String.format('{0}load', this.api ? 'direct' : '');
4741 this.doAction(loadAction, options);
4746 * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
4747 * @param {Record} record The record to edit
4748 * @return {BasicForm} this
4750 updateRecord : function(record){
4752 var fs = record.fields;
4753 fs.each(function(f){
4754 var field = this.findField(f.name);
4756 record.set(f.name, field.getValue());
4764 * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
4765 * {@link Ext.data.Record#data record data}.
4766 * See also {@link #trackResetOnLoad}.
4767 * @param {Record} record The record to load
4768 * @return {BasicForm} this
4770 loadRecord : function(record){
4771 this.setValues(record.data);
4776 beforeAction : function(action){
4777 var o = action.options;
4779 if(this.waitMsgTarget === true){
4780 this.el.mask(o.waitMsg, 'x-mask-loading');
4781 }else if(this.waitMsgTarget){
4782 this.waitMsgTarget = Ext.get(this.waitMsgTarget);
4783 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
4785 Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
4791 afterAction : function(action, success){
4792 this.activeAction = null;
4793 var o = action.options;
4795 if(this.waitMsgTarget === true){
4797 }else if(this.waitMsgTarget){
4798 this.waitMsgTarget.unmask();
4800 Ext.MessageBox.updateProgress(1);
4801 Ext.MessageBox.hide();
4808 Ext.callback(o.success, o.scope, [this, action]);
4809 this.fireEvent('actioncomplete', this, action);
4811 Ext.callback(o.failure, o.scope, [this, action]);
4812 this.fireEvent('actionfailed', this, action);
4817 * Find a {@link Ext.form.Field} in this form.
4818 * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
4819 * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
4822 findField : function(id){
4823 var field = this.items.get(id);
4824 if(!Ext.isObject(field)){
4825 this.items.each(function(f){
4826 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4832 return field || null;
4837 * Mark fields in this form invalid in bulk.
4838 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4839 * @return {BasicForm} this
4841 markInvalid : function(errors){
4842 if(Ext.isArray(errors)){
4843 for(var i = 0, len = errors.length; i < len; i++){
4844 var fieldError = errors[i];
4845 var f = this.findField(fieldError.id);
4847 f.markInvalid(fieldError.msg);
4853 if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
4854 field.markInvalid(errors[id]);
4862 * Set values for fields in this form in bulk.
4863 * @param {Array/Object} values Either an array in the form:<pre><code>
4864 [{id:'clientName', value:'Fred. Olsen Lines'},
4865 {id:'portOfLoading', value:'FXT'},
4866 {id:'portOfDischarge', value:'OSL'} ]</code></pre>
4867 * or an object hash of the form:<pre><code>
4869 clientName: 'Fred. Olsen Lines',
4870 portOfLoading: 'FXT',
4871 portOfDischarge: 'OSL'
4873 * @return {BasicForm} this
4875 setValues : function(values){
4876 if(Ext.isArray(values)){ // array of objects
4877 for(var i = 0, len = values.length; i < len; i++){
4879 var f = this.findField(v.id);
4881 f.setValue(v.value);
4882 if(this.trackResetOnLoad){
4883 f.originalValue = f.getValue();
4887 }else{ // object hash
4890 if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
4891 field.setValue(values[id]);
4892 if(this.trackResetOnLoad){
4893 field.originalValue = field.getValue();
4902 * <p>Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit.
4903 * If multiple fields exist with the same name they are returned as an array.</p>
4904 * <p><b>Note:</b> The values are collected from all enabled HTML input elements within the form, <u>not</u> from
4905 * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the
4906 * value can potentially be the emptyText of a field.</p>
4907 * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object)
4908 * @return {String/Object}
4910 getValues : function(asString){
4911 var fs = Ext.lib.Ajax.serializeForm(this.el.dom);
4912 if(asString === true){
4915 return Ext.urlDecode(fs);
4918 getFieldValues : function(){
4920 this.items.each(function(f){
4921 o[f.getName()] = f.getValue();
4927 * Clears all invalid messages in this form.
4928 * @return {BasicForm} this
4930 clearInvalid : function(){
4931 this.items.each(function(f){
4939 * @return {BasicForm} this
4942 this.items.each(function(f){
4949 * Add Ext.form Components to this form's Collection. This does not result in rendering of
4950 * the passed Component, it just enables the form to validate Fields, and distribute values to
4952 * <p><b>You will not usually call this function. In order to be rendered, a Field must be added
4953 * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}.
4954 * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's
4955 * collection.</b></p>
4956 * @param {Field} field1
4957 * @param {Field} field2 (optional)
4958 * @param {Field} etc (optional)
4959 * @return {BasicForm} this
4962 this.items.addAll(Array.prototype.slice.call(arguments, 0));
4968 * Removes a field from the items collection (does NOT remove its markup).
4969 * @param {Field} field
4970 * @return {BasicForm} this
4972 remove : function(field){
4973 this.items.remove(field);
4978 * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm,
4979 * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id.
4980 * @return {BasicForm} this
4982 render : function(){
4983 this.items.each(function(f){
4984 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
4985 f.applyToMarkup(f.id);
4992 * Calls {@link Ext#apply} for all fields in this form with the passed object.
4993 * @param {Object} values
4994 * @return {BasicForm} this
4996 applyToFields : function(o){
4997 this.items.each(function(f){
5004 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
5005 * @param {Object} values
5006 * @return {BasicForm} this
5008 applyIfToFields : function(o){
5009 this.items.each(function(f){
5015 callFieldMethod : function(fnName, args){
5017 this.items.each(function(f){
5018 if(Ext.isFunction(f[fnName])){
5019 f[fnName].apply(f, args);
5027 Ext.BasicForm = Ext.form.BasicForm;/**
5028 * @class Ext.form.FormPanel
5029 * @extends Ext.Panel
5030 * <p>Standard form container.</p>
5032 * <p><b><u>Layout</u></b></p>
5033 * <p>By default, FormPanel is configured with <tt>layout:'form'</tt> to use an {@link Ext.layout.FormLayout}
5034 * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers
5035 * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the
5036 * {@link Ext.layout.FormLayout} layout manager.</p>
5038 * <p><b><u>BasicForm</u></b></p>
5039 * <p>Although <b>not listed</b> as configuration options of FormPanel, the FormPanel class accepts all
5040 * of the config options required to configure its internal {@link Ext.form.BasicForm} for:
5041 * <div class="mdetail-params"><ul>
5042 * <li>{@link Ext.form.BasicForm#fileUpload file uploads}</li>
5043 * <li>functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form</li>
5046 * <p><b>Note</b>: If subclassing FormPanel, any configuration options for the BasicForm must be applied to
5047 * the <tt><b>initialConfig</b></tt> property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm}
5048 * configuration settings to <b><tt>this</tt></b> will <b>not</b> affect the BasicForm's configuration.</p>
5050 * <p><b><u>Form Validation</u></b></p>
5051 * <p>For information on form validation see the following:</p>
5052 * <div class="mdetail-params"><ul>
5053 * <li>{@link Ext.form.TextField}</li>
5054 * <li>{@link Ext.form.VTypes}</li>
5055 * <li>{@link Ext.form.BasicForm#doAction BasicForm.doAction <b>clientValidation</b> notes}</li>
5056 * <li><tt>{@link Ext.form.FormPanel#monitorValid monitorValid}</tt></li>
5059 * <p><b><u>Form Submission</u></b></p>
5060 * <p>By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser
5061 * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the
5062 * <tt><b>{@link Ext.form.BasicForm#standardSubmit standardSubmit}</b></tt> option.</p>
5065 * @param {Object} config Configuration options
5068 Ext.FormPanel = Ext.extend(Ext.Panel, {
5070 * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).
5073 * @cfg {Boolean} hideLabels
5074 * <p><tt>true</tt> to hide field labels by default (sets <tt>display:none</tt>). Defaults to
5075 * <tt>false</tt>.</p>
5076 * <p>Also see {@link Ext.Component}.<tt>{@link Ext.Component#hideLabel hideLabel}</tt>.
5079 * @cfg {Number} labelPad
5080 * The default padding in pixels for field labels (defaults to <tt>5</tt>). <tt>labelPad</tt> only
5081 * applies if <tt>{@link #labelWidth}</tt> is also specified, otherwise it will be ignored.
5084 * @cfg {String} labelSeparator
5085 * See {@link Ext.Component}.<tt>{@link Ext.Component#labelSeparator labelSeparator}</tt>
5088 * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
5089 * and can be overridden on any child container (e.g., a fieldset can specify a different <tt>labelWidth</tt>
5090 * for its fields) (defaults to <tt>100</tt>).
5093 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
5096 * @cfg {Array} buttons
5097 * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.<br>
5098 * <p>Buttons in the footer of a FormPanel may be configured with the option <tt>formBind: true</tt>. This causes
5099 * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on
5100 * the form's valid/invalid state.</p>
5105 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to <tt>75</tt>).
5107 minButtonWidth : 75,
5110 * @cfg {String} labelAlign The label alignment value used for the <tt>text-align</tt> specification
5111 * for the <b>container</b>. Valid values are <tt>"left</tt>", <tt>"top"</tt> or <tt>"right"</tt>
5112 * (defaults to <tt>"left"</tt>). This property cascades to child <b>containers</b> and can be
5113 * overridden on any child <b>container</b> (e.g., a fieldset can specify a different <tt>labelAlign</tt>
5116 labelAlign : 'left',
5119 * @cfg {Boolean} monitorValid If <tt>true</tt>, the form monitors its valid state <b>client-side</b> and
5120 * regularly fires the {@link #clientvalidation} event passing that state.<br>
5121 * <p>When monitoring valid state, the FormPanel enables/disables any of its configured
5122 * {@link #buttons} which have been configured with <code>formBind: true</code> depending
5123 * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to <tt>false</tt></p>
5125 monitorValid : false,
5128 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
5133 * @cfg {String} layout Defaults to <tt>'form'</tt>. Normally this configuration property should not be altered.
5134 * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}.
5139 initComponent : function(){
5140 this.form = this.createForm();
5141 Ext.FormPanel.superclass.initComponent.call(this);
5145 cls: this.baseCls + '-body',
5146 method : this.method || 'POST',
5147 id : this.formId || Ext.id()
5149 if(this.fileUpload) {
5150 this.bodyCfg.enctype = 'multipart/form-data';
5156 * @event clientvalidation
5157 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5158 * @param {Ext.form.FormPanel} this
5159 * @param {Boolean} valid true if the form has passed client-side validation
5164 this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']);
5168 createForm : function(){
5169 var config = Ext.applyIf({listeners: {}}, this.initialConfig);
5170 return new Ext.form.BasicForm(null, config);
5174 initFields : function(){
5176 var formPanel = this;
5177 var fn = function(c){
5178 if(formPanel.isField(c)){
5180 }else if(c.findBy && c != formPanel){
5181 formPanel.applySettings(c);
5182 //each check required for check/radio groups.
5183 if(c.items && c.items.each){
5184 c.items.each(fn, this);
5188 this.items.each(fn, this);
5192 applySettings: function(c){
5195 labelAlign: ct.labelAlign,
5196 labelWidth: ct.labelWidth,
5202 getLayoutTarget : function(){
5203 return this.form.el;
5207 * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains.
5208 * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains.
5210 getForm : function(){
5215 onRender : function(ct, position){
5217 Ext.FormPanel.superclass.onRender.call(this, ct, position);
5218 this.form.initEl(this.body);
5222 beforeDestroy : function(){
5223 this.stopMonitoring();
5224 Ext.FormPanel.superclass.beforeDestroy.call(this);
5226 * Clear the items here to prevent them being destroyed again.
5227 * Don't move this behaviour to BasicForm because it can be used
5230 this.form.items.clear();
5231 Ext.destroy(this.form);
5234 // Determine if a Component is usable as a form Field.
5235 isField : function(c) {
5236 return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
5240 initEvents : function(){
5241 Ext.FormPanel.superclass.initEvents.call(this);
5242 // Listeners are required here to catch bubbling events from children.
5245 add: this.onAddEvent,
5246 remove: this.onRemoveEvent
5248 if(this.monitorValid){ // initialize after render
5249 this.startMonitoring();
5255 Ext.FormPanel.superclass.onAdd.call(this, c);
5260 onAddEvent: function(ct, c){
5267 processAdd : function(c){
5268 // If a single form Field, add it
5269 if(this.isField(c)){
5271 // If a Container, add any Fields it might contain
5273 this.applySettings(c);
5274 this.form.add.apply(this.form, c.findBy(this.isField));
5279 onRemove: function(c){
5280 Ext.FormPanel.superclass.onRemove.call(this, c);
5281 this.processRemove(c);
5284 onRemoveEvent: function(ct, c){
5286 this.processRemove(c);
5291 processRemove : function(c){
5292 // If a single form Field, remove it
5293 if(this.isField(c)){
5294 this.form.remove(c);
5295 // If a Container, remove any Fields it might contain
5297 Ext.each(c.findBy(this.isField), this.form.remove, this.form);
5302 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
5303 * option "monitorValid"
5305 startMonitoring : function(){
5306 if(!this.validTask){
5307 this.validTask = new Ext.util.TaskRunner();
5308 this.validTask.start({
5309 run : this.bindHandler,
5310 interval : this.monitorPoll || 200,
5317 * Stops monitoring of the valid state of this form
5319 stopMonitoring : function(){
5321 this.validTask.stopAll();
5322 this.validTask = null;
5327 * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.
5328 * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)
5331 this.form.load.apply(this.form, arguments);
5335 onDisable : function(){
5336 Ext.FormPanel.superclass.onDisable.call(this);
5338 this.form.items.each(function(){
5345 onEnable : function(){
5346 Ext.FormPanel.superclass.onEnable.call(this);
5348 this.form.items.each(function(){
5355 bindHandler : function(){
5357 this.form.items.each(function(f){
5358 if(!f.isValid(true)){
5364 var fitems = this.fbar.items.items;
5365 for(var i = 0, len = fitems.length; i < len; i++){
5366 var btn = fitems[i];
5367 if(btn.formBind === true && btn.disabled === valid){
5368 btn.setDisabled(!valid);
5372 this.fireEvent('clientvalidation', this, valid);
5375 Ext.reg('form', Ext.FormPanel);
5377 Ext.form.FormPanel = Ext.FormPanel;
5380 * @class Ext.form.FieldSet
\r
5381 * @extends Ext.Panel
\r
5382 * Standard container used for grouping items within a {@link Ext.form.FormPanel form}.
\r
5384 var form = new Ext.FormPanel({
\r
5385 title: 'Simple Form with FieldSets',
\r
5386 labelWidth: 75, // label settings here cascade unless overridden
\r
5387 url: 'save-form.php',
\r
5389 bodyStyle:'padding:5px 5px 0',
\r
5391 renderTo: document.body,
\r
5392 layout:'column', // arrange items in columns
\r
5393 defaults: { // defaults applied to items
\r
5396 bodyStyle: 'padding:4px'
\r
5399 // Fieldset in Column 1
\r
5402 title: 'Fieldset 1',
\r
5403 collapsible: true,
\r
5406 anchor: '-20' // leave room for error icon
\r
5408 defaultType: 'textfield',
\r
5410 fieldLabel: 'Field 1'
\r
5412 fieldLabel: 'Field 2'
\r
5414 fieldLabel: 'Field 3'
\r
5418 // Fieldset in Column 2 - Panel inside
\r
5420 title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header
\r
5423 checkboxToggle: true,
\r
5424 collapsed: true, // fieldset initially collapsed
\r
5429 title: 'Panel inside a fieldset',
\r
5437 * @param {Object} config Configuration options
\r
5440 Ext.form.FieldSet = Ext.extend(Ext.Panel, {
\r
5442 * @cfg {Mixed} checkboxToggle <tt>true</tt> to render a checkbox into the fieldset frame just
\r
5443 * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults
\r
5444 * to <tt>false</tt>).
\r
5445 * <p>A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox.
\r
5446 * If <tt>true</tt> is specified, the default DomHelper config object used to create the element
\r
5447 * is:</p><pre><code>
\r
5448 * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}
\r
5452 * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if <tt>{@link #checkboxToggle} = true</tt>
\r
5453 * (defaults to <tt>'[checkbox id]-checkbox'</tt>).
\r
5456 * @cfg {Boolean} collapsible
\r
5457 * <tt>true</tt> to make the fieldset collapsible and have the expand/collapse toggle button automatically
\r
5458 * rendered into the legend element, <tt>false</tt> to keep the fieldset statically sized with no collapse
\r
5459 * button (defaults to <tt>false</tt>). Another option is to configure <tt>{@link #checkboxToggle}</tt>.
\r
5462 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
\r
5465 * @cfg {String} itemCls A css class to apply to the <tt>x-form-item</tt> of fields (see
\r
5466 * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).
\r
5467 * This property cascades to child containers.
\r
5470 * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to <tt>'x-fieldset'</tt>).
\r
5472 baseCls : 'x-fieldset',
\r
5474 * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to <tt>'form'</tt>).
\r
5478 * @cfg {Boolean} animCollapse
\r
5479 * <tt>true</tt> to animate the transition when the panel is collapsed, <tt>false</tt> to skip the
\r
5480 * animation (defaults to <tt>false</tt>).
\r
5482 animCollapse : false,
\r
5485 onRender : function(ct, position){
\r
5487 this.el = document.createElement('fieldset');
\r
5488 this.el.id = this.id;
\r
5489 if (this.title || this.header || this.checkboxToggle) {
\r
5490 this.el.appendChild(document.createElement('legend')).className = 'x-fieldset-header';
\r
5494 Ext.form.FieldSet.superclass.onRender.call(this, ct, position);
\r
5496 if(this.checkboxToggle){
\r
5497 var o = typeof this.checkboxToggle == 'object' ?
\r
5498 this.checkboxToggle :
\r
5499 {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};
\r
5500 this.checkbox = this.header.insertFirst(o);
\r
5501 this.checkbox.dom.checked = !this.collapsed;
\r
5502 this.mon(this.checkbox, 'click', this.onCheckClick, this);
\r
5507 onCollapse : function(doAnim, animArg){
\r
5508 if(this.checkbox){
\r
5509 this.checkbox.dom.checked = false;
\r
5511 Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);
\r
5516 onExpand : function(doAnim, animArg){
\r
5517 if(this.checkbox){
\r
5518 this.checkbox.dom.checked = true;
\r
5520 Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);
\r
5524 * This function is called by the fieldset's checkbox when it is toggled (only applies when
\r
5525 * checkboxToggle = true). This method should never be called externally, but can be
\r
5526 * overridden to provide custom behavior when the checkbox is toggled if needed.
\r
5528 onCheckClick : function(){
\r
5529 this[this.checkbox.dom.checked ? 'expand' : 'collapse']();
\r
5533 * @cfg {String/Number} activeItem
\r
5537 * @cfg {Mixed} applyTo
\r
5541 * @cfg {Boolean} bodyBorder
\r
5545 * @cfg {Boolean} border
\r
5549 * @cfg {Boolean/Number} bufferResize
\r
5553 * @cfg {Boolean} collapseFirst
\r
5557 * @cfg {String} defaultType
\r
5561 * @cfg {String} disabledClass
\r
5565 * @cfg {String} elements
\r
5569 * @cfg {Boolean} floating
\r
5573 * @cfg {Boolean} footer
\r
5577 * @cfg {Boolean} frame
\r
5581 * @cfg {Boolean} header
\r
5585 * @cfg {Boolean} headerAsText
\r
5589 * @cfg {Boolean} hideCollapseTool
\r
5593 * @cfg {String} iconCls
\r
5597 * @cfg {Boolean/String} shadow
\r
5601 * @cfg {Number} shadowOffset
\r
5605 * @cfg {Boolean} shim
\r
5609 * @cfg {Object/Array} tbar
\r
5613 * @cfg {Array} tools
\r
5617 * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
\r
5621 * @cfg {String} xtype
\r
5625 * @property header
\r
5629 * @property footer
\r
5637 * @method getBottomToolbar
\r
5641 * @method getTopToolbar
\r
5645 * @method setIconClass
\r
5653 * @event beforeclose
\r
5657 * @event bodyresize
\r
5665 * @event deactivate
\r
5669 Ext.reg('fieldset', Ext.form.FieldSet);
\r
5671 * @class Ext.form.HtmlEditor
\r
5672 * @extends Ext.form.Field
\r
5673 * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
\r
5674 * automatically hidden when needed. These are noted in the config options where appropriate.
\r
5675 * <br><br>The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
\r
5676 * enabled by default unless the global {@link Ext.QuickTips} singleton is {@link Ext.QuickTips#init initialized}.
\r
5677 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
\r
5678 * supported by this editor.</b>
\r
5679 * <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
5680 * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.
\r
5681 * <br><br>Example usage:
\r
5683 // Simple example rendered with default options:
\r
5684 Ext.QuickTips.init(); // enable tooltips
\r
5685 new Ext.form.HtmlEditor({
\r
5686 renderTo: Ext.getBody(),
\r
5691 // Passed via xtype into a container and with custom options:
\r
5692 Ext.QuickTips.init(); // enable tooltips
\r
5694 title: 'HTML Editor',
\r
5695 renderTo: Ext.getBody(),
\r
5701 xtype: 'htmleditor',
\r
5702 enableColors: false,
\r
5703 enableAlignments: false
\r
5708 * Create a new HtmlEditor
\r
5709 * @param {Object} config
\r
5710 * @xtype htmleditor
\r
5713 Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
\r
5715 * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
\r
5717 enableFormat : true,
\r
5719 * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
\r
5721 enableFontSize : true,
\r
5723 * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
\r
5725 enableColors : true,
\r
5727 * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
\r
5729 enableAlignments : true,
\r
5731 * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
\r
5733 enableLists : true,
\r
5735 * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
\r
5737 enableSourceEdit : true,
\r
5739 * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
\r
5741 enableLinks : true,
\r
5743 * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
\r
5745 enableFont : true,
\r
5747 * @cfg {String} createLinkText The default text for the create link prompt
\r
5749 createLinkText : 'Please enter the URL for the link:',
\r
5751 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
\r
5753 defaultLinkValue : 'http:/'+'/',
\r
5755 * @cfg {Array} fontFamilies An array of available font families
\r
5761 'Times New Roman',
\r
5764 defaultFont: 'tahoma',
\r
5766 * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to ​ (Zero-width space), (Non-breaking space) in Opera and IE6).
\r
5768 defaultValue: (Ext.isOpera || Ext.isIE6) ? ' ' : '​',
\r
5770 // private properties
\r
5771 actionMode: 'wrap',
\r
5772 validationEvent : false,
\r
5773 deferHeight: true,
\r
5774 initialized : false,
\r
5775 activated : false,
\r
5776 sourceEditMode : false,
\r
5777 onFocus : Ext.emptyFn,
\r
5779 hideMode:'offsets',
\r
5780 defaultAutoCreate : {
\r
5782 style:"width:500px;height:300px;",
\r
5783 autocomplete: "off"
\r
5787 initComponent : function(){
\r
5790 * @event initialize
\r
5791 * Fires when the editor is fully initialized (including the iframe)
\r
5792 * @param {HtmlEditor} this
\r
5797 * Fires when the editor is first receives the focus. Any insertion must wait
\r
5798 * until after this event.
\r
5799 * @param {HtmlEditor} this
\r
5803 * @event beforesync
\r
5804 * Fires before the textarea is updated with content from the editor iframe. Return false
\r
5805 * to cancel the sync.
\r
5806 * @param {HtmlEditor} this
\r
5807 * @param {String} html
\r
5811 * @event beforepush
\r
5812 * Fires before the iframe editor is updated with content from the textarea. Return false
\r
5813 * to cancel the push.
\r
5814 * @param {HtmlEditor} this
\r
5815 * @param {String} html
\r
5820 * Fires when the textarea is updated with content from the editor iframe.
\r
5821 * @param {HtmlEditor} this
\r
5822 * @param {String} html
\r
5827 * Fires when the iframe editor is updated with content from the textarea.
\r
5828 * @param {HtmlEditor} this
\r
5829 * @param {String} html
\r
5833 * @event editmodechange
\r
5834 * Fires when the editor switches edit modes
\r
5835 * @param {HtmlEditor} this
\r
5836 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
\r
5843 createFontOptions : function(){
\r
5844 var buf = [], fs = this.fontFamilies, ff, lc;
\r
5845 for(var i = 0, len = fs.length; i< len; i++){
\r
5847 lc = ff.toLowerCase();
\r
5849 '<option value="',lc,'" style="font-family:',ff,';"',
\r
5850 (this.defaultFont == lc ? ' selected="true">' : '>'),
\r
5855 return buf.join('');
\r
5859 * Protected method that will not generally be called directly. It
\r
5860 * is called when the editor creates its toolbar. Override this method if you need to
\r
5861 * add custom toolbar buttons.
\r
5862 * @param {HtmlEditor} editor
\r
5864 createToolbar : function(editor){
\r
5866 var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
\r
5868 function btn(id, toggle, handler){
\r
5871 cls : 'x-btn-icon',
\r
5872 iconCls: 'x-edit-'+id,
\r
5873 enableToggle:toggle !== false,
\r
5875 handler:handler||editor.relayBtnCmd,
\r
5876 clickEvent:'mousedown',
\r
5877 tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
\r
5878 overflowText: editor.buttonTips[id].title || undefined,
\r
5883 // build the toolbar
\r
5884 var tb = new Ext.Toolbar({
\r
5885 renderTo:this.wrap.dom.firstChild
\r
5888 // stop form submits
\r
5889 this.mon(tb.el, 'click', function(e){
\r
5890 e.preventDefault();
\r
5893 if(this.enableFont && !Ext.isSafari2){
\r
5894 this.fontSelect = tb.el.createChild({
\r
5896 cls:'x-font-select',
\r
5897 html: this.createFontOptions()
\r
5899 this.mon(this.fontSelect, 'change', function(){
\r
5900 var font = this.fontSelect.dom.value;
\r
5901 this.relayCmd('fontname', font);
\r
5902 this.deferFocus();
\r
5906 this.fontSelect.dom,
\r
5911 if(this.enableFormat){
\r
5919 if(this.enableFontSize){
\r
5922 btn('increasefontsize', false, this.adjustFont),
\r
5923 btn('decreasefontsize', false, this.adjustFont)
\r
5927 if(this.enableColors){
\r
5930 itemId:'forecolor',
\r
5932 iconCls: 'x-edit-forecolor',
\r
5933 clickEvent:'mousedown',
\r
5934 tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,
\r
5936 menu : new Ext.menu.ColorMenu({
\r
5937 allowReselect: true,
\r
5938 focus: Ext.emptyFn,
\r
5943 select: function(cp, color){
\r
5944 this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
\r
5945 this.deferFocus();
\r
5948 clickEvent:'mousedown'
\r
5951 itemId:'backcolor',
\r
5953 iconCls: 'x-edit-backcolor',
\r
5954 clickEvent:'mousedown',
\r
5955 tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,
\r
5957 menu : new Ext.menu.ColorMenu({
\r
5958 focus: Ext.emptyFn,
\r
5961 allowReselect: true,
\r
5964 select: function(cp, color){
\r
5966 this.execCmd('useCSS', false);
\r
5967 this.execCmd('hilitecolor', color);
\r
5968 this.execCmd('useCSS', true);
\r
5969 this.deferFocus();
\r
5971 this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
\r
5972 this.deferFocus();
\r
5976 clickEvent:'mousedown'
\r
5982 if(this.enableAlignments){
\r
5985 btn('justifyleft'),
\r
5986 btn('justifycenter'),
\r
5987 btn('justifyright')
\r
5991 if(!Ext.isSafari2){
\r
5992 if(this.enableLinks){
\r
5995 btn('createlink', false, this.createLink)
\r
5999 if(this.enableLists){
\r
6002 btn('insertorderedlist'),
\r
6003 btn('insertunorderedlist')
\r
6006 if(this.enableSourceEdit){
\r
6009 btn('sourceedit', true, function(btn){
\r
6010 this.toggleSourceEdit(!this.sourceEditMode);
\r
6020 * Protected method that will not generally be called directly. It
\r
6021 * is called when the editor initializes the iframe with HTML contents. Override this method if you
\r
6022 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
\r
6024 getDocMarkup : function(){
\r
6025 return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
\r
6029 getEditorBody : function(){
\r
6030 return this.doc.body || this.doc.documentElement;
\r
6034 getDoc : function(){
\r
6035 return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
\r
6039 getWin : function(){
\r
6040 return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
\r
6044 onRender : function(ct, position){
\r
6045 Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
\r
6046 this.el.dom.style.border = '0 none';
\r
6047 this.el.dom.setAttribute('tabIndex', -1);
\r
6048 this.el.addClass('x-hidden');
\r
6049 if(Ext.isIE){ // fix IE 1px bogus margin
\r
6050 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
\r
6052 this.wrap = this.el.wrap({
\r
6053 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
\r
6056 this.createToolbar(this);
\r
6058 this.disableItems(true);
\r
6059 // is this needed?
\r
6060 // this.tb.doLayout();
\r
6062 this.createIFrame();
\r
6065 var sz = this.el.getSize();
\r
6066 this.setSize(sz.width, this.height || sz.height);
\r
6068 this.resizeEl = this.positionEl = this.wrap;
\r
6071 createIFrame: function(){
\r
6072 var iframe = document.createElement('iframe');
\r
6073 iframe.name = Ext.id();
\r
6074 iframe.frameBorder = '0';
\r
6075 iframe.src = Ext.SSL_SECURE_URL;
\r
6076 this.wrap.dom.appendChild(iframe);
\r
6078 this.iframe = iframe;
\r
6080 this.monitorTask = Ext.TaskMgr.start({
\r
6081 run: this.checkDesignMode,
\r
6087 initFrame : function(){
\r
6088 Ext.TaskMgr.stop(this.monitorTask);
\r
6089 this.doc = this.getDoc();
\r
6090 this.win = this.getWin();
\r
6093 this.doc.write(this.getDocMarkup());
\r
6096 var task = { // must defer to wait for browser to be ready
\r
6098 if(this.doc.body || this.doc.readyState == 'complete'){
\r
6099 Ext.TaskMgr.stop(task);
\r
6100 this.doc.designMode="on";
\r
6101 this.initEditor.defer(10, this);
\r
6108 Ext.TaskMgr.start(task);
\r
6112 checkDesignMode : function(){
\r
6113 if(this.wrap && this.wrap.dom.offsetWidth){
\r
6114 var doc = this.getDoc();
\r
6118 if(!doc.editorInitialized || String(doc.designMode).toLowerCase() != 'on'){
\r
6124 disableItems: function(disabled){
\r
6125 if(this.fontSelect){
\r
6126 this.fontSelect.dom.disabled = disabled;
\r
6128 this.tb.items.each(function(item){
\r
6129 if(item.getItemId() != 'sourceedit'){
\r
6130 item.setDisabled(disabled);
\r
6136 onResize : function(w, h){
\r
6137 Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
\r
6138 if(this.el && this.iframe){
\r
6139 if(Ext.isNumber(w)){
\r
6140 var aw = w - this.wrap.getFrameWidth('lr');
\r
6141 this.el.setWidth(aw);
\r
6142 this.tb.setWidth(aw);
\r
6143 this.iframe.style.width = Math.max(aw, 0) + 'px';
\r
6145 if(Ext.isNumber(h)){
\r
6146 var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
\r
6147 this.el.setHeight(ah);
\r
6148 this.iframe.style.height = Math.max(ah, 0) + 'px';
\r
6150 this.getEditorBody().style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
\r
6157 * Toggles the editor between standard and source edit mode.
\r
6158 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
\r
6160 toggleSourceEdit : function(sourceEditMode){
\r
6161 if(sourceEditMode === undefined){
\r
6162 sourceEditMode = !this.sourceEditMode;
\r
6164 this.sourceEditMode = sourceEditMode === true;
\r
6165 var btn = this.tb.items.get('sourceedit');
\r
6166 if(btn.pressed !== this.sourceEditMode){
\r
6167 btn.toggle(this.sourceEditMode);
\r
6168 if(!btn.xtbHidden){
\r
6172 if(this.sourceEditMode){
\r
6173 this.disableItems(true);
\r
6175 this.iframe.className = 'x-hidden';
\r
6176 this.el.removeClass('x-hidden');
\r
6177 this.el.dom.removeAttribute('tabIndex');
\r
6180 if(this.initialized){
\r
6181 this.disableItems(false);
\r
6184 this.iframe.className = '';
\r
6185 this.el.addClass('x-hidden');
\r
6186 this.el.dom.setAttribute('tabIndex', -1);
\r
6187 this.deferFocus();
\r
6189 var lastSize = this.lastSize;
\r
6191 delete this.lastSize;
\r
6192 this.setSize(lastSize);
\r
6194 this.fireEvent('editmodechange', this, this.sourceEditMode);
\r
6197 // private used internally
\r
6198 createLink : function(){
\r
6199 var url = prompt(this.createLinkText, this.defaultLinkValue);
\r
6200 if(url && url != 'http:/'+'/'){
\r
6201 this.relayCmd('createlink', url);
\r
6206 initEvents : function(){
\r
6207 this.originalValue = this.getValue();
\r
6211 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
\r
6214 markInvalid : Ext.emptyFn,
\r
6217 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
\r
6220 clearInvalid : Ext.emptyFn,
\r
6222 // docs inherit from Field
\r
6223 setValue : function(v){
\r
6224 Ext.form.HtmlEditor.superclass.setValue.call(this, v);
\r
6230 * Protected method that will not generally be called directly. If you need/want
\r
6231 * custom HTML cleanup, this is the method you should override.
\r
6232 * @param {String} html The HTML to be cleaned
\r
6233 * @return {String} The cleaned HTML
\r
6235 cleanHtml: function(html) {
\r
6236 html = String(html);
\r
6237 if(Ext.isWebKit){ // strip safari nonsense
\r
6238 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
\r
6242 * Neat little hack. Strips out all the non-digit characters from the default
\r
6243 * value and compares it to the character code of the first character in the string
\r
6244 * because it can cause encoding issues when posted to the server.
\r
6246 if(html.charCodeAt(0) == this.defaultValue.replace(/\D/g, '')){
\r
6247 html = html.substring(1);
\r
6253 * Protected method that will not generally be called directly. Syncs the contents
\r
6254 * of the editor iframe with the textarea.
\r
6256 syncValue : function(){
\r
6257 if(this.initialized){
\r
6258 var bd = this.getEditorBody();
\r
6259 var html = bd.innerHTML;
\r
6261 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
\r
6262 var m = bs.match(/text-align:(.*?);/i);
\r
6264 html = '<div style="'+m[0]+'">' + html + '</div>';
\r
6267 html = this.cleanHtml(html);
\r
6268 if(this.fireEvent('beforesync', this, html) !== false){
\r
6269 this.el.dom.value = html;
\r
6270 this.fireEvent('sync', this, html);
\r
6275 //docs inherit from Field
\r
6276 getValue : function() {
\r
6277 this[this.sourceEditMode ? 'pushValue' : 'syncValue']();
\r
6278 return Ext.form.HtmlEditor.superclass.getValue.call(this);
\r
6282 * Protected method that will not generally be called directly. Pushes the value of the textarea
\r
6283 * into the iframe editor.
\r
6285 pushValue : function(){
\r
6286 if(this.initialized){
\r
6287 var v = this.el.dom.value;
\r
6288 if(!this.activated && v.length < 1){
\r
6289 v = this.defaultValue;
\r
6291 if(this.fireEvent('beforepush', this, v) !== false){
\r
6292 this.getEditorBody().innerHTML = v;
\r
6294 // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
\r
6296 mode = d.designMode.toLowerCase();
\r
6298 d.designMode = mode.toggle('on', 'off');
\r
6299 d.designMode = mode;
\r
6301 this.fireEvent('push', this, v);
\r
6307 deferFocus : function(){
\r
6308 this.focus.defer(10, this);
\r
6311 // docs inherit from Field
\r
6312 focus : function(){
\r
6313 if(this.win && !this.sourceEditMode){
\r
6321 initEditor : function(){
\r
6322 //Destroying the component during/before initEditor can cause issues.
\r
6324 var dbody = this.getEditorBody();
\r
6325 var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
\r
6326 ss['background-attachment'] = 'fixed'; // w3c
\r
6327 dbody.bgProperties = 'fixed'; // ie
\r
6329 Ext.DomHelper.applyStyles(dbody, ss);
\r
6333 Ext.EventManager.removeAll(this.doc);
\r
6337 this.doc = this.getDoc();
\r
6339 Ext.EventManager.on(this.doc, {
\r
6340 'mousedown': this.onEditorEvent,
\r
6341 'dblclick': this.onEditorEvent,
\r
6342 'click': this.onEditorEvent,
\r
6343 'keyup': this.onEditorEvent,
\r
6349 Ext.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
\r
6351 if(Ext.isIE || Ext.isWebKit || Ext.isOpera){
\r
6352 Ext.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
\r
6354 this.initialized = true;
\r
6355 this.fireEvent('initialize', this);
\r
6356 this.doc.editorInitialized = true;
\r
6362 onDestroy : function(){
\r
6363 if(this.monitorTask){
\r
6364 Ext.TaskMgr.stop(this.monitorTask);
\r
6366 if(this.rendered){
\r
6367 Ext.destroy(this.tb);
\r
6369 this.wrap.dom.innerHTML = '';
\r
6370 this.wrap.remove();
\r
6374 this.el.removeAllListeners();
\r
6380 Ext.EventManager.removeAll(this.doc);
\r
6381 for (var prop in this.doc){
\r
6382 delete this.doc[prop];
\r
6386 this.purgeListeners();
\r
6390 onFirstFocus : function(){
\r
6391 this.activated = true;
\r
6392 this.disableItems(false);
\r
6393 if(Ext.isGecko){ // prevent silly gecko errors
\r
6395 var s = this.win.getSelection();
\r
6396 if(!s.focusNode || s.focusNode.nodeType != 3){
\r
6397 var r = s.getRangeAt(0);
\r
6398 r.selectNodeContents(this.getEditorBody());
\r
6400 this.deferFocus();
\r
6403 this.execCmd('useCSS', true);
\r
6404 this.execCmd('styleWithCSS', false);
\r
6407 this.fireEvent('activate', this);
\r
6411 adjustFont: function(btn){
\r
6412 var adjust = btn.getItemId() == 'increasefontsize' ? 1 : -1;
\r
6414 var v = parseInt(this.doc.queryCommandValue('FontSize') || 2, 10);
\r
6415 if((Ext.isSafari && !Ext.isSafari2) || Ext.isChrome || Ext.isAir){
\r
6416 // Safari 3 values
\r
6417 // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
\r
6420 }else if(v <= 13){
\r
6422 }else if(v <= 16){
\r
6424 }else if(v <= 18){
\r
6426 }else if(v <= 24){
\r
6431 v = v.constrain(1, 6);
\r
6433 if(Ext.isSafari){ // safari
\r
6436 v = Math.max(1, v+adjust) + (Ext.isSafari ? 'px' : 0);
\r
6438 this.execCmd('FontSize', v);
\r
6442 onEditorEvent : function(e){
\r
6443 this.updateToolbar();
\r
6448 * Protected method that will not generally be called directly. It triggers
\r
6449 * a toolbar update by reading the markup state of the current selection in the editor.
\r
6451 updateToolbar: function(){
\r
6453 if(!this.activated){
\r
6454 this.onFirstFocus();
\r
6458 var btns = this.tb.items.map, doc = this.doc;
\r
6460 if(this.enableFont && !Ext.isSafari2){
\r
6461 var name = (this.doc.queryCommandValue('FontName')||this.defaultFont).toLowerCase();
\r
6462 if(name != this.fontSelect.dom.value){
\r
6463 this.fontSelect.dom.value = name;
\r
6466 if(this.enableFormat){
\r
6467 btns.bold.toggle(doc.queryCommandState('bold'));
\r
6468 btns.italic.toggle(doc.queryCommandState('italic'));
\r
6469 btns.underline.toggle(doc.queryCommandState('underline'));
\r
6471 if(this.enableAlignments){
\r
6472 btns.justifyleft.toggle(doc.queryCommandState('justifyleft'));
\r
6473 btns.justifycenter.toggle(doc.queryCommandState('justifycenter'));
\r
6474 btns.justifyright.toggle(doc.queryCommandState('justifyright'));
\r
6476 if(!Ext.isSafari2 && this.enableLists){
\r
6477 btns.insertorderedlist.toggle(doc.queryCommandState('insertorderedlist'));
\r
6478 btns.insertunorderedlist.toggle(doc.queryCommandState('insertunorderedlist'));
\r
6481 Ext.menu.MenuMgr.hideAll();
\r
6487 relayBtnCmd : function(btn){
\r
6488 this.relayCmd(btn.getItemId());
\r
6492 * Executes a Midas editor command on the editor document and performs necessary focus and
\r
6493 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
\r
6494 * @param {String} cmd The Midas command
\r
6495 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
\r
6497 relayCmd : function(cmd, value){
\r
6500 this.execCmd(cmd, value);
\r
6501 this.updateToolbar();
\r
6502 }).defer(10, this);
\r
6506 * Executes a Midas editor command directly on the editor document.
\r
6507 * For visual commands, you should use {@link #relayCmd} instead.
\r
6508 * <b>This should only be called after the editor is initialized.</b>
\r
6509 * @param {String} cmd The Midas command
\r
6510 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
\r
6512 execCmd : function(cmd, value){
\r
6513 this.doc.execCommand(cmd, false, value === undefined ? null : value);
\r
6518 applyCommand : function(e){
\r
6520 var c = e.getCharCode(), cmd;
\r
6522 c = String.fromCharCode(c);
\r
6531 cmd = 'underline';
\r
6536 this.execCmd(cmd);
\r
6537 this.deferFocus();
\r
6538 e.preventDefault();
\r
6545 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
\r
6547 * @param {String} text
\r
6549 insertAtCursor : function(text){
\r
6550 if(!this.activated){
\r
6555 var r = this.doc.selection.createRange();
\r
6558 r.pasteHTML(text);
\r
6560 this.deferFocus();
\r
6564 this.execCmd('InsertHTML', text);
\r
6565 this.deferFocus();
\r
6570 fixKeys : function(){ // load time branching for fastest keydown performance
\r
6572 return function(e){
\r
6573 var k = e.getKey(), r;
\r
6576 r = this.doc.selection.createRange();
\r
6579 r.pasteHTML(' ');
\r
6580 this.deferFocus();
\r
6582 }else if(k == e.ENTER){
\r
6583 r = this.doc.selection.createRange();
\r
6585 var target = r.parentElement();
\r
6586 if(!target || target.tagName.toLowerCase() != 'li'){
\r
6588 r.pasteHTML('<br />');
\r
6589 r.collapse(false);
\r
6595 }else if(Ext.isOpera){
\r
6596 return function(e){
\r
6597 var k = e.getKey();
\r
6601 this.execCmd('InsertHTML',' ');
\r
6602 this.deferFocus();
\r
6605 }else if(Ext.isWebKit){
\r
6606 return function(e){
\r
6607 var k = e.getKey();
\r
6610 this.execCmd('InsertText','\t');
\r
6611 this.deferFocus();
\r
6612 }else if(k == e.ENTER){
\r
6614 this.execCmd('InsertHtml','<br /><br />');
\r
6615 this.deferFocus();
\r
6622 * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>
\r
6623 * @return {Ext.Toolbar}
\r
6625 getToolbar : function(){
\r
6630 * Object collection of toolbar tooltips for the buttons in the editor. The key
\r
6631 * is the command id associated with that button and the value is a valid QuickTips object.
\r
6636 title: 'Bold (Ctrl+B)',
\r
6637 text: 'Make the selected text bold.',
\r
6638 cls: 'x-html-editor-tip'
\r
6641 title: 'Italic (Ctrl+I)',
\r
6642 text: 'Make the selected text italic.',
\r
6643 cls: 'x-html-editor-tip'
\r
6651 title: 'Bold (Ctrl+B)',
\r
6652 text: 'Make the selected text bold.',
\r
6653 cls: 'x-html-editor-tip'
\r
6656 title: 'Italic (Ctrl+I)',
\r
6657 text: 'Make the selected text italic.',
\r
6658 cls: 'x-html-editor-tip'
\r
6661 title: 'Underline (Ctrl+U)',
\r
6662 text: 'Underline the selected text.',
\r
6663 cls: 'x-html-editor-tip'
\r
6665 increasefontsize : {
\r
6666 title: 'Grow Text',
\r
6667 text: 'Increase the font size.',
\r
6668 cls: 'x-html-editor-tip'
\r
6670 decreasefontsize : {
\r
6671 title: 'Shrink Text',
\r
6672 text: 'Decrease the font size.',
\r
6673 cls: 'x-html-editor-tip'
\r
6676 title: 'Text Highlight Color',
\r
6677 text: 'Change the background color of the selected text.',
\r
6678 cls: 'x-html-editor-tip'
\r
6681 title: 'Font Color',
\r
6682 text: 'Change the color of the selected text.',
\r
6683 cls: 'x-html-editor-tip'
\r
6686 title: 'Align Text Left',
\r
6687 text: 'Align text to the left.',
\r
6688 cls: 'x-html-editor-tip'
\r
6691 title: 'Center Text',
\r
6692 text: 'Center text in the editor.',
\r
6693 cls: 'x-html-editor-tip'
\r
6696 title: 'Align Text Right',
\r
6697 text: 'Align text to the right.',
\r
6698 cls: 'x-html-editor-tip'
\r
6700 insertunorderedlist : {
\r
6701 title: 'Bullet List',
\r
6702 text: 'Start a bulleted list.',
\r
6703 cls: 'x-html-editor-tip'
\r
6705 insertorderedlist : {
\r
6706 title: 'Numbered List',
\r
6707 text: 'Start a numbered list.',
\r
6708 cls: 'x-html-editor-tip'
\r
6711 title: 'Hyperlink',
\r
6712 text: 'Make the selected text a hyperlink.',
\r
6713 cls: 'x-html-editor-tip'
\r
6716 title: 'Source Edit',
\r
6717 text: 'Switch to source editing mode.',
\r
6718 cls: 'x-html-editor-tip'
\r
6722 // hide stuff that is not compatible
\r
6736 * @event specialkey
\r
6740 * @cfg {String} fieldClass @hide
\r
6743 * @cfg {String} focusClass @hide
\r
6746 * @cfg {String} autoCreate @hide
\r
6749 * @cfg {String} inputType @hide
\r
6752 * @cfg {String} invalidClass @hide
\r
6755 * @cfg {String} invalidText @hide
\r
6758 * @cfg {String} msgFx @hide
\r
6761 * @cfg {String} validateOnBlur @hide
\r
6764 * @cfg {Boolean} allowDomMove @hide
\r
6767 * @cfg {String} applyTo @hide
\r
6770 * @cfg {String} autoHeight @hide
\r
6773 * @cfg {String} autoWidth @hide
\r
6776 * @cfg {String} cls @hide
\r
6779 * @cfg {String} disabled @hide
\r
6782 * @cfg {String} disabledClass @hide
\r
6785 * @cfg {String} msgTarget @hide
\r
6788 * @cfg {String} readOnly @hide
\r
6791 * @cfg {String} style @hide
\r
6794 * @cfg {String} validationDelay @hide
\r
6797 * @cfg {String} validationEvent @hide
\r
6800 * @cfg {String} tabIndex @hide
\r
6803 * @property disabled
\r
6807 * @method applyToMarkup
\r
6819 * @method validate
\r
6827 * @method setDisabled
\r
6835 Ext.reg('htmleditor', Ext.form.HtmlEditor);/**
\r
6836 * @class Ext.form.TimeField
\r
6837 * @extends Ext.form.ComboBox
\r
6838 * Provides a time input field with a time dropdown and automatic time validation. Example usage:
\r
6840 new Ext.form.TimeField({
\r
6841 minValue: '9:00 AM',
\r
6842 maxValue: '6:00 PM',
\r
6847 * Create a new TimeField
\r
6848 * @param {Object} config
\r
6849 * @xtype timefield
\r
6851 Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {
\r
6853 * @cfg {Date/String} minValue
\r
6854 * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string
\r
6855 * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to null).
\r
6859 * @cfg {Date/String} maxValue
\r
6860 * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string
\r
6861 * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to null).
\r
6865 * @cfg {String} minText
\r
6866 * The error text to display when the date in the cell is before minValue (defaults to
\r
6867 * 'The time in this field must be equal to or after {0}').
\r
6869 minText : "The time in this field must be equal to or after {0}",
\r
6871 * @cfg {String} maxText
\r
6872 * The error text to display when the time is after maxValue (defaults to
\r
6873 * 'The time in this field must be equal to or before {0}').
\r
6875 maxText : "The time in this field must be equal to or before {0}",
\r
6877 * @cfg {String} invalidText
\r
6878 * The error text to display when the time in the field is invalid (defaults to
\r
6879 * '{value} is not a valid time').
\r
6881 invalidText : "{0} is not a valid time",
\r
6883 * @cfg {String} format
\r
6884 * The default time format string which can be overriden for localization support. The format must be
\r
6885 * valid according to {@link Date#parseDate} (defaults to 'g:i A', e.g., '3:15 PM'). For 24-hour time
\r
6886 * format try 'H:i' instead.
\r
6890 * @cfg {String} altFormats
\r
6891 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
\r
6892 * 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
6894 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
6896 * @cfg {Number} increment
\r
6897 * The number of minutes between each time value in the list (defaults to 15).
\r
6901 // private override
\r
6903 // private override
\r
6904 triggerAction: 'all',
\r
6905 // private override
\r
6908 // private - This is the date to use when generating time values in the absence of either minValue
\r
6909 // or maxValue. Using the current date causes DST issues on DST boundary dates, so this is an
\r
6910 // arbitrary "safe" date that can be any date aside from DST boundary dates.
\r
6911 initDate: '1/1/2008',
\r
6914 initComponent : function(){
\r
6915 if(typeof this.minValue == "string"){
\r
6916 this.minValue = this.parseDate(this.minValue);
\r
6918 if(typeof this.maxValue == "string"){
\r
6919 this.maxValue = this.parseDate(this.maxValue);
\r
6923 var min = this.parseDate(this.minValue) || new Date(this.initDate).clearTime();
\r
6924 var max = this.parseDate(this.maxValue) || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1);
\r
6926 while(min <= max){
\r
6927 times.push(min.dateFormat(this.format));
\r
6928 min = min.add('mi', this.increment);
\r
6930 this.store = times;
\r
6932 Ext.form.TimeField.superclass.initComponent.call(this);
\r
6936 getValue : function(){
\r
6937 var v = Ext.form.TimeField.superclass.getValue.call(this);
\r
6938 return this.formatDate(this.parseDate(v)) || '';
\r
6942 setValue : function(value){
\r
6943 return Ext.form.TimeField.superclass.setValue.call(this, this.formatDate(this.parseDate(value)));
\r
6946 // private overrides
\r
6947 validateValue : Ext.form.DateField.prototype.validateValue,
\r
6948 parseDate : Ext.form.DateField.prototype.parseDate,
\r
6949 formatDate : Ext.form.DateField.prototype.formatDate,
\r
6952 beforeBlur : function(){
\r
6953 var v = this.parseDate(this.getRawValue());
\r
6955 this.setValue(v.dateFormat(this.format));
\r
6957 Ext.form.TimeField.superclass.beforeBlur.call(this);
\r
6961 * @cfg {Boolean} grow @hide
\r
6964 * @cfg {Number} growMin @hide
\r
6967 * @cfg {Number} growMax @hide
\r
6971 * @method autoSize
\r
6974 Ext.reg('timefield', Ext.form.TimeField);/**
6975 * @class Ext.form.Label
6976 * @extends Ext.BoxComponent
6977 * Basic Label field.
6979 * Creates a new Label
6980 * @param {Ext.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
6981 * 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
6982 * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
6985 Ext.form.Label = Ext.extend(Ext.BoxComponent, {
6987 * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
6988 * tags within the label's innerHTML, use the {@link #html} config instead.
6991 * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
6992 * attribute. If not specified, the attribute will not be added to the label.
6995 * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
6996 * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
7000 onRender : function(ct, position){
7002 this.el = document.createElement('label');
7003 this.el.id = this.getId();
7004 this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
7006 this.el.setAttribute('for', this.forId);
7009 Ext.form.Label.superclass.onRender.call(this, ct, position);
7013 * Updates the label's innerHTML with the specified string.
7014 * @param {String} text The new label text
7015 * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
7016 * to the label (defaults to true which encodes the value). This might be useful if you want to include
7017 * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
7018 * @return {Label} this
7020 setText : function(t, encode){
7021 var e = encode === false;
7022 this[!e ? 'text' : 'html'] = t;
7023 delete this[e ? 'text' : 'html'];
7025 this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
7031 Ext.reg('label', Ext.form.Label);/**
\r
7032 * @class Ext.form.Action
\r
7033 * <p>The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.</p>
\r
7034 * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
\r
7035 * the Form needs to perform an action such as submit or load. The Configuration options
\r
7036 * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit},
\r
7037 * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}</p>
\r
7038 * <p>The instance of Action which performed the action is passed to the success
\r
7039 * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit},
\r
7040 * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}),
\r
7041 * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and
\r
7042 * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.</p>
\r
7044 Ext.form.Action = function(form, options){
\r
7046 this.options = options || {};
\r
7050 * Failure type returned when client side validation of the Form fails
\r
7051 * thus aborting a submit action. Client side validation is performed unless
\r
7052 * {@link #clientValidation} is explicitly set to <tt>false</tt>.
\r
7056 Ext.form.Action.CLIENT_INVALID = 'client';
\r
7058 * <p>Failure type returned when server side processing fails and the {@link #result}'s
\r
7059 * <tt style="font-weight:bold">success</tt> property is set to <tt>false</tt>.</p>
\r
7060 * <p>In the case of a form submission, field-specific error messages may be returned in the
\r
7061 * {@link #result}'s <tt style="font-weight:bold">errors</tt> property.</p>
\r
7065 Ext.form.Action.SERVER_INVALID = 'server';
\r
7067 * Failure type returned when a communication error happens when attempting
\r
7068 * to send a request to the remote server. The {@link #response} may be examined to
\r
7069 * provide further information.
\r
7073 Ext.form.Action.CONNECT_FAILURE = 'connect';
\r
7075 * Failure type returned when the response's <tt style="font-weight:bold">success</tt>
\r
7076 * property is set to <tt>false</tt>, or no field values are returned in the response's
\r
7077 * <tt style="font-weight:bold">data</tt> property.
\r
7081 Ext.form.Action.LOAD_FAILURE = 'load';
\r
7083 Ext.form.Action.prototype = {
\r
7085 * @cfg {String} url The URL that the Action is to invoke.
\r
7088 * @cfg {Boolean} reset When set to <tt><b>true</b></tt>, causes the Form to be
\r
7089 * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens
\r
7090 * <b>before</b> the {@link #success} callback is called and before the Form's
\r
7091 * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires.
\r
7094 * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the
\r
7095 * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method.
\r
7098 * @cfg {Mixed} params <p>Extra parameter values to pass. These are added to the Form's
\r
7099 * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's
\r
7100 * input fields.</p>
\r
7101 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
\r
7104 * @cfg {Number} timeout The number of seconds to wait for a server response before
\r
7105 * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified,
\r
7106 * defaults to the configured <tt>{@link Ext.form.BasicForm#timeout timeout}</tt> of the
\r
7107 * {@link Ext.form.BasicForm form}.
\r
7110 * @cfg {Function} success The function to call when a valid success return packet is recieved.
\r
7111 * The function is passed the following parameters:<ul class="mdetail-params">
\r
7112 * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
\r
7113 * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. The {@link #result}
\r
7114 * property of this object may be examined to perform custom postprocessing.</div></li>
\r
7118 * @cfg {Function} failure The function to call when a failure packet was recieved, or when an
\r
7119 * error ocurred in the Ajax communication.
\r
7120 * The function is passed the following parameters:<ul class="mdetail-params">
\r
7121 * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
\r
7122 * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. If an Ajax
\r
7123 * error ocurred, the failure type will be in {@link #failureType}. The {@link #result}
\r
7124 * property of this object may be examined to perform custom postprocessing.</div></li>
\r
7128 * @cfg {Object} scope The scope in which to call the callback functions (The <tt>this</tt> reference
\r
7129 * for the callback functions).
\r
7132 * @cfg {String} waitMsg The message to be displayed by a call to {@link Ext.MessageBox#wait}
\r
7133 * during the time the action is being processed.
\r
7136 * @cfg {String} waitTitle The title to be displayed by a call to {@link Ext.MessageBox#wait}
\r
7137 * during the time the action is being processed.
\r
7141 * The type of action this Action instance performs.
\r
7142 * Currently only "submit" and "load" are supported.
\r
7147 * The type of failure detected will be one of these: {@link #CLIENT_INVALID},
\r
7148 * {@link #SERVER_INVALID}, {@link #CONNECT_FAILURE}, or {@link #LOAD_FAILURE}. Usage:
\r
7150 var fp = new Ext.form.FormPanel({
\r
7155 handler: function(){
\r
7156 if(fp.getForm().isValid()){
\r
7157 fp.getForm().submit({
\r
7158 url: 'form-submit.php',
\r
7159 waitMsg: 'Submitting your data...',
\r
7160 success: function(form, action){
\r
7161 // server responded with success = true
\r
7162 var result = action.{@link #result};
\r
7164 failure: function(form, action){
\r
7165 if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
\r
7166 Ext.Msg.alert('Error',
\r
7167 'Status:'+action.{@link #response}.status+': '+
\r
7168 action.{@link #response}.statusText);
\r
7170 if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
\r
7171 // server responded with success = false
\r
7172 Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
\r
7180 handler: function(){
\r
7181 fp.getForm().reset();
\r
7185 * @property failureType
\r
7189 * The XMLHttpRequest object used to perform the action.
\r
7190 * @property response
\r
7194 * The decoded response object containing a boolean <tt style="font-weight:bold">success</tt> property and
\r
7195 * other, action-specific properties.
\r
7196 * @property result
\r
7200 // interface method
\r
7201 run : function(options){
\r
7205 // interface method
\r
7206 success : function(response){
\r
7210 // interface method
\r
7211 handleResponse : function(response){
\r
7215 // default connection failure
\r
7216 failure : function(response){
\r
7217 this.response = response;
\r
7218 this.failureType = Ext.form.Action.CONNECT_FAILURE;
\r
7219 this.form.afterAction(this, false);
\r
7223 // shared code among all Actions to validate that there was a response
\r
7224 // with either responseText or responseXml
\r
7225 processResponse : function(response){
\r
7226 this.response = response;
\r
7227 if(!response.responseText && !response.responseXML){
\r
7230 this.result = this.handleResponse(response);
\r
7231 return this.result;
\r
7234 // utility functions used internally
\r
7235 getUrl : function(appendParams){
\r
7236 var url = this.options.url || this.form.url || this.form.el.dom.action;
\r
7238 var p = this.getParams();
\r
7240 url = Ext.urlAppend(url, p);
\r
7247 getMethod : function(){
\r
7248 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
\r
7252 getParams : function(){
\r
7253 var bp = this.form.baseParams;
\r
7254 var p = this.options.params;
\r
7256 if(typeof p == "object"){
\r
7257 p = Ext.urlEncode(Ext.applyIf(p, bp));
\r
7258 }else if(typeof p == 'string' && bp){
\r
7259 p += '&' + Ext.urlEncode(bp);
\r
7262 p = Ext.urlEncode(bp);
\r
7268 createCallback : function(opts){
\r
7269 var opts = opts || {};
\r
7271 success: this.success,
\r
7272 failure: this.failure,
\r
7274 timeout: (opts.timeout*1000) || (this.form.timeout*1000),
\r
7275 upload: this.form.fileUpload ? this.success : undefined
\r
7281 * @class Ext.form.Action.Submit
\r
7282 * @extends Ext.form.Action
\r
7283 * <p>A class which handles submission of data from {@link Ext.form.BasicForm Form}s
\r
7284 * and processes the returned response.</p>
\r
7285 * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
\r
7286 * {@link Ext.form.BasicForm#submit submit}ting.</p>
\r
7287 * <p><u><b>Response Packet Criteria</b></u></p>
\r
7288 * <p>A response packet may contain:
\r
7289 * <div class="mdetail-params"><ul>
\r
7290 * <li><b><code>success</code></b> property : Boolean
\r
7291 * <div class="sub-desc">The <code>success</code> property is required.</div></li>
\r
7292 * <li><b><code>errors</code></b> property : Object
\r
7293 * <div class="sub-desc"><div class="sub-desc">The <code>errors</code> property,
\r
7294 * which is optional, contains error messages for invalid fields.</div></li>
\r
7296 * <p><u><b>JSON Packets</b></u></p>
\r
7297 * <p>By default, response packets are assumed to be JSON, so a typical response
\r
7298 * packet may look like this:</p><pre><code>
\r
7302 clientCode: "Client not found",
\r
7303 portOfLoading: "This field must not be null"
\r
7306 * <p>Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback
\r
7307 * or event handler methods. The object decoded from this JSON is available in the
\r
7308 * {@link Ext.form.Action#result result} property.</p>
\r
7309 * <p>Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:</p><pre><code>
\r
7310 errorReader: new Ext.data.XmlReader({
\r
7312 success: '@success'
\r
7318 * <p>then the results may be sent back in XML format:</p><pre><code>
\r
7319 <?xml version="1.0" encoding="UTF-8"?>
\r
7320 <message success="false">
\r
7323 <id>clientCode</id>
\r
7324 <msg><![CDATA[Code not found. <br /><i>This is a test validation message from the server </i>]]></msg>
\r
7327 <id>portOfLoading</id>
\r
7328 <msg><![CDATA[Port not found. <br /><i>This is a test validation message from the server </i>]]></msg>
\r
7333 * <p>Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback
\r
7334 * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.</p>
\r
7336 Ext.form.Action.Submit = function(form, options){
\r
7337 Ext.form.Action.Submit.superclass.constructor.call(this, form, options);
\r
7340 Ext.extend(Ext.form.Action.Submit, Ext.form.Action, {
\r
7342 * @cfg {Ext.data.DataReader} errorReader <p><b>Optional. JSON is interpreted with
\r
7343 * no need for an errorReader.</b></p>
\r
7344 * <p>A Reader which reads a single record from the returned data. The DataReader's
\r
7345 * <b>success</b> property specifies how submission success is determined. The Record's
\r
7346 * data provides the error messages to apply to any invalid form Fields.</p>
\r
7349 * @cfg {boolean} clientValidation Determines whether a Form's fields are validated
\r
7350 * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission.
\r
7351 * Pass <tt>false</tt> in the Form's submit options to prevent this. If not defined, pre-submission field validation
\r
7358 var o = this.options;
\r
7359 var method = this.getMethod();
\r
7360 var isGet = method == 'GET';
\r
7361 if(o.clientValidation === false || this.form.isValid()){
\r
7362 Ext.Ajax.request(Ext.apply(this.createCallback(o), {
\r
7363 form:this.form.el.dom,
\r
7364 url:this.getUrl(isGet),
\r
7366 headers: o.headers,
\r
7367 params:!isGet ? this.getParams() : null,
\r
7368 isUpload: this.form.fileUpload
\r
7370 }else if (o.clientValidation !== false){ // client validation failed
\r
7371 this.failureType = Ext.form.Action.CLIENT_INVALID;
\r
7372 this.form.afterAction(this, false);
\r
7377 success : function(response){
\r
7378 var result = this.processResponse(response);
\r
7379 if(result === true || result.success){
\r
7380 this.form.afterAction(this, true);
\r
7383 if(result.errors){
\r
7384 this.form.markInvalid(result.errors);
\r
7386 this.failureType = Ext.form.Action.SERVER_INVALID;
\r
7387 this.form.afterAction(this, false);
\r
7391 handleResponse : function(response){
\r
7392 if(this.form.errorReader){
\r
7393 var rs = this.form.errorReader.read(response);
\r
7396 for(var i = 0, len = rs.records.length; i < len; i++) {
\r
7397 var r = rs.records[i];
\r
7398 errors[i] = r.data;
\r
7401 if(errors.length < 1){
\r
7405 success : rs.success,
\r
7409 return Ext.decode(response.responseText);
\r
7415 * @class Ext.form.Action.Load
\r
7416 * @extends Ext.form.Action
\r
7417 * <p>A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.</p>
\r
7418 * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
\r
7419 * {@link Ext.form.BasicForm#load load}ing.</p>
\r
7420 * <p><u><b>Response Packet Criteria</b></u></p>
\r
7421 * <p>A response packet <b>must</b> contain:
\r
7422 * <div class="mdetail-params"><ul>
\r
7423 * <li><b><code>success</code></b> property : Boolean</li>
\r
7424 * <li><b><code>data</code></b> property : Object</li>
\r
7425 * <div class="sub-desc">The <code>data</code> property contains the values of Fields to load.
\r
7426 * The individual value object for each Field is passed to the Field's
\r
7427 * {@link Ext.form.Field#setValue setValue} method.</div></li>
\r
7429 * <p><u><b>JSON Packets</b></u></p>
\r
7430 * <p>By default, response packets are assumed to be JSON, so for the following form load call:<pre><code>
\r
7431 var myFormPanel = new Ext.form.FormPanel({
\r
7432 title: 'Client and routing info',
\r
7434 fieldLabel: 'Client',
\r
7435 name: 'clientName'
\r
7437 fieldLabel: 'Port of loading',
\r
7438 name: 'portOfLoading'
\r
7440 fieldLabel: 'Port of discharge',
\r
7441 name: 'portOfDischarge'
\r
7444 myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
\r
7445 url: '/getRoutingInfo.php',
\r
7447 consignmentRef: myConsignmentRef
\r
7449 failure: function(form, action) {
\r
7450 Ext.Msg.alert("Load failed", action.result.errorMessage);
\r
7454 * a <b>success response</b> packet may look like this:</p><pre><code>
\r
7458 clientName: "Fred. Olsen Lines",
\r
7459 portOfLoading: "FXT",
\r
7460 portOfDischarge: "OSL"
\r
7463 * while a <b>failure response</b> packet may look like this:</p><pre><code>
\r
7466 errorMessage: "Consignment reference not found"
\r
7468 * <p>Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s
\r
7469 * callback or event handler methods. The object decoded from this JSON is available in the
\r
7470 * {@link Ext.form.Action#result result} property.</p>
\r
7472 Ext.form.Action.Load = function(form, options){
\r
7473 Ext.form.Action.Load.superclass.constructor.call(this, form, options);
\r
7474 this.reader = this.form.reader;
\r
7477 Ext.extend(Ext.form.Action.Load, Ext.form.Action, {
\r
7483 Ext.Ajax.request(Ext.apply(
\r
7484 this.createCallback(this.options), {
\r
7485 method:this.getMethod(),
\r
7486 url:this.getUrl(false),
\r
7487 headers: this.options.headers,
\r
7488 params:this.getParams()
\r
7493 success : function(response){
\r
7494 var result = this.processResponse(response);
\r
7495 if(result === true || !result.success || !result.data){
\r
7496 this.failureType = Ext.form.Action.LOAD_FAILURE;
\r
7497 this.form.afterAction(this, false);
\r
7500 this.form.clearInvalid();
\r
7501 this.form.setValues(result.data);
\r
7502 this.form.afterAction(this, true);
\r
7506 handleResponse : function(response){
\r
7507 if(this.form.reader){
\r
7508 var rs = this.form.reader.read(response);
\r
7509 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
\r
7511 success : rs.success,
\r
7515 return Ext.decode(response.responseText);
\r
7522 * @class Ext.form.Action.DirectLoad
\r
7523 * @extends Ext.form.Action.Load
\r
7524 * <p>Provides Ext.direct support for loading form data.</p>
\r
7525 * <p>This example illustrates usage of Ext.Direct to <b>load</b> a form through Ext.Direct.</p>
\r
7527 var myFormPanel = new Ext.form.FormPanel({
\r
7528 // configs for FormPanel
\r
7529 title: 'Basic Information',
\r
7530 renderTo: document.body,
\r
7531 width: 300, height: 160,
\r
7534 // configs apply to child items
\r
7535 defaults: {anchor: '100%'},
\r
7536 defaultType: 'textfield',
\r
7538 fieldLabel: 'Name',
\r
7541 fieldLabel: 'Email',
\r
7544 fieldLabel: 'Company',
\r
7548 // configs for BasicForm
\r
7550 // The server-side method to call for load() requests
\r
7551 load: Profile.getBasicInfo,
\r
7552 // The server-side must mark the submit handler as a 'formHandler'
\r
7553 submit: Profile.updateBasicInfo
\r
7555 // specify the order for the passed params
\r
7556 paramOrder: ['uid', 'foo']
\r
7560 myFormPanel.getForm().load({
\r
7561 // pass 2 arguments to server side getBasicInfo method (len=2)
\r
7568 * The data packet sent to the server will resemble something like:
\r
7572 "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
\r
7573 "data":[34,"bar"] // note the order of the params
\r
7577 * The form will process a data packet returned by the server that is similar
\r
7578 * to the following format:
\r
7582 "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
\r
7586 "name":"Fred Flintstone",
\r
7587 "company":"Slate Rock and Gravel",
\r
7588 "email":"fred.flintstone@slaterg.com"
\r
7595 Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
\r
7596 constructor: function(form, opts) {
\r
7597 Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
\r
7599 type : 'directload',
\r
7602 var args = this.getParams();
\r
7603 args.push(this.success, this);
\r
7604 this.form.api.load.apply(window, args);
\r
7607 getParams : function() {
\r
7608 var buf = [], o = {};
\r
7609 var bp = this.form.baseParams;
\r
7610 var p = this.options.params;
\r
7611 Ext.apply(o, p, bp);
\r
7612 var paramOrder = this.form.paramOrder;
\r
7614 for(var i = 0, len = paramOrder.length; i < len; i++){
\r
7615 buf.push(o[paramOrder[i]]);
\r
7617 }else if(this.form.paramsAsHash){
\r
7622 // Direct actions have already been processed and therefore
\r
7623 // we can directly set the result; Direct Actions do not have
\r
7624 // a this.response property.
\r
7625 processResponse : function(result) {
\r
7626 this.result = result;
\r
7630 success : function(response, trans){
\r
7631 if(trans.type == Ext.Direct.exceptions.SERVER){
\r
7634 Ext.form.Action.DirectLoad.superclass.success.call(this, response);
\r
7639 * @class Ext.form.Action.DirectSubmit
\r
7640 * @extends Ext.form.Action.Submit
\r
7641 * <p>Provides Ext.direct support for submitting form data.</p>
\r
7642 * <p>This example illustrates usage of Ext.Direct to <b>submit</b> a form through Ext.Direct.</p>
\r
7644 var myFormPanel = new Ext.form.FormPanel({
\r
7645 // configs for FormPanel
\r
7646 title: 'Basic Information',
\r
7647 renderTo: document.body,
\r
7648 width: 300, height: 160,
\r
7652 handler: function(){
\r
7653 myFormPanel.getForm().submit({
\r
7662 // configs apply to child items
\r
7663 defaults: {anchor: '100%'},
\r
7664 defaultType: 'textfield',
\r
7666 fieldLabel: 'Name',
\r
7669 fieldLabel: 'Email',
\r
7672 fieldLabel: 'Company',
\r
7676 // configs for BasicForm
\r
7678 // The server-side method to call for load() requests
\r
7679 load: Profile.getBasicInfo,
\r
7680 // The server-side must mark the submit handler as a 'formHandler'
\r
7681 submit: Profile.updateBasicInfo
\r
7683 // specify the order for the passed params
\r
7684 paramOrder: ['uid', 'foo']
\r
7687 * The data packet sent to the server will resemble something like:
\r
7690 "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
\r
7694 "extAction":"Profile","extMethod":"updateBasicInfo",
\r
7695 "extType":"rpc","extTID":"6","extUpload":"false",
\r
7696 "name":"Aaron Conran","email":"aaron@extjs.com","company":"Ext JS, LLC"
\r
7701 * The form will process a data packet returned by the server that is similar
\r
7702 * to the following:
\r
7704 // sample success packet (batched requests)
\r
7707 "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,
\r
7714 // sample failure packet (one request)
\r
7716 "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
\r
7719 "email":"already taken"
\r
7726 * Also see the discussion in {@link Ext.form.Action.DirectLoad}.
\r
7728 Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
\r
7729 constructor : function(form, opts) {
\r
7730 Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
\r
7732 type : 'directsubmit',
\r
7733 // override of Submit
\r
7735 var o = this.options;
\r
7736 if(o.clientValidation === false || this.form.isValid()){
\r
7737 // tag on any additional params to be posted in the
\r
7739 this.success.params = this.getParams();
\r
7740 this.form.api.submit(this.form.el.dom, this.success, this);
\r
7741 }else if (o.clientValidation !== false){ // client validation failed
\r
7742 this.failureType = Ext.form.Action.CLIENT_INVALID;
\r
7743 this.form.afterAction(this, false);
\r
7747 getParams : function() {
\r
7749 var bp = this.form.baseParams;
\r
7750 var p = this.options.params;
\r
7751 Ext.apply(o, p, bp);
\r
7754 // Direct actions have already been processed and therefore
\r
7755 // we can directly set the result; Direct Actions do not have
\r
7756 // a this.response property.
\r
7757 processResponse : function(result) {
\r
7758 this.result = result;
\r
7762 success : function(response, trans){
\r
7763 if(trans.type == Ext.Direct.exceptions.SERVER){
\r
7766 Ext.form.Action.DirectSubmit.superclass.success.call(this, response);
\r
7770 Ext.form.Action.ACTION_TYPES = {
\r
7771 'load' : Ext.form.Action.Load,
\r
7772 'submit' : Ext.form.Action.Submit,
\r
7773 'directload' : Ext.form.Action.DirectLoad,
\r
7774 'directsubmit' : Ext.form.Action.DirectSubmit
\r
7777 * @class Ext.form.VTypes
7778 * <p>This is a singleton object which contains a set of commonly used field validation functions.
7779 * The validations provided are basic and intended to be easily customizable and extended.</p>
7780 * <p>To add custom VTypes specify the <code>{@link Ext.form.TextField#vtype vtype}</code> validation
7781 * test function, and optionally specify any corresponding error text to display and any keystroke
7782 * filtering mask to apply. For example:</p>
7784 // custom Vtype for vtype:'time'
7785 var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
7786 Ext.apply(Ext.form.VTypes, {
7787 // vtype validation function
7788 time: function(val, field) {
7789 return timeTest.test(val);
7791 // vtype Text property: The error text to display when the validation function returns false
7792 timeText: 'Not a valid time. Must be in the format "12:34 PM".',
7793 // vtype Mask property: The keystroke filter mask
7794 timeMask: /[\d\s:amp]/i
7799 // custom Vtype for vtype:'IPAddress'
7800 Ext.apply(Ext.form.VTypes, {
7801 IPAddress: function(v) {
7802 return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
7804 IPAddressText: 'Must be a numeric IP address',
7805 IPAddressMask: /[\d\.]/i
7810 Ext.form.VTypes = function(){
7811 // closure these in so they are only created once.
7812 var alpha = /^[a-zA-Z_]+$/,
7813 alphanum = /^[a-zA-Z0-9_]+$/,
7814 email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,4}$/,
7815 url = /(((^https?)|(^ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7817 // All these messages and functions are configurable
7820 * The function used to validate email addresses. Note that this is a very basic validation -- complete
7821 * validation per the email RFC specifications is very complex and beyond the scope of this class, although
7822 * this function can be overridden if a more comprehensive validation scheme is desired. See the validation
7823 * section of the <a href="http://en.wikipedia.org/wiki/E-mail_address">Wikipedia article on email addresses</a>
7824 * for additional information. This implementation is intended to validate the following emails:<tt>
7825 * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
7827 * @param {String} value The email address
7828 * @return {Boolean} true if the RegExp test passed, and false if not.
7830 'email' : function(v){
7831 return email.test(v);
7834 * The error text to display when the email validation function returns false. Defaults to:
7835 * <tt>'This field should be an e-mail address in the format "user@example.com"'</tt>
7838 'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
7840 * The keystroke filter mask to be applied on email input. See the {@link #email} method for
7841 * information about more complex email validation. Defaults to:
7842 * <tt>/[a-z0-9_\.\-@]/i</tt>
7845 'emailMask' : /[a-z0-9_\.\-@]/i,
7848 * The function used to validate URLs
7849 * @param {String} value The URL
7850 * @return {Boolean} true if the RegExp test passed, and false if not.
7852 'url' : function(v){
7856 * The error text to display when the url validation function returns false. Defaults to:
7857 * <tt>'This field should be a URL in the format "http:/'+'/www.example.com"'</tt>
7860 'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
7863 * The function used to validate alpha values
7864 * @param {String} value The value
7865 * @return {Boolean} true if the RegExp test passed, and false if not.
7867 'alpha' : function(v){
7868 return alpha.test(v);
7871 * The error text to display when the alpha validation function returns false. Defaults to:
7872 * <tt>'This field should only contain letters and _'</tt>
7875 'alphaText' : 'This field should only contain letters and _',
7877 * The keystroke filter mask to be applied on alpha input. Defaults to:
7878 * <tt>/[a-z_]/i</tt>
7881 'alphaMask' : /[a-z_]/i,
7884 * The function used to validate alphanumeric values
7885 * @param {String} value The value
7886 * @return {Boolean} true if the RegExp test passed, and false if not.
7888 'alphanum' : function(v){
7889 return alphanum.test(v);
7892 * The error text to display when the alphanumeric validation function returns false. Defaults to:
7893 * <tt>'This field should only contain letters, numbers and _'</tt>
7896 'alphanumText' : 'This field should only contain letters, numbers and _',
7898 * The keystroke filter mask to be applied on alphanumeric input. Defaults to:
7899 * <tt>/[a-z0-9_]/i</tt>
7902 'alphanumMask' : /[a-z0-9_]/i