X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/25ef3491bd9ae007ff1fc2b0d7943e6eaaccf775..2e847cf21b8ab9d15fa167b315ca5b2fa92638fc:/pkgs/pkg-forms-debug.js diff --git a/pkgs/pkg-forms-debug.js b/pkgs/pkg-forms-debug.js index a866f1e3..910f07ef 100644 --- a/pkgs/pkg-forms-debug.js +++ b/pkgs/pkg-forms-debug.js @@ -1,6 +1,6 @@ /*! - * Ext JS Library 3.0.3 - * Copyright(c) 2006-2009 Ext JS, LLC + * Ext JS Library 3.1.1 + * Copyright(c) 2006-2010 Ext JS, LLC * licensing@extjs.com * http://www.extjs.com/license */ @@ -14,6 +14,12 @@ * @xtype field */ Ext.form.Field = Ext.extend(Ext.BoxComponent, { + /** + *
The label Element associated with this Field. Only available after this Field has been rendered by a + * {@link form Ext.layout.FormLayout} layout manager.
+ * @type Ext.Element + * @property label + */ /** * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults * to 'text'). The types 'file' and 'password' must be used to render those field types currently -- there are @@ -80,17 +86,16 @@ Ext.form.Field = Ext.extend(Ext.BoxComponent, { */ fieldClass : 'x-form-field', /** - * @cfg {String} msgTarget The location where error text should display. Should be one of the following values - * (defaults to 'qtip'): - *-Value Description ------------ ---------------------------------------------------------------------- -qtip Display a quick tip when the user hovers over the field -title Display a default browser title attribute popup -under Add a block div beneath the field containing the error text -side Add an error icon to the right of the field with a popup on hover -[element id] Add the error text directly to the innerHTML of the specified element -+ * @cfg {String} msgTarget
The location where the message text set through {@link #markInvalid} should display. + * Must be one of the following values:
+ *qtip
Display a quick tip containing the message when the user hovers over the field. This is the default.
+ * title
Display the message in a default browser title attribute popup.under
Add a block div beneath the field containing the error message.side
Add an error icon to the right of the field, displaying the message in a popup on hover.[element id]
Add the error message directly to the innerHTML of the specified element.The value that the Field had at the time it was last focused. This is the value that is passed + * to the {@link #change} event which is fired if the value has been changed when the Field is blurred.
+ *This will be undefined until the Field has been visited. Compare {@link #originalValue}.
+ * @type mixed + * @property startValue + */ this.startValue = this.getValue(); this.fireEvent('focus', this); } @@ -337,7 +367,7 @@ var form = new Ext.form.FormPanel({ this.el.removeClass(this.focusClass); } this.hasFocus = false; - if(this.validationEvent !== false && (this.validateOnBlur || this.validationEvent != 'blur')){ + if(this.validationEvent !== false && (this.validateOnBlur || this.validationEvent == 'blur')){ this.validate(); } var v = this.getValue(); @@ -402,9 +432,19 @@ var form = new Ext.form.FormPanel({ }, /** - * Mark this field as invalid, using {@link #msgTarget} to determine how to - * display the error and applying {@link #invalidClass} to the field's element. - * Note: this method does not actually make the field + * Gets the active error message for this field. + * @return {String} Returns the active error message on the field, if there is no error, an empty string is returned. + */ + getActiveError : function(){ + return this.activeError || ''; + }, + + /** + *Display an error message associated with this field, using {@link #msgTarget} to determine how to + * display the message and applying {@link #invalidClass} to the field's UI element.
+ *Note: this method does not cause the Field's {@link #validate} method to return false
+ * if the value does pass validation. So simply marking a Field as invalid will not prevent
+ * submission of forms submitted with the {@link Ext.form.Action.Submit#clientValidation} option set.
-var trigger = new Ext.form.TriggerField();
-trigger.onTriggerClick = myTriggerFn;
-trigger.applyToMarkup('my-field');
-
- *
- * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
- * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
- *
- * @constructor
- * Create a new TriggerField.
- * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
- * to the base TextField)
- * @xtype trigger
- */
-Ext.form.TriggerField = Ext.extend(Ext.form.TextField, {
- /**
- * @cfg {String} triggerClass
- * An additional CSS class used to style the trigger button. The trigger will always get the
- * class 'x-form-trigger' by default and triggerClass will be appended if specified.
- */
- /**
- * @cfg {Mixed} triggerConfig
- * A {@link Ext.DomHelper DomHelper} config object specifying the structure of the - * trigger element for this Field. (Optional).
- *Specify this when you need a customized element to act as the trigger button for a TriggerField.
- *Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning - * and appearance of the trigger. Defaults to:
- *{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}
- */
- /**
- * @cfg {String/Object} autoCreate A {@link Ext.DomHelper DomHelper} element spec, or true for a default - * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component. - * See {@link Ext.Component#autoEl autoEl} for details. Defaults to:
- *{tag: "input", type: "text", size: "16", autocomplete: "off"}
- */
- defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
- /**
- * @cfg {Boolean} hideTrigger true to hide the trigger element and display only the base
- * text field (defaults to false)
- */
- hideTrigger:false,
- /**
- * @cfg {Boolean} editable false to prevent the user from typing text directly into the field,
- * the field will only respond to a click on the trigger to set the value. (defaults to true)
- */
- editable: true,
- /**
- * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
- * x-trigger-wrap-focus.
- */
- wrapFocusClass: 'x-trigger-wrap-focus',
- /**
- * @hide
- * @method autoSize
- */
- autoSize: Ext.emptyFn,
- // private
- monitorTab : true,
- // private
- deferHeight : true,
- // private
- mimicing : false,
-
- actionMode: 'wrap',
-
- defaultTriggerWidth: 17,
-
- // private
- onResize : function(w, h){
- Ext.form.TriggerField.superclass.onResize.call(this, w, h);
- var tw = this.getTriggerWidth();
- if(Ext.isNumber(w)){
- this.el.setWidth(w - tw);
- }
- this.wrap.setWidth(this.el.getWidth() + tw);
- },
-
- getTriggerWidth: function(){
- var tw = this.trigger.getWidth();
- if(!this.hideTrigger && tw === 0){
- tw = this.defaultTriggerWidth;
- }
- return tw;
- },
-
- // private
- alignErrorIcon : function(){
- if(this.wrap){
- this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
- }
- },
-
- // private
- onRender : function(ct, position){
- this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
- Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
-
- this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
- this.trigger = this.wrap.createChild(this.triggerConfig ||
- {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
- if(this.hideTrigger){
- this.trigger.setDisplayed(false);
- }
- this.initTrigger();
- if(!this.width){
- this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
- }
- if(!this.editable){
- this.editable = true;
- this.setEditable(false);
- }
- this.resizeEl = this.positionEl = this.wrap;
- },
-
- afterRender : function(){
- Ext.form.TriggerField.superclass.afterRender.call(this);
- },
-
- // private
- initTrigger : function(){
- this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
- this.trigger.addClassOnOver('x-form-trigger-over');
- this.trigger.addClassOnClick('x-form-trigger-click');
- },
-
- // private
- onDestroy : function(){
- Ext.destroy(this.trigger, this.wrap);
- if (this.mimicing){
- this.doc.un('mousedown', this.mimicBlur, this);
- }
- Ext.form.TriggerField.superclass.onDestroy.call(this);
- },
-
- // private
- onFocus : function(){
- Ext.form.TriggerField.superclass.onFocus.call(this);
- if(!this.mimicing){
- this.wrap.addClass(this.wrapFocusClass);
- this.mimicing = true;
- this.doc.on('mousedown', this.mimicBlur, this, {delay: 10});
- if(this.monitorTab){
- this.on('specialkey', this.checkTab, this);
- }
- }
- },
-
- // private
- checkTab : function(me, e){
- if(e.getKey() == e.TAB){
- this.triggerBlur();
- }
- },
-
- // private
- onBlur : Ext.emptyFn,
-
- // private
- mimicBlur : function(e){
- if(!this.isDestroyed && !this.wrap.contains(e.target) && this.validateBlur(e)){
- this.triggerBlur();
- }
- },
-
- // private
- triggerBlur : function(){
- this.mimicing = false;
- this.doc.un('mousedown', this.mimicBlur, this);
- if(this.monitorTab && this.el){
- this.un('specialkey', this.checkTab, this);
- }
- Ext.form.TriggerField.superclass.onBlur.call(this);
- if(this.wrap){
- this.wrap.removeClass(this.wrapFocusClass);
- }
- },
-
- beforeBlur : Ext.emptyFn,
-
- /**
- * Allow or prevent the user from directly editing the field text. If false is passed,
- * the user will only be able to modify the field using the trigger. This method
- * is the runtime equivalent of setting the 'editable' config option at config time.
- * @param {Boolean} value True to allow the user to directly edit the field text
- */
- setEditable : function(value){
- if(value == this.editable){
- return;
- }
- this.editable = value;
- if(!value){
- this.el.addClass('x-trigger-noedit').on('click', this.onTriggerClick, this).dom.setAttribute('readOnly', true);
- }else{
- this.el.removeClass('x-trigger-noedit').un('click', this.onTriggerClick, this).dom.removeAttribute('readOnly');
- }
- },
-
- // private
- // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
- validateBlur : function(e){
- return true;
- },
-
- /**
- * The function that should handle the trigger's click event. This method does nothing by default
- * until overridden by an implementing function. See Ext.form.ComboBox and Ext.form.DateField for
- * sample implementations.
- * @method
- * @param {EventObject} e
- */
- onTriggerClick : Ext.emptyFn
-
- /**
- * @cfg {Boolean} grow @hide
- */
- /**
- * @cfg {Number} growMin @hide
- */
- /**
- * @cfg {Number} growMax @hide
- */
-});
-
-/**
- * @class Ext.form.TwinTriggerField
- * @extends Ext.form.TriggerField
- * TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
- * to be extended by an implementing class. For an example of implementing this class, see the custom
- * SearchField implementation here:
- * http://extjs.com/deploy/ext/examples/form/custom.html
- */
-Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
- /**
- * @cfg {Mixed} triggerConfig
- * A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements - * for this Field. (Optional).
- *Specify this when you need a customized element to contain the two trigger elements for this Field. - * Each trigger element must be marked by the CSS class x-form-trigger (also see - * {@link #trigger1Class} and {@link #trigger2Class}).
- *Note that when using this option, it is the developer's responsibility to ensure correct sizing, - * positioning and appearance of the triggers.
- */ - /** - * @cfg {String} trigger1Class - * An additional CSS class used to style the trigger button. The trigger will always get the - * class 'x-form-trigger' by default and triggerClass will be appended if specified. - */ - /** - * @cfg {String} trigger2Class - * An additional CSS class used to style the trigger button. The trigger will always get the - * class 'x-form-trigger' by default and triggerClass will be appended if specified. - */ - - initComponent : function(){ - Ext.form.TwinTriggerField.superclass.initComponent.call(this); - - this.triggerConfig = { - tag:'span', cls:'x-form-twin-triggers', cn:[ - {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class}, - {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class} - ]}; - }, - - getTrigger : function(index){ - return this.triggers[index]; - }, - - initTrigger : function(){ - var ts = this.trigger.select('.x-form-trigger', true); - var triggerField = this; - ts.each(function(t, all, index){ - var triggerIndex = 'Trigger'+(index+1); - t.hide = function(){ - var w = triggerField.wrap.getWidth(); - this.dom.style.display = 'none'; - triggerField.el.setWidth(w-triggerField.trigger.getWidth()); - this['hidden' + triggerIndex] = true; - }; - t.show = function(){ - var w = triggerField.wrap.getWidth(); - this.dom.style.display = ''; - triggerField.el.setWidth(w-triggerField.trigger.getWidth()); - this['hidden' + triggerIndex] = false; - }; - - if(this['hide'+triggerIndex]){ - t.dom.style.display = 'none'; - this['hidden' + triggerIndex] = true; - } - this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true}); - t.addClassOnOver('x-form-trigger-over'); - t.addClassOnClick('x-form-trigger-click'); - }, this); - this.triggers = ts.elements; - }, - - getTriggerWidth: function(){ - var tw = 0; - Ext.each(this.triggers, function(t, index){ - var triggerIndex = 'Trigger' + (index + 1), - w = t.getWidth(); - if(w === 0 && !this['hidden' + triggerIndex]){ - tw += this.defaultTriggerWidth; - }else{ - tw += w; - } - }, this); - return tw; - }, - - // private - onDestroy : function() { - Ext.destroy(this.triggers); - Ext.form.TwinTriggerField.superclass.onDestroy.call(this); - }, - - /** - * The function that should handle the trigger's click event. This method does nothing by default - * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick} - * for additional information. - * @method - * @param {EventObject} e - */ - onTrigger1Click : Ext.emptyFn, - /** - * The function that should handle the trigger's click event. This method does nothing by default - * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick} - * for additional information. - * @method - * @param {EventObject} e - */ - onTrigger2Click : Ext.emptyFn -}); -Ext.reg('trigger', Ext.form.TriggerField);/** - * @class Ext.form.TextArea +/** + * @class Ext.form.TriggerField * @extends Ext.form.TextField - * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds - * support for auto-sizing. + * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default). + * The trigger has no default action, so you must assign a function to implement the trigger click handler by + * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox + * for which you can provide a custom implementation. For example: + *
+var trigger = new Ext.form.TriggerField();
+trigger.onTriggerClick = myTriggerFn;
+trigger.applyToMarkup('my-field');
+
+ *
+ * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
+ * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
+ *
* @constructor
- * Creates a new TextArea
- * @param {Object} config Configuration options
- * @xtype textarea
+ * Create a new TriggerField.
+ * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
+ * to the base TextField)
+ * @xtype trigger
*/
-Ext.form.TextArea = Ext.extend(Ext.form.TextField, {
- /**
- * @cfg {Number} growMin The minimum height to allow when {@link Ext.form.TextField#grow grow}=true
- * (defaults to 60)
- */
- growMin : 60,
+Ext.form.TriggerField = Ext.extend(Ext.form.TextField, {
/**
- * @cfg {Number} growMax The maximum height to allow when {@link Ext.form.TextField#grow grow}=true
- * (defaults to 1000)
+ * @cfg {String} triggerClass
+ * An additional CSS class used to style the trigger button. The trigger will always get the
+ * class 'x-form-trigger' by default and triggerClass will be appended if specified.
*/
- growMax: 1000,
- growAppend : ' \n ',
- growPad : Ext.isWebKit ? -6 : 0,
-
- enterIsSpecial : false,
-
/**
- * @cfg {Boolean} preventScrollbars true to prevent scrollbars from appearing regardless of how much text is
- * in the field. This option is only relevant when {@link #grow} is true. Equivalent to setting overflow: hidden, defaults to
- * false.
+ * @cfg {Mixed} triggerConfig
+ * A {@link Ext.DomHelper DomHelper} config object specifying the structure of the + * trigger element for this Field. (Optional).
+ *Specify this when you need a customized element to act as the trigger button for a TriggerField.
+ *Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning + * and appearance of the trigger. Defaults to:
+ *{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}
*/
- preventScrollbars: false,
/**
* @cfg {String/Object} autoCreate A {@link Ext.DomHelper DomHelper} element spec, or true for a default * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component. * See {@link Ext.Component#autoEl autoEl} for details. Defaults to:
- *{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}
+ * {tag: "input", type: "text", size: "16", autocomplete: "off"}
*/
+ defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
+ /**
+ * @cfg {Boolean} hideTrigger true to hide the trigger element and display only the base
+ * text field (defaults to false)
+ */
+ hideTrigger:false,
+ /**
+ * @cfg {Boolean} editable false to prevent the user from typing text directly into the field,
+ * the field will only respond to a click on the trigger to set the value. (defaults to true).
+ */
+ editable: true,
+ /**
+ * @cfg {Boolean} readOnly true to prevent the user from changing the field, and
+ * hides the trigger. Superceeds the editable and hideTrigger options if the value is true.
+ * (defaults to false)
+ */
+ readOnly: false,
+ /**
+ * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
+ * x-trigger-wrap-focus.
+ */
+ wrapFocusClass: 'x-trigger-wrap-focus',
+ /**
+ * @hide
+ * @method autoSize
+ */
+ autoSize: Ext.emptyFn,
+ // private
+ monitorTab : true,
+ // private
+ deferHeight : true,
+ // private
+ mimicing : false,
+
+ actionMode: 'wrap',
+
+ defaultTriggerWidth: 17,
// private
- onRender : function(ct, position){
- if(!this.el){
- this.defaultAutoCreate = {
- tag: "textarea",
- style:"width:100px;height:60px;",
- autocomplete: "off"
- };
- }
- Ext.form.TextArea.superclass.onRender.call(this, ct, position);
- if(this.grow){
- this.textSizeEl = Ext.DomHelper.append(document.body, {
- tag: "pre", cls: "x-form-grow-sizer"
- });
- if(this.preventScrollbars){
- this.el.setStyle("overflow", "hidden");
- }
- this.el.setHeight(this.growMin);
+ onResize : function(w, h){
+ Ext.form.TriggerField.superclass.onResize.call(this, w, h);
+ var tw = this.getTriggerWidth();
+ if(Ext.isNumber(w)){
+ this.el.setWidth(w - tw);
}
+ this.wrap.setWidth(this.el.getWidth() + tw);
},
- onDestroy : function(){
- Ext.destroy(this.textSizeEl);
- Ext.form.TextArea.superclass.onDestroy.call(this);
+ getTriggerWidth: function(){
+ var tw = this.trigger.getWidth();
+ if(!this.hideTrigger && tw === 0){
+ tw = this.defaultTriggerWidth;
+ }
+ return tw;
},
- fireKey : function(e){
- if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
- this.fireEvent("specialkey", this, e);
+ // private
+ alignErrorIcon : function(){
+ if(this.wrap){
+ this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
}
},
// private
- onKeyUp : function(e){
- if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
- this.autoSize();
+ onRender : function(ct, position){
+ this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
+ Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
+
+ this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
+ this.trigger = this.wrap.createChild(this.triggerConfig ||
+ {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
+ this.initTrigger();
+ if(!this.width){
+ this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
+ }
+ this.resizeEl = this.positionEl = this.wrap;
+ },
+
+ updateEditState: function(){
+ if(this.rendered){
+ if (this.readOnly) {
+ this.el.dom.readOnly = true;
+ this.el.addClass('x-trigger-noedit');
+ this.mun(this.el, 'click', this.onTriggerClick, this);
+ this.trigger.setDisplayed(false);
+ } else {
+ if (!this.editable) {
+ this.el.dom.readOnly = true;
+ this.el.addClass('x-trigger-noedit');
+ this.mon(this.el, 'click', this.onTriggerClick, this);
+ } else {
+ this.el.dom.readOnly = false;
+ this.el.removeClass('x-trigger-noedit');
+ this.mun(this.el, 'click', this.onTriggerClick, this);
+ }
+ this.trigger.setDisplayed(!this.hideTrigger);
+ }
+ this.onResize(this.width || this.wrap.getWidth());
+ }
+ },
+
+ setHideTrigger: function(hideTrigger){
+ if(hideTrigger != this.hideTrigger){
+ this.hideTrigger = hideTrigger;
+ this.updateEditState();
}
- Ext.form.TextArea.superclass.onKeyUp.call(this, e);
},
/**
- * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
- * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
+ * @param {Boolean} value True to allow the user to directly edit the field text
+ * Allow or prevent the user from directly editing the field text. If false is passed,
+ * the user will only be able to modify the field using the trigger. Will also add
+ * a click event to the text field which will call the trigger. This method
+ * is the runtime equivalent of setting the 'editable' config option at config time.
*/
- autoSize: function(){
- if(!this.grow || !this.textSizeEl){
- return;
+ setEditable: function(editable){
+ if(editable != this.editable){
+ this.editable = editable;
+ this.updateEditState();
}
- var el = this.el;
- var v = el.dom.value;
- var ts = this.textSizeEl;
- ts.innerHTML = '';
- ts.appendChild(document.createTextNode(v));
- v = ts.innerHTML;
+ },
+
+ /**
+ * @param {Boolean} value True to prevent the user changing the field and explicitly
+ * hide the trigger.
+ * Setting this to true will superceed settings editable and hideTrigger.
+ * Setting this to false will defer back to editable and hideTrigger. This method
+ * is the runtime equivalent of setting the 'readOnly' config option at config time.
+ */
+ setReadOnly: function(readOnly){
+ if(readOnly != this.readOnly){
+ this.readOnly = readOnly;
+ this.updateEditState();
+ }
+ },
+
+ afterRender : function(){
+ Ext.form.TriggerField.superclass.afterRender.call(this);
+ this.updateEditState();
+ },
+
+ // private
+ initTrigger : function(){
+ this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
+ this.trigger.addClassOnOver('x-form-trigger-over');
+ this.trigger.addClassOnClick('x-form-trigger-click');
+ },
+
+ // private
+ onDestroy : function(){
+ Ext.destroy(this.trigger, this.wrap);
+ if (this.mimicing){
+ this.doc.un('mousedown', this.mimicBlur, this);
+ }
+ delete this.doc;
+ Ext.form.TriggerField.superclass.onDestroy.call(this);
+ },
+
+ // private
+ onFocus : function(){
+ Ext.form.TriggerField.superclass.onFocus.call(this);
+ if(!this.mimicing){
+ this.wrap.addClass(this.wrapFocusClass);
+ this.mimicing = true;
+ this.doc.on('mousedown', this.mimicBlur, this, {delay: 10});
+ if(this.monitorTab){
+ this.on('specialkey', this.checkTab, this);
+ }
+ }
+ },
+
+ // private
+ checkTab : function(me, e){
+ if(e.getKey() == e.TAB){
+ this.triggerBlur();
+ }
+ },
+
+ // private
+ onBlur : Ext.emptyFn,
+
+ // private
+ mimicBlur : function(e){
+ if(!this.isDestroyed && !this.wrap.contains(e.target) && this.validateBlur(e)){
+ this.triggerBlur();
+ }
+ },
+
+ // private
+ triggerBlur : function(){
+ this.mimicing = false;
+ this.doc.un('mousedown', this.mimicBlur, this);
+ if(this.monitorTab && this.el){
+ this.un('specialkey', this.checkTab, this);
+ }
+ Ext.form.TriggerField.superclass.onBlur.call(this);
+ if(this.wrap){
+ this.wrap.removeClass(this.wrapFocusClass);
+ }
+ },
+
+ beforeBlur : Ext.emptyFn,
+
+ // private
+ // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
+ validateBlur : function(e){
+ return true;
+ },
+
+ /**
+ * The function that should handle the trigger's click event. This method does nothing by default
+ * until overridden by an implementing function. See Ext.form.ComboBox and Ext.form.DateField for
+ * sample implementations.
+ * @method
+ * @param {EventObject} e
+ */
+ onTriggerClick : Ext.emptyFn
+
+ /**
+ * @cfg {Boolean} grow @hide
+ */
+ /**
+ * @cfg {Number} growMin @hide
+ */
+ /**
+ * @cfg {Number} growMax @hide
+ */
+});
+
+/**
+ * @class Ext.form.TwinTriggerField
+ * @extends Ext.form.TriggerField
+ * TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
+ * to be extended by an implementing class. For an example of implementing this class, see the custom
+ * SearchField implementation here:
+ * http://extjs.com/deploy/ext/examples/form/custom.html
+ */
+Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
+ /**
+ * @cfg {Mixed} triggerConfig
+ * A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements + * for this Field. (Optional).
+ *Specify this when you need a customized element to contain the two trigger elements for this Field. + * Each trigger element must be marked by the CSS class x-form-trigger (also see + * {@link #trigger1Class} and {@link #trigger2Class}).
+ *Note that when using this option, it is the developer's responsibility to ensure correct sizing, + * positioning and appearance of the triggers.
+ */ + /** + * @cfg {String} trigger1Class + * An additional CSS class used to style the trigger button. The trigger will always get the + * class 'x-form-trigger' by default and triggerClass will be appended if specified. + */ + /** + * @cfg {String} trigger2Class + * An additional CSS class used to style the trigger button. The trigger will always get the + * class 'x-form-trigger' by default and triggerClass will be appended if specified. + */ + + initComponent : function(){ + Ext.form.TwinTriggerField.superclass.initComponent.call(this); + + this.triggerConfig = { + tag:'span', cls:'x-form-twin-triggers', cn:[ + {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class}, + {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class} + ]}; + }, + + getTrigger : function(index){ + return this.triggers[index]; + }, + + initTrigger : function(){ + var ts = this.trigger.select('.x-form-trigger', true); + var triggerField = this; + ts.each(function(t, all, index){ + var triggerIndex = 'Trigger'+(index+1); + t.hide = function(){ + var w = triggerField.wrap.getWidth(); + this.dom.style.display = 'none'; + triggerField.el.setWidth(w-triggerField.trigger.getWidth()); + this['hidden' + triggerIndex] = true; + }; + t.show = function(){ + var w = triggerField.wrap.getWidth(); + this.dom.style.display = ''; + triggerField.el.setWidth(w-triggerField.trigger.getWidth()); + this['hidden' + triggerIndex] = false; + }; + + if(this['hide'+triggerIndex]){ + t.dom.style.display = 'none'; + this['hidden' + triggerIndex] = true; + } + this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true}); + t.addClassOnOver('x-form-trigger-over'); + t.addClassOnClick('x-form-trigger-click'); + }, this); + this.triggers = ts.elements; + }, + + getTriggerWidth: function(){ + var tw = 0; + Ext.each(this.triggers, function(t, index){ + var triggerIndex = 'Trigger' + (index + 1), + w = t.getWidth(); + if(w === 0 && !this['hidden' + triggerIndex]){ + tw += this.defaultTriggerWidth; + }else{ + tw += w; + } + }, this); + return tw; + }, + + // private + onDestroy : function() { + Ext.destroy(this.triggers); + Ext.form.TwinTriggerField.superclass.onDestroy.call(this); + }, + + /** + * The function that should handle the trigger's click event. This method does nothing by default + * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick} + * for additional information. + * @method + * @param {EventObject} e + */ + onTrigger1Click : Ext.emptyFn, + /** + * The function that should handle the trigger's click event. This method does nothing by default + * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick} + * for additional information. + * @method + * @param {EventObject} e + */ + onTrigger2Click : Ext.emptyFn +}); +Ext.reg('trigger', Ext.form.TriggerField); +/** + * @class Ext.form.TextArea + * @extends Ext.form.TextField + * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds + * support for auto-sizing. + * @constructor + * Creates a new TextArea + * @param {Object} config Configuration options + * @xtype textarea + */ +Ext.form.TextArea = Ext.extend(Ext.form.TextField, { + /** + * @cfg {Number} growMin The minimum height to allow when {@link Ext.form.TextField#grow grow}=true + * (defaults to 60) + */ + growMin : 60, + /** + * @cfg {Number} growMax The maximum height to allow when {@link Ext.form.TextField#grow grow}=true + * (defaults to 1000) + */ + growMax: 1000, + growAppend : ' \n ', + + enterIsSpecial : false, + + /** + * @cfg {Boolean} preventScrollbars true to prevent scrollbars from appearing regardless of how much text is + * in the field. This option is only relevant when {@link #grow} is true. Equivalent to setting overflow: hidden, defaults to + * false. + */ + preventScrollbars: false, + /** + * @cfg {String/Object} autoCreateA {@link Ext.DomHelper DomHelper} element spec, or true for a default + * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component. + * See {@link Ext.Component#autoEl autoEl} for details. Defaults to:
+ *{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}
+ */
+
+ // private
+ onRender : function(ct, position){
+ if(!this.el){
+ this.defaultAutoCreate = {
+ tag: "textarea",
+ style:"width:100px;height:60px;",
+ autocomplete: "off"
+ };
+ }
+ Ext.form.TextArea.superclass.onRender.call(this, ct, position);
+ if(this.grow){
+ this.textSizeEl = Ext.DomHelper.append(document.body, {
+ tag: "pre", cls: "x-form-grow-sizer"
+ });
+ if(this.preventScrollbars){
+ this.el.setStyle("overflow", "hidden");
+ }
+ this.el.setHeight(this.growMin);
+ }
+ },
+
+ onDestroy : function(){
+ Ext.removeNode(this.textSizeEl);
+ Ext.form.TextArea.superclass.onDestroy.call(this);
+ },
+
+ fireKey : function(e){
+ if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
+ this.fireEvent("specialkey", this, e);
+ }
+ },
+
+ // private
+ doAutoSize : function(e){
+ return !e.isNavKeyPress() || e.getKey() == e.ENTER;
+ },
+
+ /**
+ * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
+ * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
+ */
+ autoSize: function(){
+ if(!this.grow || !this.textSizeEl){
+ return;
+ }
+ var el = this.el,
+ v = Ext.util.Format.htmlEncode(el.dom.value),
+ ts = this.textSizeEl,
+ h;
+
Ext.fly(ts).setWidth(this.el.getWidth());
if(v.length < 1){
v = " ";
}else{
v += this.growAppend;
if(Ext.isIE){
- v = v.replace(/\n/g, 'A combobox control with support for autocomplete, remote-loading, paging and many other features.
- *A ComboBox works in a similar manner to a traditional HTML <select> field. The difference is - * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input - * field to hold the value of the valueField. The {@link #displayField} is shown in the text field - * which is named according to the {@link #name}.
- *Events
- *To do something when something in ComboBox is selected, configure the select event:
-var cb = new Ext.form.ComboBox({
- // all of your config options
- listeners:{
- scope: yourScope,
- 'select': yourFunction
- }
-});
-
-// Alternatively, you can assign events after the object is created:
-var cb = new Ext.form.ComboBox(yourOptions);
-cb.on('select', yourFunction, yourScope);
- *
- *
- * ComboBox in Grid
- *If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer} - * will be needed to show the displayField when the editor is not active. Set up the renderer manually, or implement - * a reusable render, for example:
-// create reusable renderer
-Ext.util.Format.comboRenderer = function(combo){
- return function(value){
- var record = combo.findRecord(combo.{@link #valueField}, value);
- return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
- }
-}
-
-// create the combo instance
-var combo = new Ext.form.ComboBox({
- {@link #typeAhead}: true,
- {@link #triggerAction}: 'all',
- {@link #lazyRender}:true,
- {@link #mode}: 'local',
- {@link #store}: new Ext.data.ArrayStore({
- id: 0,
- fields: [
- 'myId',
- 'displayText'
- ],
- data: [[1, 'item1'], [2, 'item2']]
- }),
- {@link #valueField}: 'myId',
- {@link #displayField}: 'displayText'
-});
-
-// snippet of column model used within grid
-var cm = new Ext.grid.ColumnModel([{
- ...
- },{
- header: "Some Header",
- dataIndex: 'whatever',
- width: 130,
- editor: combo, // specify reference to combo instance
- renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
- },
- ...
-]);
- *
- *
- * Filtering
- *A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox - * store manually see {@link #lastQuery}.
- * @constructor - * Create a new ComboBox. - * @param {Object} config Configuration options - * @xtype combo - */ -Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, { - /** - * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox. - * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or - * {@link Ext.form.FormPanel}, you must also set {@link #lazyRender} = true. - */ - /** - * @cfg {Boolean} lazyRender true to prevent the ComboBox from rendering until requested - * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}), - * defaults to false). - */ - /** - * @cfg {String/Object} autoCreateA {@link Ext.DomHelper DomHelper} element spec, or true for a default - * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component. - * See {@link Ext.Component#autoEl autoEl} for details. Defaults to:
- *{tag: "input", type: "text", size: "24", autocomplete: "off"}
- */
- /**
- * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to undefined).
- * Acceptable values for this property are:
- * See also {@link #mode}.
- */ - /** - * @cfg {String} title If supplied, a header element is created containing this text and added into the top of - * the dropdown list (defaults to undefined, with no header element) - */ - - // private - defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"}, - /** - * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown - * list (defaults to the width of the ComboBox field). See also {@link #minListWidth} - */ - /** - * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this - * ComboBox (defaults to undefined if {@link #mode} = 'remote' or 'field1' if - * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on - * the store configuration}). - *See also {@link #valueField}.
- *Note: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a - * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not - * active.
- */ - /** - * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this - * ComboBox (defaults to undefined if {@link #mode} = 'remote' or 'field2' if - * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on - * the store configuration}). - *Note: use of a valueField requires the user to make a selection in order for a value to be - * mapped. See also {@link #hiddenName}, {@link #hiddenValue}, and {@link #displayField}.
- */ - /** - * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the - * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically - * post during a form submission. See also {@link #valueField}. - *Note: the hidden field's id will also default to this name if {@link #hiddenId} is not specified. - * The ComboBox {@link Ext.Component#id id} and the {@link #hiddenId} should be different, since - * no two DOM nodes should share the same id. So, if the ComboBox {@link Ext.form.Field#name name} and - * hiddenName are the same, you should specify a unique {@link #hiddenId}.
- */ - /** - * @cfg {String} hiddenId If {@link #hiddenName} is specified, hiddenId can also be provided - * to give the hidden field a unique id (defaults to the {@link #hiddenName}). The hiddenId - * and combo {@link Ext.Component#id id} should be different, since no two DOM - * nodes should share the same id. - */ - /** - * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is - * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured - * {@link Ext.form.Field#value value}. - */ - /** - * @cfg {String} listClass The CSS class to add to the predefined 'x-combo-list' class - * applied the dropdown list element (defaults to ''). - */ - listClass : '', - /** - * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list - * (defaults to 'x-combo-selected') - */ - selectedClass : 'x-combo-selected', - /** - * @cfg {String} listEmptyText The empty text to display in the data view if no items are found. - * (defaults to '') - */ - listEmptyText: '', - /** - * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always - * get the class 'x-form-trigger' and triggerClass will be appended if specified - * (defaults to 'x-form-arrow-trigger' which displays a downward arrow icon). - */ - triggerClass : 'x-form-arrow-trigger', - /** - * @cfg {Boolean/String} shadow true or "sides" for the default effect, "frame" for - * 4-way shadow, and "drop" for bottom-right - */ - shadow : 'sides', - /** - * @cfg {String} listAlign A valid anchor position value. See {@link Ext.Element#alignTo} for details - * on supported anchor positions (defaults to 'tl-bl?') - */ - listAlign : 'tl-bl?', - /** - * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown - * (defaults to 300) - */ - maxHeight : 300, - /** - * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its - * distance to the viewport edges (defaults to 90) - */ - minHeight : 90, - /** - * @cfg {String} triggerAction The action to execute when the trigger is clicked. - *{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.
{@link #doQuery run the query} specified by the {@link #allQuery} config option
See also {@link #queryParam}
.
Automatically loads the {@link #store} the first time the trigger - * is clicked. If you do not want the store to be automatically loaded the first time the trigger is - * clicked, set to 'local' and manually load the store. To force a requery of the store - * every time the trigger is clicked see {@link #lastQuery}.
ComboBox loads local data
- *
-var combo = new Ext.form.ComboBox({
- renderTo: document.body,
- mode: 'local',
- store: new Ext.data.ArrayStore({
- id: 0,
- fields: [
- 'myId', // numeric value is the key
- 'displayText'
- ],
- data: [[1, 'item1'], [2, 'item2']] // data is local
- }),
- valueField: 'myId',
- displayField: 'displayText',
- triggerAction: 'all'
-});
- *
-var combo = new Ext.form.ComboBox({
- ...
- mode: 'remote',
- ...
- listeners: {
- // delete the previous query in the beforequery event or set
- // combo.lastQuery = null (this will reload the store the next time it expands)
- beforequery: function(qe){
- delete qe.combo.lastQuery;
- }
- }
-});
- *
- * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
- * configure the combo with lastQuery=''. Example use:
- *
-var combo = new Ext.form.ComboBox({
- ...
- mode: 'local',
- triggerAction: 'all',
- lastQuery: ''
-});
- *
- * @property lastQuery
- * @type String
- */
-
- // private
- initComponent : function(){
- Ext.form.ComboBox.superclass.initComponent.call(this);
- this.addEvents(
- /**
- * @event expand
- * Fires when the dropdown list is expanded
- * @param {Ext.form.ComboBox} combo This combo box
- */
- 'expand',
- /**
- * @event collapse
- * Fires when the dropdown list is collapsed
- * @param {Ext.form.ComboBox} combo This combo box
- */
- 'collapse',
- /**
- * @event beforeselect
- * Fires before a list item is selected. Return false to cancel the selection.
- * @param {Ext.form.ComboBox} combo This combo box
- * @param {Ext.data.Record} record The data record returned from the underlying store
- * @param {Number} index The index of the selected item in the dropdown list
- */
- 'beforeselect',
- /**
- * @event select
- * Fires when a list item is selected
- * @param {Ext.form.ComboBox} combo This combo box
- * @param {Ext.data.Record} record The data record returned from the underlying store
- * @param {Number} index The index of the selected item in the dropdown list
- */
- 'select',
- /**
- * @event beforequery
- * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
- * cancel property to true.
- * @param {Object} queryEvent An object that has these properties:combo
: Ext.form.ComboBox query
: String forceAll
: Boolean cancel
: Boolean The template string, or {@link Ext.XTemplate} instance to - * use to display each item in the dropdown list. The dropdown list is displayed in a - * DataView. See {@link #view}.
- *The default template string is:
- '<tpl for="."><div class="x-combo-list-item">{' + this.displayField + '}</div></tpl>'
- *
- * Override the default value to create custom UI layouts for items in the list. - * For example:
- '<tpl for="."><div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}</div></tpl>'
- *
- * The template must contain one or more substitution parameters using field - * names from the Combo's {@link #store Store}. In the example above an - *
ext:qtipattribute is added to display other fields from the Store. - *
To preserve the default visual look of list items, add the CSS class name - *
x-combo-list-itemto the template's container element. - *
Also see {@link #itemSelector} for additional details.
- */ - this.tpl = 'A simple CSS selector (e.g. div.some-class or span:first-child) that will be - * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown - * display will be working with.
- *Note: this setting is required if a custom XTemplate has been - * specified in {@link #tpl} which assigns a class other than
'x-combo-list-item'- * to dropdown list items - */ - } - - /** - * The {@link Ext.DataView DataView} used to display the ComboBox's options. - * @type Ext.DataView - */ - this.view = new Ext.DataView({ - applyTo: this.innerList, - tpl: this.tpl, - singleSelect: true, - selectedClass: this.selectedClass, - itemSelector: this.itemSelector || '.' + cls + '-item', - emptyText: this.listEmptyText - }); - - this.mon(this.view, 'click', this.onViewClick, this); - - this.bindStore(this.store, true); - - if(this.resizable){ - this.resizer = new Ext.Resizable(this.list, { - pinned:true, handles:'se' - }); - this.mon(this.resizer, 'resize', function(r, w, h){ - this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight; - this.listWidth = w; - this.innerList.setWidth(w - this.list.getFrameWidth('lr')); - this.restrictHeight(); - }, this); - - this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px'); - } - } - }, - - /** - *
Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.
- * A custom implementation may be provided as a configuration option if the floating list needs to be rendered - * to a different Element. An example might be rendering the list inside a Menu so that clicking - * the list does not hide the Menu:
-var store = new Ext.data.ArrayStore({
- autoDestroy: true,
- fields: ['initials', 'fullname'],
- data : [
- ['FF', 'Fred Flintstone'],
- ['BR', 'Barney Rubble']
- ]
-});
-
-var combo = new Ext.form.ComboBox({
- store: store,
- displayField: 'fullname',
- emptyText: 'Select a name...',
- forceSelection: true,
- getListParent: function() {
- return this.el.up('.x-menu');
- },
- iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
- mode: 'local',
- selectOnFocus: true,
- triggerAction: 'all',
- typeAhead: true,
- width: 135
-});
-
-var menu = new Ext.menu.Menu({
- id: 'mainMenu',
- items: [
- combo // A Field in a Menu
- ]
-});
-
- */
- getListParent : function() {
- return document.body;
- },
-
- /**
- * Returns the store associated with this combo.
- * @return {Ext.data.Store} The store
- */
- getStore : function(){
- return this.store;
- },
-
- // private
- bindStore : function(store, initial){
- if(this.store && !initial){
- if(this.store !== store && this.store.autoDestroy){
- this.store.destroy();
- }else{
- this.store.un('beforeload', this.onBeforeLoad, this);
- this.store.un('load', this.onLoad, this);
- this.store.un('exception', this.collapse, this);
- }
- if(!store){
- this.store = null;
- if(this.view){
- this.view.bindStore(null);
- }
- if(this.pageTb){
- this.pageTb.bindStore(null);
- }
- }
- }
- if(store){
- if(!initial) {
- this.lastQuery = null;
- if(this.pageTb) {
- this.pageTb.bindStore(store);
- }
- }
-
- this.store = Ext.StoreMgr.lookup(store);
- this.store.on({
- scope: this,
- beforeload: this.onBeforeLoad,
- load: this.onLoad,
- exception: this.collapse
- });
-
- if(this.view){
- this.view.bindStore(store);
- }
- }
- },
-
- // private
- initEvents : function(){
- Ext.form.ComboBox.superclass.initEvents.call(this);
-
- this.keyNav = new Ext.KeyNav(this.el, {
- "up" : function(e){
- this.inKeyMode = true;
- this.selectPrev();
- },
-
- "down" : function(e){
- if(!this.isExpanded()){
- this.onTriggerClick();
- }else{
- this.inKeyMode = true;
- this.selectNext();
- }
- },
-
- "enter" : function(e){
- this.onViewClick();
- },
-
- "esc" : function(e){
- this.collapse();
- },
-
- "tab" : function(e){
- this.onViewClick(false);
- return true;
- },
-
- scope : this,
-
- doRelay : function(e, h, hname){
- if(hname == 'down' || this.scope.isExpanded()){
- // this MUST be called before ComboBox#fireKey()
- var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
- if(!Ext.isIE && Ext.EventManager.useKeydown){
- // call Combo#fireKey() for browsers which use keydown event (except IE)
- this.scope.fireKey(e);
- }
- return relay;
- }
- return true;
- },
-
- forceKeyDown : true,
- defaultEventAction: 'stopEvent'
- });
- this.queryDelay = Math.max(this.queryDelay || 10,
- this.mode == 'local' ? 10 : 250);
- this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
- if(this.typeAhead){
- this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
- }
- if(this.editable !== false && !this.enableKeyEvents){
- this.mon(this.el, 'keyup', this.onKeyUp, this);
- }
- },
-
- // private
- onDestroy : function(){
- if (this.dqTask){
- this.dqTask.cancel();
- this.dqTask = null;
- }
- this.bindStore(null);
- Ext.destroy(
- this.resizer,
- this.view,
- this.pageTb,
- this.list
- );
- Ext.form.ComboBox.superclass.onDestroy.call(this);
- },
-
- // private
- fireKey : function(e){
- if (!this.isExpanded()) {
- Ext.form.ComboBox.superclass.fireKey.call(this, e);
- }
- },
-
- // private
- onResize : function(w, h){
- Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
- if(this.isVisible() && this.list){
- this.doResize(w);
- }else{
- this.bufferSize = w;
- }
- },
-
- doResize: function(w){
- if(!Ext.isDefined(this.listWidth)){
- var lw = Math.max(w, this.minListWidth);
- this.list.setWidth(lw);
- this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
- }
- },
-
- // private
- onEnable : function(){
- Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
- if(this.hiddenField){
- this.hiddenField.disabled = false;
- }
- },
-
- // private
- onDisable : function(){
- Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
- if(this.hiddenField){
- this.hiddenField.disabled = true;
- }
- },
-
- // private
- onBeforeLoad : function(){
- if(!this.hasFocus){
- return;
- }
- this.innerList.update(this.loadingText ?
- 'A combobox control with support for autocomplete, remote-loading, paging and many other features.
+ *A ComboBox works in a similar manner to a traditional HTML <select> field. The difference is + * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input + * field to hold the value of the valueField. The {@link #displayField} is shown in the text field + * which is named according to the {@link #name}.
+ *Events
+ *To do something when something in ComboBox is selected, configure the select event:
+var cb = new Ext.form.ComboBox({
+ // all of your config options
+ listeners:{
+ scope: yourScope,
+ 'select': yourFunction
+ }
+});
- // private
- initEvents : function(){
- Ext.form.Checkbox.superclass.initEvents.call(this);
- this.mon(this.el, {
- scope: this,
- click: this.onClick,
- change: this.onClick
- });
- },
+// Alternatively, you can assign events after the object is created:
+var cb = new Ext.form.ComboBox(yourOptions);
+cb.on('select', yourFunction, yourScope);
+ *
+ *
+ * ComboBox in Grid
+ *If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer} + * will be needed to show the displayField when the editor is not active. Set up the renderer manually, or implement + * a reusable render, for example:
+// create reusable renderer
+Ext.util.Format.comboRenderer = function(combo){
+ return function(value){
+ var record = combo.findRecord(combo.{@link #valueField}, value);
+ return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
+ }
+}
+
+// create the combo instance
+var combo = new Ext.form.ComboBox({
+ {@link #typeAhead}: true,
+ {@link #triggerAction}: 'all',
+ {@link #lazyRender}:true,
+ {@link #mode}: 'local',
+ {@link #store}: new Ext.data.ArrayStore({
+ id: 0,
+ fields: [
+ 'myId',
+ 'displayText'
+ ],
+ data: [[1, 'item1'], [2, 'item2']]
+ }),
+ {@link #valueField}: 'myId',
+ {@link #displayField}: 'displayText'
+});
+// snippet of column model used within grid
+var cm = new Ext.grid.ColumnModel([{
+ ...
+ },{
+ header: "Some Header",
+ dataIndex: 'whatever',
+ width: 130,
+ editor: combo, // specify reference to combo instance
+ renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
+ },
+ ...
+]);
+ *
+ *
+ * Filtering
+ *A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox + * store manually see {@link #lastQuery}.
+ * @constructor + * Create a new ComboBox. + * @param {Object} config Configuration options + * @xtype combo + */ +Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, { /** - * @hide - * Overridden and disabled. The editor element does not support standard valid/invalid marking. - * @method + * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox. + * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or + * {@link Ext.form.FormPanel}, you must also set {@link #lazyRender} = true. */ - markInvalid : Ext.emptyFn, /** - * @hide - * Overridden and disabled. The editor element does not support standard valid/invalid marking. - * @method + * @cfg {Boolean} lazyRender true to prevent the ComboBox from rendering until requested + * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}), + * defaults to false). + */ + /** + * @cfg {String/Object} autoCreateA {@link Ext.DomHelper DomHelper} element spec, or true for a default + * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component. + * See {@link Ext.Component#autoEl autoEl} for details. Defaults to:
+ *{tag: "input", type: "text", size: "24", autocomplete: "off"}
+ */
+ /**
+ * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to undefined).
+ * Acceptable values for this property are:
+ * See also {@link #mode}.
+ */ + /** + * @cfg {String} title If supplied, a header element is created containing this text and added into the top of + * the dropdown list (defaults to undefined, with no header element) */ - clearInvalid : Ext.emptyFn, - - // private - onRender : function(ct, position){ - Ext.form.Checkbox.superclass.onRender.call(this, ct, position); - if(this.inputValue !== undefined){ - this.el.dom.value = this.inputValue; - } - this.wrap = this.el.wrap({cls: 'x-form-check-wrap'}); - if(this.boxLabel){ - this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel}); - } - if(this.checked){ - this.setValue(true); - }else{ - this.checked = this.el.dom.checked; - } - // Need to repaint for IE, otherwise positioning is broken - if(Ext.isIE){ - this.wrap.repaint(); - } - this.resizeEl = this.positionEl = this.wrap; - }, - - // private - onDestroy : function(){ - Ext.destroy(this.wrap); - Ext.form.Checkbox.superclass.onDestroy.call(this); - }, // private - initValue : function() { - this.originalValue = this.getValue(); - }, - + defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"}, /** - * Returns the checked state of the checkbox. - * @return {Boolean} True if checked, else false + * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown + * list (defaults to the width of the ComboBox field). See also {@link #minListWidth} */ - getValue : function(){ - if(this.rendered){ - return this.el.dom.checked; - } - return this.checked; - }, - - // private - onClick : function(){ - if(this.el.dom.checked != this.checked){ - this.setValue(this.el.dom.checked); - } - }, - /** - * Sets the checked state of the checkbox, fires the 'check' event, and calls a - *{@link #handler}
(if configured).
- * @param {Boolean/String} checked The following values will check the checkbox:
- * true, 'true', '1', or 'on'
. Any other value will uncheck the checkbox.
- * @return {Ext.form.Field} this
+ * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
+ * ComboBox (defaults to undefined if {@link #mode} = 'remote' or 'field1' if
+ * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
+ * the store configuration}).
+ * See also {@link #valueField}.
+ *Note: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a + * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not + * active.
*/ - setValue : function(v){ - var checked = this.checked ; - this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on'); - if(this.rendered){ - this.el.dom.checked = this.checked; - this.el.dom.defaultChecked = this.checked; - } - if(checked != this.checked){ - this.fireEvent('check', this, this.checked); - if(this.handler){ - this.handler.call(this.scope || this, this, this.checked); - } - } - return this; - } -}); -Ext.reg('checkbox', Ext.form.Checkbox); -/** - * @class Ext.form.CheckboxGroup - * @extends Ext.form.Field - *A grouping container for {@link Ext.form.Checkbox} controls.
- *Sample usage:
- *
-var myCheckboxGroup = new Ext.form.CheckboxGroup({
- id:'myGroup',
- xtype: 'checkboxgroup',
- fieldLabel: 'Single Column',
- itemCls: 'x-check-group-alt',
- // Put all controls in a single column with width 100%
- columns: 1,
- items: [
- {boxLabel: 'Item 1', name: 'cb-col-1'},
- {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
- {boxLabel: 'Item 3', name: 'cb-col-3'}
- ]
-});
- *
- * @constructor
- * Creates a new CheckboxGroup
- * @param {Object} config Configuration options
- * @xtype checkboxgroup
- */
-Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {
- /**
- * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects
- * to arrange in the group.
- */
- /**
- * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
- * checkbox/radio controls using automatic layout. This config can take several types of values:
- * The controls will be rendered one per column on one row and the width - * of each column will be evenly distributed based on the width of the overall field container. This is the default.
If you specific a number (e.g., 3) that number of columns will be - * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.
You can also specify an array of column widths, mixing integer - * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will - * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float - * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field - * container you should do so.
-// call with name and value
-myCheckboxGroup.setValue('cb-col-1', true);
-// call with an array of boolean values
-myCheckboxGroup.setValue([true, false, false]);
-// call with an object literal specifying item:value pairs
-myCheckboxGroup.setValue({
- 'cb-col-2': false,
- 'cb-col-3': true
-});
-// use comma separated string to set items with name to true (checked)
-myCheckboxGroup.setValue('cb-col-1,cb-col-3');
- *
- * See {@link Ext.form.Checkbox#setValue} for additional information.
- * @param {Mixed} id The checkbox to check, or as described by example shown.
- * @param {Boolean} value (optional) The value to set the item.
- * @return {Ext.form.CheckboxGroup} this
- */
- setValue: function(){
- if(this.rendered){
- this.onSetValue.apply(this, arguments);
- }else{
- this.buffered = true;
- this.value = arguments;
- }
- return this;
- },
-
- onSetValue: function(id, value){
- if(arguments.length == 1){
- if(Ext.isArray(id)){
- // an array of boolean values
- Ext.each(id, function(val, idx){
- var item = this.items.itemAt(idx);
- if(item){
- item.setValue(val);
- }
- }, this);
- }else if(Ext.isObject(id)){
- // set of name/value pairs
- for(var i in id){
- var f = this.getBox(i);
- if(f){
- f.setValue(id[i]);
- }
- }
- }else{
- this.setValueForItem(id);
- }
- }else{
- var f = this.getBox(id);
- if(f){
- f.setValue(value);
- }
- }
- },
-
- // private
- onDestroy: function(){
- Ext.destroy(this.panel);
- Ext.form.CheckboxGroup.superclass.onDestroy.call(this);
-
- },
-
- setValueForItem : function(val){
- val = String(val).split(',');
- this.eachItem(function(item){
- if(val.indexOf(item.inputValue)> -1){
- item.setValue(true);
- }
- });
- },
-
- // private
- getBox : function(id){
- var box = null;
- this.eachItem(function(f){
- if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
- box = f;
- return false;
- }
- });
- return box;
- },
-
- /**
- * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
- * @return {Array} An array of the selected checkboxes.
- */
- getValue : function(){
- var out = [];
- this.eachItem(function(item){
- if(item.checked){
- out.push(item);
- }
- });
- return out;
- },
-
- // private
- eachItem: function(fn){
- if(this.items && this.items.each){
- this.items.each(fn, this);
- }
- },
-
- /**
- * @cfg {String} name
- * @hide
- */
-
- /**
- * @method getRawValue
- * @hide
- */
- getRawValue : Ext.emptyFn,
-
- /**
- * @method setRawValue
- * @hide
- */
- setRawValue : Ext.emptyFn
-
-});
-
-Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
-/**
- * @class Ext.form.Radio
- * @extends Ext.form.Checkbox
- * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
- * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
- * @constructor
- * Creates a new Radio
- * @param {Object} config Configuration options
- * @xtype radio
- */
-Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
- inputType: 'radio',
-
/**
- * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
- * @method
+ * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this
+ * ComboBox (defaults to undefined if {@link #mode} = 'remote' or 'field2' if
+ * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
+ * the store configuration}).
+ * Note: use of a valueField requires the user to make a selection in order for a value to be + * mapped. See also {@link #hiddenName}, {@link #hiddenValue}, and {@link #displayField}.
+ */ + /** + * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the + * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically + * post during a form submission. See also {@link #valueField}. + *Note: the hidden field's id will also default to this name if {@link #hiddenId} is not specified. + * The ComboBox {@link Ext.Component#id id} and the {@link #hiddenId} should be different, since + * no two DOM nodes should share the same id. So, if the ComboBox {@link Ext.form.Field#name name} and + * hiddenName are the same, you should specify a unique {@link #hiddenId}.
+ */ + /** + * @cfg {String} hiddenId If {@link #hiddenName} is specified, hiddenId can also be provided + * to give the hidden field a unique id (defaults to the {@link #hiddenName}). The hiddenId + * and combo {@link Ext.Component#id id} should be different, since no two DOM + * nodes should share the same id. + */ + /** + * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is + * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured + * {@link Ext.form.Field#value value}. + */ + /** + * @cfg {String} listClass The CSS class to add to the predefined 'x-combo-list' class + * applied the dropdown list element (defaults to ''). + */ + listClass : '', + /** + * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list + * (defaults to 'x-combo-selected') */ - markInvalid : Ext.emptyFn, + selectedClass : 'x-combo-selected', /** - * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide - * @method + * @cfg {String} listEmptyText The empty text to display in the data view if no items are found. + * (defaults to '') */ - clearInvalid : Ext.emptyFn, - + listEmptyText: '', /** - * If this radio is part of a group, it will return the selected value - * @return {String} + * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always + * get the class 'x-form-trigger' and triggerClass will be appended if specified + * (defaults to 'x-form-arrow-trigger' which displays a downward arrow icon). */ - getGroupValue : function(){ - var p = this.el.up('form') || Ext.getBody(); - var c = p.child('input[name='+this.el.dom.name+']:checked', true); - return c ? c.value : null; - }, - - // private - onClick : function(){ - if(this.el.dom.checked != this.checked){ - var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']'); - els.each(function(el){ - if(el.dom.id == this.id){ - this.setValue(true); - }else{ - Ext.getCmp(el.dom.id).setValue(false); - } - }, this); - } - }, - + triggerClass : 'x-form-arrow-trigger', /** - * Sets either the checked/unchecked status of this Radio, or, if a string value - * is passed, checks a sibling Radio of the same name whose value is the value specified. - * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check. - * @return {Ext.form.Field} this + * @cfg {Boolean/String} shadow true or "sides" for the default effect, "frame" for + * 4-way shadow, and "drop" for bottom-right */ - setValue : function(v){ - if (typeof v == 'boolean') { - Ext.form.Radio.superclass.setValue.call(this, v); - } else { - var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true); - if(r){ - Ext.getCmp(r.id).setValue(true); - } - } - return this; - }, - - // private - getCheckEl: function(){ - if(this.inGroup){ - return this.el.up('.x-form-radio-group') - } - return this.el.up('form') || Ext.getBody(); - } -}); -Ext.reg('radio', Ext.form.Radio); -/** - * @class Ext.form.RadioGroup - * @extends Ext.form.CheckboxGroup - * A grouping container for {@link Ext.form.Radio} controls. - * @constructor - * Creates a new RadioGroup - * @param {Object} config Configuration options - * @xtype radiogroup - */ -Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, { - /** - * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true). - * If allowBlank = false and no items are selected at validation time, {@link @blankText} will - * be used as the error text. - */ - allowBlank : true, - /** - * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails - * (defaults to 'You must select one item in this group') - */ - blankText : 'You must select one item in this group', - - // private - defaultType : 'radio', - - // private - groupCls : 'x-form-radio-group', - - /** - * @event change - * Fires when the state of a child radio changes. - * @param {Ext.form.RadioGroup} this - * @param {Ext.form.Radio} checked The checked radio - */ - - /** - * Gets the selected {@link Ext.form.Radio} in the group, if it exists. - * @return {Ext.form.Radio} The selected radio. - */ - getValue : function(){ - var out = null; - this.eachItem(function(item){ - if(item.checked){ - out = item; - return false; - } - }); - return out; - }, - - /** - * Sets the checked radio in the group. - * @param {String/Ext.form.Radio} id The radio to check. - * @param {Boolean} value The value to set the radio. - * @return {Ext.form.RadioGroup} this - */ - onSetValue : function(id, value){ - if(arguments.length > 1){ - var f = this.getBox(id); - if(f){ - f.setValue(value); - if(f.checked){ - this.eachItem(function(item){ - if (item !== f){ - item.setValue(false); - } - }); - } - } - }else{ - this.setValueForItem(id); - } - }, - - setValueForItem : function(val){ - val = String(val).split(',')[0]; - this.eachItem(function(item){ - item.setValue(val == item.inputValue); - }); - }, - - // private - fireChecked : function(){ - if(!this.checkTask){ - this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this); - } - this.checkTask.delay(10); - }, - - // private - bufferChecked : function(){ - var out = null; - this.eachItem(function(item){ - if(item.checked){ - out = item; - return false; - } - }); - this.fireEvent('change', this, out); - }, - - onDestroy : function(){ - if(this.checkTask){ - this.checkTask.cancel(); - this.checkTask = null; - } - Ext.form.RadioGroup.superclass.onDestroy.call(this); - } - -}); - -Ext.reg('radiogroup', Ext.form.RadioGroup); -/** - * @class Ext.form.Hidden - * @extends Ext.form.Field - * A basic hidden field for storing hidden values in forms that need to be passed in the form submit. - * @constructor - * Create a new Hidden field. - * @param {Object} config Configuration options - * @xtype hidden - */ -Ext.form.Hidden = Ext.extend(Ext.form.Field, { - // private - inputType : 'hidden', - - // private - onRender : function(){ - Ext.form.Hidden.superclass.onRender.apply(this, arguments); - }, - - // private - initEvents : function(){ - this.originalValue = this.getValue(); - }, - - // These are all private overrides - setSize : Ext.emptyFn, - setWidth : Ext.emptyFn, - setHeight : Ext.emptyFn, - setPosition : Ext.emptyFn, - setPagePosition : Ext.emptyFn, - markInvalid : Ext.emptyFn, - clearInvalid : Ext.emptyFn -}); -Ext.reg('hidden', Ext.form.Hidden);/** - * @class Ext.form.BasicForm - * @extends Ext.util.Observable - *Encapsulates the DOM <form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides - * input field management, validation, submission, and form loading services.
- *By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}. - * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.
- *File Uploads
- *{@link #fileUpload File uploads} are not performed using Ajax submission, that - * is they are not performed using XMLHttpRequests. Instead the form is submitted in the standard - * manner with the DOM <form> element temporarily modified to have its - * target set to refer - * to a dynamically generated, hidden <iframe> which is inserted into the document - * but removed after the return data has been gathered.
- *The server response is parsed by the browser to create the document for the IFRAME. If the - * server is using JSON to send the return object, then the - * Content-Type header - * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.
- *Characters which are significant to an HTML parser must be sent as HTML entities, so encode - * "<" as "<", "&" as "&" etc.
- *The response text is retrieved from the document, and a fake XMLHttpRequest object - * is created containing a responseText property in order to conform to the - * requirements of event handlers and callbacks.
- *Be aware that file upload packets are sent with the content type multipart/form - * and some server technologies (notably JEE) may require some custom processing in order to - * retrieve parameter names and parameter values from the packet content.
- * @constructor - * @param {Mixed} el The form element or its id - * @param {Object} config Configuration options - */ -Ext.form.BasicForm = function(el, config){ - Ext.apply(this, config); - if(Ext.isString(this.paramOrder)){ - this.paramOrder = this.paramOrder.split(/[\s,|]/); - } + shadow : 'sides', /** - * @property items - * A {@link Ext.util.MixedCollection MixedCollection) containing all the Ext.form.Fields in this form. - * @type MixedCollection + * @cfg {String/Array} listAlign A valid anchor position value. See {@link Ext.Element#alignTo} for details + * on supported anchor positions and offsets. To specify x/y offsets as well, this value + * may be specified as an Array of {@link Ext.Element#alignTo} method arguments. + *[ 'tl-bl?', [6,0] ]
(defaults to 'tl-bl?')
*/
- this.items = new Ext.util.MixedCollection(false, function(o){
- return o.getItemId();
- });
- this.addEvents(
- /**
- * @event beforeaction
- * Fires before any action is performed. Return false to cancel the action.
- * @param {Form} this
- * @param {Action} action The {@link Ext.form.Action} to be performed
- */
- 'beforeaction',
- /**
- * @event actionfailed
- * Fires when an action fails.
- * @param {Form} this
- * @param {Action} action The {@link Ext.form.Action} that failed
- */
- 'actionfailed',
- /**
- * @event actioncomplete
- * Fires when an action is completed.
- * @param {Form} this
- * @param {Action} action The {@link Ext.form.Action} that completed
- */
- 'actioncomplete'
- );
-
- if(el){
- this.initEl(el);
- }
- Ext.form.BasicForm.superclass.constructor.call(this);
-};
-
-Ext.extend(Ext.form.BasicForm, Ext.util.Observable, {
+ listAlign : 'tl-bl?',
/**
- * @cfg {String} method
- * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
+ * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown
+ * (defaults to 300)
*/
+ maxHeight : 300,
/**
- * @cfg {DataReader} reader
- * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read
- * data when executing 'load' actions. This is optional as there is built-in
- * support for processing JSON. For additional information on using an XMLReader
- * see the example provided in examples/form/xml-form.html.
+ * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its
+ * distance to the viewport edges (defaults to 90)
*/
+ minHeight : 90,
/**
- * @cfg {DataReader} errorReader
- * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to - * read field error messages returned from 'submit' actions. This is optional - * as there is built-in support for processing JSON.
- *The Records which provide messages for the invalid Fields must use the - * Field name (or id) as the Record ID, and must contain a field called 'msg' - * which contains the error message.
- *The errorReader does not have to be a full-blown implementation of a - * DataReader. It simply needs to implement a read(xhr) function - * which returns an Array of Records in an object with the following - * structure:
-{
- records: recordArray
-}
-
+ * @cfg {String} triggerAction The action to execute when the trigger is clicked.
+ * {@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.
{@link #doQuery run the query} specified by the {@link #allQuery} config option
See also {@link #queryParam}
.
{@link #doAction doAction} options
.
+ * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
+ * {@link #typeAhead} activate (defaults to 4 if {@link #mode} = 'remote' or 0 if
+ * {@link #mode} = 'local', does not apply if
+ * {@link Ext.form.TriggerField#editable editable} = false).
*/
+ minChars : 4,
/**
- * @cfg {Boolean} fileUpload
- * Set to true if this form is a file upload.
- * File uploads are not performed using normal 'Ajax' techniques, that is they are not - * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the - * DOM <form> element temporarily modified to have its - * target set to refer - * to a dynamically generated, hidden <iframe> which is inserted into the document - * but removed after the return data has been gathered.
- *The server response is parsed by the browser to create the document for the IFRAME. If the - * server is using JSON to send the return object, then the - * Content-Type header - * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.
- *Characters which are significant to an HTML parser must be sent as HTML entities, so encode - * "<" as "<", "&" as "&" etc.
- *The response text is retrieved from the document, and a fake XMLHttpRequest object - * is created containing a responseText property in order to conform to the - * requirements of event handlers and callbacks.
- *Be aware that file upload packets are sent with the content type multipart/form - * and some server technologies (notably JEE) may require some custom processing in order to - * retrieve parameter names and parameter values from the packet content.
+ * @cfg {Boolean} autoSelect true to select the first result gathered by the data store (defaults + * to true). A false value would require a manual selection from the dropdown list to set the components value + * unless the value of ({@link #typeAheadDelay}) were true. */ + autoSelect : true, /** - * @cfg {Object} baseParams - *Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
- *Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
+ * @cfg {Boolean} typeAhead true to populate and autoselect the remainder of the text being + * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults + * to false) */ + typeAhead : false, /** - * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds). + * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and + * sending the query to filter the dropdown list (defaults to 500 if {@link #mode} = 'remote' + * or 10 if {@link #mode} = 'local') */ - timeout: 30, - + queryDelay : 500, /** - * @cfg {Object} api (Optional) If specified load and submit actions will be handled - * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}. - * Methods which have been imported by Ext.Direct can be specified here to load and submit - * forms. - * Such as the following:
-api: {
- load: App.ss.MyProfile.load,
- submit: App.ss.MyProfile.submit
-}
-
- * Load actions can use {@link #paramOrder}
or {@link #paramsAsHash}
- * to customize how the load method is invoked.
- * Submit actions will always use a standard form submit. The formHandler configuration must
- * be set on the associated server-side method which has been imported by Ext.Direct
Automatically loads the {@link #store} the first time the trigger + * is clicked. If you do not want the store to be automatically loaded the first time the trigger is + * clicked, set to 'local' and manually load the store. To force a requery of the store + * every time the trigger is clicked see {@link #lastQuery}.
ComboBox loads local data
+ *
+var combo = new Ext.form.ComboBox({
+ renderTo: document.body,
+ mode: 'local',
+ store: new Ext.data.ArrayStore({
+ id: 0,
+ fields: [
+ 'myId', // numeric value is the key
+ 'displayText'
+ ],
+ data: [[1, 'item1'], [2, 'item2']] // data is local
+ }),
+ valueField: 'myId',
+ displayField: 'displayText',
+ triggerAction: 'all'
+});
+ *
A list of params to be executed server side.
- * Defaults to undefined. Only used for the {@link #api}
- * load
configuration.
Specify the params in the order in which they must be executed on the - * server-side as either (1) an Array of String values, or (2) a String of params - * delimited by either whitespace, comma, or pipe. For example, - * any of the following would be acceptable:
-paramOrder: ['param1','param2','param3']
-paramOrder: 'param1 param2 param3'
-paramOrder: 'param1,param2,param3'
-paramOrder: 'param1|param2|param'
-
+ * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will
+ * be ignored if {@link #listWidth} has a higher value)
*/
- paramOrder: undefined,
-
+ minListWidth : 70,
/**
- * @cfg {Boolean} paramsAsHash Only used for the {@link #api}
- * load
configuration. Send parameters as a collection of named
- * arguments (defaults to false). Providing a
- * {@link #paramOrder} nullifies this configuration.
+ * @cfg {Boolean} forceSelection true to restrict the selected value to one of the values in the list,
+ * false to allow the user to set arbitrary text into the field (defaults to false)
+ */
+ forceSelection : false,
+ /**
+ * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
+ * if {@link #typeAhead} = true (defaults to 250)
+ */
+ typeAheadDelay : 250,
+ /**
+ * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
+ * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
+ * default text is used, it means there is no value set and no validation will occur on this field.
*/
- paramsAsHash: false,
/**
- * @cfg {String} waitTitle
- * The default title to show for the waiting message box (defaults to 'Please Wait...')
+ * @cfg {Boolean} lazyInit true to not initialize the list for this combo until the field is focused
+ * (defaults to true)
*/
- waitTitle: 'Please Wait...',
+ lazyInit : true,
- // private
- activeAction : null,
+ /**
+ * @cfg {Boolean} clearFilterOnReset true to clear any filters on the store (when in local mode) when reset is called
+ * (defaults to true)
+ */
+ clearFilterOnReset : true,
/**
- * @cfg {Boolean} trackResetOnLoad If set to true, {@link #reset}() resets to the last loaded
- * or {@link #setValues}() data instead of when the form was first created. Defaults to false.
+ * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
+ * If a hiddenName is specified, setting this to true will cause both the hidden field and the element to be submitted.
+ * Defaults to undefined.
*/
- trackResetOnLoad : false,
+ submitValue: undefined,
/**
- * @cfg {Boolean} standardSubmit
- * If set to true, standard HTML form submits are used instead - * of XHR (Ajax) style form submissions. Defaults to false.
- *Note: When using standardSubmit
, the
- * options
to {@link #submit}
are ignored because
- * Ext's Ajax infrastracture is bypassed. To pass extra parameters (e.g.
- * baseParams
and params
), utilize hidden fields
- * to submit extra data, for example:
-new Ext.FormPanel({
- standardSubmit: true,
- baseParams: {
- foo: 'bar'
- },
- {@link url}: 'myProcess.php',
- items: [{
- xtype: 'textfield',
- name: 'userName'
- }],
- buttons: [{
- text: 'Save',
- handler: function(){
- var fp = this.ownerCt.ownerCt,
- form = fp.getForm();
- if (form.isValid()) {
- // check if there are baseParams and if
- // hiddent items have been added already
- if (fp.baseParams && !fp.paramsAdded) {
- // add hidden items for all baseParams
- for (i in fp.baseParams) {
- fp.add({
- xtype: 'hidden',
- name: i,
- value: fp.baseParams[i]
- });
+var combo = new Ext.form.ComboBox({
+ ...
+ mode: 'remote',
+ ...
+ listeners: {
+ // delete the previous query in the beforequery event or set
+ // combo.lastQuery = null (this will reload the store the next time it expands)
+ beforequery: function(qe){
+ delete qe.combo.lastQuery;
+ }
+ }
+});
+ *
+ * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
+ * configure the combo with lastQuery=''. Example use:
+ *
+var combo = new Ext.form.ComboBox({
+ ...
+ mode: 'local',
+ triggerAction: 'all',
+ lastQuery: ''
+});
+ *
+ * @property lastQuery
+ * @type String
+ */
+
+ // private
+ initComponent : function(){
+ Ext.form.ComboBox.superclass.initComponent.call(this);
+ this.addEvents(
+ /**
+ * @event expand
+ * Fires when the dropdown list is expanded
+ * @param {Ext.form.ComboBox} combo This combo box
+ */
+ 'expand',
+ /**
+ * @event collapse
+ * Fires when the dropdown list is collapsed
+ * @param {Ext.form.ComboBox} combo This combo box
+ */
+ 'collapse',
+
+ /**
+ * @event beforeselect
+ * Fires before a list item is selected. Return false to cancel the selection.
+ * @param {Ext.form.ComboBox} combo This combo box
+ * @param {Ext.data.Record} record The data record returned from the underlying store
+ * @param {Number} index The index of the selected item in the dropdown list
+ */
+ 'beforeselect',
+ /**
+ * @event select
+ * Fires when a list item is selected
+ * @param {Ext.form.ComboBox} combo This combo box
+ * @param {Ext.data.Record} record The data record returned from the underlying store
+ * @param {Number} index The index of the selected item in the dropdown list
+ */
+ 'select',
+ /**
+ * @event beforequery
+ * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
+ * cancel property to true.
+ * @param {Object} queryEvent An object that has these properties:combo
: Ext.form.ComboBox query
: String forceAll
: Boolean cancel
: Boolean The template string, or {@link Ext.XTemplate} instance to + * use to display each item in the dropdown list. The dropdown list is displayed in a + * DataView. See {@link #view}.
+ *The default template string is:
+ '<tpl for="."><div class="x-combo-list-item">{' + this.displayField + '}</div></tpl>'
+ *
+ * Override the default value to create custom UI layouts for items in the list. + * For example:
+ '<tpl for="."><div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}</div></tpl>'
+ *
+ * The template must contain one or more substitution parameters using field + * names from the Combo's {@link #store Store}. In the example above an + *
ext:qtipattribute is added to display other fields from the Store. + *
To preserve the default visual look of list items, add the CSS class name + *
x-combo-list-itemto the template's container element. + *
Also see {@link #itemSelector} for additional details.
+ */ + this.tpl = 'A simple CSS selector (e.g. div.some-class or span:first-child) that will be + * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown + * display will be working with.
+ *Note: this setting is required if a custom XTemplate has been + * specified in {@link #tpl} which assigns a class other than
'x-combo-list-item'+ * to dropdown list items + */ + } + + /** + * The {@link Ext.DataView DataView} used to display the ComboBox's options. + * @type Ext.DataView + */ + this.view = new Ext.DataView({ + applyTo: this.innerList, + tpl: this.tpl, + singleSelect: true, + selectedClass: this.selectedClass, + itemSelector: this.itemSelector || '.' + cls + '-item', + emptyText: this.listEmptyText, + deferEmptyText: false + }); + + this.mon(this.view, { + containerclick : this.onViewClick, + click : this.onViewClick, + scope :this + }); + + this.bindStore(this.store, true); + + if(this.resizable){ + this.resizer = new Ext.Resizable(this.list, { + pinned:true, handles:'se' + }); + this.mon(this.resizer, 'resize', function(r, w, h){ + this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight; + this.listWidth = w; + this.innerList.setWidth(w - this.list.getFrameWidth('lr')); + this.restrictHeight(); + }, this); + + this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px'); + } + } + }, + + /** + *
Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.
+ * A custom implementation may be provided as a configuration option if the floating list needs to be rendered + * to a different Element. An example might be rendering the list inside a Menu so that clicking + * the list does not hide the Menu:
+var store = new Ext.data.ArrayStore({
+ autoDestroy: true,
+ fields: ['initials', 'fullname'],
+ data : [
+ ['FF', 'Fred Flintstone'],
+ ['BR', 'Barney Rubble']
+ ]
});
- *
+
+var combo = new Ext.form.ComboBox({
+ store: store,
+ displayField: 'fullname',
+ emptyText: 'Select a name...',
+ forceSelection: true,
+ getListParent: function() {
+ return this.el.up('.x-menu');
+ },
+ iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
+ mode: 'local',
+ selectOnFocus: true,
+ triggerAction: 'all',
+ typeAhead: true,
+ width: 135
+});
+
+var menu = new Ext.menu.Menu({
+ id: 'mainMenu',
+ items: [
+ combo // A Field in a Menu
+ ]
+});
+
*/
+ getListParent : function() {
+ return document.body;
+ },
+
/**
- * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
- * element by passing it or its id or mask the form itself by passing in true.
- * @type Mixed
- * @property waitMsgTarget
+ * Returns the store associated with this combo.
+ * @return {Ext.data.Store} The store
*/
+ getStore : function(){
+ return this.store;
+ },
// private
- initEl : function(el){
- this.el = Ext.get(el);
- this.id = this.el.id || Ext.id();
- if(!this.standardSubmit){
- this.el.on('submit', this.onSubmit, this);
+ bindStore : function(store, initial){
+ if(this.store && !initial){
+ if(this.store !== store && this.store.autoDestroy){
+ this.store.destroy();
+ }else{
+ this.store.un('beforeload', this.onBeforeLoad, this);
+ this.store.un('load', this.onLoad, this);
+ this.store.un('exception', this.collapse, this);
+ }
+ if(!store){
+ this.store = null;
+ if(this.view){
+ this.view.bindStore(null);
+ }
+ if(this.pageTb){
+ this.pageTb.bindStore(null);
+ }
+ }
}
- this.el.addClass('x-form');
+ if(store){
+ if(!initial) {
+ this.lastQuery = null;
+ if(this.pageTb) {
+ this.pageTb.bindStore(store);
+ }
+ }
+
+ this.store = Ext.StoreMgr.lookup(store);
+ this.store.on({
+ scope: this,
+ beforeload: this.onBeforeLoad,
+ load: this.onLoad,
+ exception: this.collapse
+ });
+
+ if(this.view){
+ this.view.bindStore(store);
+ }
+ }
+ },
+
+ reset : function(){
+ Ext.form.ComboBox.superclass.reset.call(this);
+ if(this.clearFilterOnReset && this.mode == 'local'){
+ this.store.clearFilter();
+ }
+ },
+
+ // private
+ initEvents : function(){
+ Ext.form.ComboBox.superclass.initEvents.call(this);
+
+
+ this.keyNav = new Ext.KeyNav(this.el, {
+ "up" : function(e){
+ this.inKeyMode = true;
+ this.selectPrev();
+ },
+
+ "down" : function(e){
+ if(!this.isExpanded()){
+ this.onTriggerClick();
+ }else{
+ this.inKeyMode = true;
+ this.selectNext();
+ }
+ },
+
+ "enter" : function(e){
+ this.onViewClick();
+ },
+
+ "esc" : function(e){
+ this.collapse();
+ },
+
+ "tab" : function(e){
+ this.collapse();
+ return true;
+ },
+
+ scope : this,
+
+ doRelay : function(e, h, hname){
+ if(hname == 'down' || this.scope.isExpanded()){
+ // this MUST be called before ComboBox#fireKey()
+ var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
+ if(!Ext.isIE && Ext.EventManager.useKeydown){
+ // call Combo#fireKey() for browsers which use keydown event (except IE)
+ this.scope.fireKey(e);
+ }
+ return relay;
+ }
+ return true;
+ },
+
+ forceKeyDown : true,
+ defaultEventAction: 'stopEvent'
+ });
+ this.queryDelay = Math.max(this.queryDelay || 10,
+ this.mode == 'local' ? 10 : 250);
+ this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
+ if(this.typeAhead){
+ this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
+ }
+ if(!this.enableKeyEvents){
+ this.mon(this.el, 'keyup', this.onKeyUp, this);
+ }
+ },
+
+
+ // private
+ onDestroy : function(){
+ if (this.dqTask){
+ this.dqTask.cancel();
+ this.dqTask = null;
+ }
+ this.bindStore(null);
+ Ext.destroy(
+ this.resizer,
+ this.view,
+ this.pageTb,
+ this.list
+ );
+ Ext.destroyMembers(this, 'hiddenField');
+ Ext.form.ComboBox.superclass.onDestroy.call(this);
+ },
+
+ // private
+ fireKey : function(e){
+ if (!this.isExpanded()) {
+ Ext.form.ComboBox.superclass.fireKey.call(this, e);
+ }
+ },
+
+ // private
+ onResize : function(w, h){
+ Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
+ if(this.isVisible() && this.list){
+ this.doResize(w);
+ }else{
+ this.bufferSize = w;
+ }
+ },
+
+ doResize: function(w){
+ if(!Ext.isDefined(this.listWidth)){
+ var lw = Math.max(w, this.minListWidth);
+ this.list.setWidth(lw);
+ this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+ }
+ },
+
+ // private
+ onEnable : function(){
+ Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
+ if(this.hiddenField){
+ this.hiddenField.disabled = false;
+ }
+ },
+
+ // private
+ onDisable : function(){
+ Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
+ if(this.hiddenField){
+ this.hiddenField.disabled = true;
+ }
+ },
+
+ // private
+ onBeforeLoad : function(){
+ if(!this.hasFocus){
+ return;
+ }
+ this.innerList.update(this.loadingText ?
+ 'Returns true if any fields in this form have changed from their original values.
- *Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the - * Fields' original values are updated when the values are loaded by {@link #setValues} - * or {@link #loadRecord}.
- * @return Boolean + * Clears any text/value currently set in the field */ - isDirty : function(){ - var dirty = false; - this.items.each(function(f){ - if(f.isDirty()){ - dirty = true; - return false; - } - }); - return dirty; + clearValue : function(){ + if(this.hiddenField){ + this.hiddenField.value = ''; + } + this.setRawValue(''); + this.lastSelectionText = ''; + this.applyEmptyText(); + this.value = ''; }, /** - * Performs a predefined action ({@link Ext.form.Action.Submit} or - * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action} - * to perform application-specific processing. - * @param {String/Object} actionName The name of the predefined action type, - * or instance of {@link Ext.form.Action} to perform. - * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}. - * All of the config options listed below are supported by both the - * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load} - * actions unless otherwise noted (custom actions could also accept - * other config options):The params to pass - * (defaults to the form's baseParams, or none if not defined)
- *Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
Note: this is ignored when using the {@link #standardSubmit} option.
- *The following code:
-myFormPanel.getForm().submit({
- clientValidation: true,
- url: 'updateConsignment.php',
- params: {
- newStatus: 'delivered'
+ // private
+ findRecord : function(prop, value){
+ var record;
+ if(this.store.getCount() > 0){
+ this.store.each(function(r){
+ if(r.data[prop] == value){
+ record = r;
+ return false;
+ }
+ });
+ }
+ return record;
},
- success: function(form, action) {
- Ext.Msg.alert('Success', action.result.msg);
+
+ // private
+ onViewMove : function(e, t){
+ this.inKeyMode = false;
},
- failure: function(form, action) {
- switch (action.failureType) {
- case Ext.form.Action.CLIENT_INVALID:
- Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
- break;
- case Ext.form.Action.CONNECT_FAILURE:
- Ext.Msg.alert('Failure', 'Ajax communication failed');
- break;
- case Ext.form.Action.SERVER_INVALID:
- Ext.Msg.alert('Failure', action.result.msg);
- }
- }
-});
-
- * would process the following server response for a successful submission:
-{
- "success":true, // note this is Boolean, not string
- "msg":"Consignment updated"
-}
-
- * and the following server response for a failed submission:
-{
- "success":false, // note this is Boolean, not string
- "msg":"You do not have permission to perform this operation"
-}
-
- * @return {BasicForm} this
- */
- submit : function(options){
- if(this.standardSubmit){
- var v = this.isValid();
- if(v){
- var el = this.el.dom;
- if(this.url && Ext.isEmpty(el.action)){
- el.action = this.url;
- }
- el.submit();
- }
- return v;
+
+ // private
+ onViewOver : function(e, t){
+ if(this.inKeyMode){ // prevent key nav and mouse over conflicts
+ return;
}
- var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
- this.doAction(submitAction, options);
- return this;
+ var item = this.view.findItemFromChild(t);
+ if(item){
+ var index = this.view.indexOf(item);
+ this.select(index, false);
+ }
+ },
+
+ // private
+ onViewClick : function(doFocus){
+ var index = this.view.getSelectedIndexes()[0],
+ s = this.store,
+ r = s.getAt(index);
+ if(r){
+ this.onSelect(r, index);
+ }else {
+ this.collapse();
+ }
+ if(doFocus !== false){
+ this.el.focus();
+ }
+ },
+
+
+ // private
+ restrictHeight : function(){
+ this.innerList.dom.style.height = '';
+ var inner = this.innerList.dom,
+ pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,
+ h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
+ ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
+ hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
+ space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;
+
+ h = Math.min(h, space, this.maxHeight);
+
+ this.innerList.setHeight(h);
+ this.list.beginUpdate();
+ this.list.setHeight(h+pad);
+ this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
+ this.list.endUpdate();
},
/**
- * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
- * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
- * @return {BasicForm} this
+ * Returns true if the dropdown list is expanded, else false.
*/
- load : function(options){
- var loadAction = String.format('{0}load', this.api ? 'direct' : '');
- this.doAction(loadAction, options);
- return this;
+ isExpanded : function(){
+ return this.list && this.list.isVisible();
},
/**
- * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
- * @param {Record} record The record to edit
- * @return {BasicForm} this
+ * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
+ * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+ * @param {String} value The data value of the item to select
+ * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+ * selected item if it is not currently in view (defaults to true)
+ * @return {Boolean} True if the value matched an item in the list, else false
*/
- updateRecord : function(record){
- record.beginEdit();
- var fs = record.fields;
- fs.each(function(f){
- var field = this.findField(f.name);
- if(field){
- record.set(f.name, field.getValue());
+ selectByValue : function(v, scrollIntoView){
+ if(!Ext.isEmpty(v, true)){
+ var r = this.findRecord(this.valueField || this.displayField, v);
+ if(r){
+ this.select(this.store.indexOf(r), scrollIntoView);
+ return true;
}
- }, this);
- record.endEdit();
- return this;
+ }
+ return false;
},
/**
- * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
- * {@link Ext.data.Record#data record data}.
- * See also {@link #trackResetOnLoad}.
- * @param {Record} record The record to load
- * @return {BasicForm} this
+ * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
+ * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+ * @param {Number} index The zero-based index of the list item to select
+ * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+ * selected item if it is not currently in view (defaults to true)
*/
- loadRecord : function(record){
- this.setValues(record.data);
- return this;
+ select : function(index, scrollIntoView){
+ this.selectedIndex = index;
+ this.view.select(index);
+ if(scrollIntoView !== false){
+ var el = this.view.getNode(index);
+ if(el){
+ this.innerList.scrollChildIntoView(el, false);
+ }
+ }
+
+ },
+
+ // private
+ selectNext : function(){
+ var ct = this.store.getCount();
+ if(ct > 0){
+ if(this.selectedIndex == -1){
+ this.select(0);
+ }else if(this.selectedIndex < ct-1){
+ this.select(this.selectedIndex+1);
+ }
+ }
+ },
+
+ // private
+ selectPrev : function(){
+ var ct = this.store.getCount();
+ if(ct > 0){
+ if(this.selectedIndex == -1){
+ this.select(0);
+ }else if(this.selectedIndex !== 0){
+ this.select(this.selectedIndex-1);
+ }
+ }
+ },
+
+ // private
+ onKeyUp : function(e){
+ var k = e.getKey();
+ if(this.editable !== false && this.readOnly !== true && (k == e.BACKSPACE || !e.isSpecialKey())){
+
+ this.lastKey = k;
+ this.dqTask.delay(this.queryDelay);
+ }
+ Ext.form.ComboBox.superclass.onKeyUp.call(this, e);
+ },
+
+ // private
+ validateBlur : function(){
+ return !this.list || !this.list.isVisible();
},
// private
- beforeAction : function(action){
- var o = action.options;
- if(o.waitMsg){
- if(this.waitMsgTarget === true){
- this.el.mask(o.waitMsg, 'x-mask-loading');
- }else if(this.waitMsgTarget){
- this.waitMsgTarget = Ext.get(this.waitMsgTarget);
- this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
- }else{
- Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
- }
- }
+ initQuery : function(){
+ this.doQuery(this.getRawValue());
},
// private
- afterAction : function(action, success){
- this.activeAction = null;
- var o = action.options;
- if(o.waitMsg){
- if(this.waitMsgTarget === true){
- this.el.unmask();
- }else if(this.waitMsgTarget){
- this.waitMsgTarget.unmask();
+ beforeBlur : function(){
+ this.assertValue();
+ },
+
+ // private
+ postBlur : function(){
+ Ext.form.ComboBox.superclass.postBlur.call(this);
+ this.collapse();
+ this.inKeyMode = false;
+ },
+
+ /**
+ * Execute a query to filter the dropdown list. Fires the {@link #beforequery} event prior to performing the
+ * query allowing the query action to be canceled if needed.
+ * @param {String} query The SQL query to execute
+ * @param {Boolean} forceAll true to force the query to execute even if there are currently fewer
+ * characters in the field than the minimum specified by the {@link #minChars} config option. It
+ * also clears any filter previously saved in the current store (defaults to false)
+ */
+ doQuery : function(q, forceAll){
+ q = Ext.isEmpty(q) ? '' : q;
+ var qe = {
+ query: q,
+ forceAll: forceAll,
+ combo: this,
+ cancel:false
+ };
+ if(this.fireEvent('beforequery', qe)===false || qe.cancel){
+ return false;
+ }
+ q = qe.query;
+ forceAll = qe.forceAll;
+ if(forceAll === true || (q.length >= this.minChars)){
+ if(this.lastQuery !== q){
+ this.lastQuery = q;
+ if(this.mode == 'local'){
+ this.selectedIndex = -1;
+ if(forceAll){
+ this.store.clearFilter();
+ }else{
+ this.store.filter(this.displayField, q);
+ }
+ this.onLoad();
+ }else{
+ this.store.baseParams[this.queryParam] = q;
+ this.store.load({
+ params: this.getParams(q)
+ });
+ this.expand();
+ }
}else{
- Ext.MessageBox.updateProgress(1);
- Ext.MessageBox.hide();
+ this.selectedIndex = -1;
+ this.onLoad();
}
}
- if(success){
- if(o.reset){
- this.reset();
- }
- Ext.callback(o.success, o.scope, [this, action]);
- this.fireEvent('actioncomplete', this, action);
- }else{
- Ext.callback(o.failure, o.scope, [this, action]);
- this.fireEvent('actionfailed', this, action);
+ },
+
+ // private
+ getParams : function(q){
+ var p = {};
+ //p[this.queryParam] = q;
+ if(this.pageSize){
+ p.start = 0;
+ p.limit = this.pageSize;
}
+ return p;
},
/**
- * Find a {@link Ext.form.Field} in this form.
- * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
- * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
- * @return Field
+ * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.
*/
- findField : function(id){
- var field = this.items.get(id);
- if(!Ext.isObject(field)){
- this.items.each(function(f){
- if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
- field = f;
- return false;
- }
- });
+ collapse : function(){
+ if(!this.isExpanded()){
+ return;
}
- return field || null;
+ this.list.hide();
+ Ext.getDoc().un('mousewheel', this.collapseIf, this);
+ Ext.getDoc().un('mousedown', this.collapseIf, this);
+ this.fireEvent('collapse', this);
},
+ // private
+ collapseIf : function(e){
+ if(!e.within(this.wrap) && !e.within(this.list)){
+ this.collapse();
+ }
+ },
/**
- * Mark fields in this form invalid in bulk.
- * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
- * @return {BasicForm} this
+ * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.
*/
- markInvalid : function(errors){
- if(Ext.isArray(errors)){
- for(var i = 0, len = errors.length; i < len; i++){
- var fieldError = errors[i];
- var f = this.findField(fieldError.id);
- if(f){
- f.markInvalid(fieldError.msg);
- }
- }
- }else{
- var field, id;
- for(id in errors){
- if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
- field.markInvalid(errors[id]);
- }
- }
+ expand : function(){
+ if(this.isExpanded() || !this.hasFocus){
+ return;
}
- return this;
+ if(this.bufferSize){
+ this.doResize(this.bufferSize);
+ delete this.bufferSize;
+ }
+ this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
+ this.list.show();
+ if(Ext.isGecko2){
+ this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
+ }
+ this.mon(Ext.getDoc(), {
+ scope: this,
+ mousewheel: this.collapseIf,
+ mousedown: this.collapseIf
+ });
+ this.fireEvent('expand', this);
},
/**
- * Set values for fields in this form in bulk.
- * @param {Array/Object} values Either an array in the form:
-[{id:'clientName', value:'Fred. Olsen Lines'},
- {id:'portOfLoading', value:'FXT'},
- {id:'portOfDischarge', value:'OSL'} ]
- * or an object hash of the form:
-{
- clientName: 'Fred. Olsen Lines',
- portOfLoading: 'FXT',
- portOfDischarge: 'OSL'
-}
- * @return {BasicForm} this
+ * @method onTriggerClick
+ * @hide
*/
- setValues : function(values){
- if(Ext.isArray(values)){ // array of objects
- for(var i = 0, len = values.length; i < len; i++){
- var v = values[i];
- var f = this.findField(v.id);
- if(f){
- f.setValue(v.value);
- if(this.trackResetOnLoad){
- f.originalValue = f.getValue();
- }
- }
- }
- }else{ // object hash
- var field, id;
- for(id in values){
- if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
- field.setValue(values[id]);
- if(this.trackResetOnLoad){
- field.originalValue = field.getValue();
- }
- }
+ // private
+ // Implements the default empty TriggerField.onTriggerClick function
+ onTriggerClick : function(){
+ if(this.readOnly || this.disabled){
+ return;
+ }
+ if(this.isExpanded()){
+ this.collapse();
+ this.el.focus();
+ }else {
+ this.onFocus({});
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
}
+ this.el.focus();
}
- return this;
- },
+ }
/**
- * Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit. - * If multiple fields exist with the same name they are returned as an array.
- *Note: The values are collected from all enabled HTML input elements within the form, not from - * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the - * value can potentially be the emptyText of a field.
- * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object) - * @return {String/Object} + * @hide + * @method autoSize */ - getValues : function(asString){ - var fs = Ext.lib.Ajax.serializeForm(this.el.dom); - if(asString === true){ - return fs; - } - return Ext.urlDecode(fs); + /** + * @cfg {Boolean} grow @hide + */ + /** + * @cfg {Number} growMin @hide + */ + /** + * @cfg {Number} growMax @hide + */ + +}); +Ext.reg('combo', Ext.form.ComboBox); +/** + * @class Ext.form.Checkbox + * @extends Ext.form.Field + * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields. + * @constructor + * Creates a new Checkbox + * @param {Object} config Configuration options + * @xtype checkbox + */ +Ext.form.Checkbox = Ext.extend(Ext.form.Field, { + /** + * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined) + */ + focusClass : undefined, + /** + * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to 'x-form-field') + */ + fieldClass : 'x-form-field', + /** + * @cfg {Boolean} checked true if the checkbox should render initially checked (defaults to false) + */ + checked : false, + /** + * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to + * {tag: 'input', type: 'checkbox', autocomplete: 'off'}) + */ + defaultAutoCreate : { tag: 'input', type: 'checkbox', autocomplete: 'off'}, + /** + * @cfg {String} boxLabel The text that appears beside the checkbox + */ + /** + * @cfg {String} inputValue The value that should go into the generated input element's value attribute + */ + /** + * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of + * handling the check event). The handler is passed the following parameters: + *You will not usually call this function. In order to be rendered, a Field must be added - * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}. - * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's - * collection.
- * @param {Field} field1 - * @param {Field} field2 (optional) - * @param {Field} etc (optional) - * @return {BasicForm} this + * @hide + * Overridden and disabled. The editor element does not support standard valid/invalid marking. + * @method */ - add : function(){ - this.items.addAll(Array.prototype.slice.call(arguments, 0)); - return this; + clearInvalid : Ext.emptyFn, + + // private + onRender : function(ct, position){ + Ext.form.Checkbox.superclass.onRender.call(this, ct, position); + if(this.inputValue !== undefined){ + this.el.dom.value = this.inputValue; + } + this.wrap = this.el.wrap({cls: 'x-form-check-wrap'}); + if(this.boxLabel){ + this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel}); + } + if(this.checked){ + this.setValue(true); + }else{ + this.checked = this.el.dom.checked; + } + // Need to repaint for IE, otherwise positioning is broken + if(Ext.isIE){ + this.wrap.repaint(); + } + this.resizeEl = this.positionEl = this.wrap; }, + // private + onDestroy : function(){ + Ext.destroy(this.wrap); + Ext.form.Checkbox.superclass.onDestroy.call(this); + }, - /** - * Removes a field from the items collection (does NOT remove its markup). - * @param {Field} field - * @return {BasicForm} this - */ - remove : function(field){ - this.items.remove(field); - return this; + // private + initValue : function() { + this.originalValue = this.getValue(); }, /** - * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm, - * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id. - * @return {BasicForm} this + * Returns the checked state of the checkbox. + * @return {Boolean} True if checked, else false */ - render : function(){ - this.items.each(function(f){ - if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists - f.applyToMarkup(f.id); - } - }); - return this; + getValue : function(){ + if(this.rendered){ + return this.el.dom.checked; + } + return this.checked; }, - /** - * Calls {@link Ext#apply} for all fields in this form with the passed object. - * @param {Object} values - * @return {BasicForm} this - */ - applyToFields : function(o){ - this.items.each(function(f){ - Ext.apply(f, o); - }); - return this; + // private + onClick : function(){ + if(this.el.dom.checked != this.checked){ + this.setValue(this.el.dom.checked); + } }, /** - * Calls {@link Ext#applyIf} for all field in this form with the passed object. - * @param {Object} values - * @return {BasicForm} this + * Sets the checked state of the checkbox, fires the 'check' event, and calls a + *{@link #handler}
(if configured).
+ * @param {Boolean/String} checked The following values will check the checkbox:
+ * true, 'true', '1', or 'on'
. Any other value will uncheck the checkbox.
+ * @return {Ext.form.Field} this
*/
- applyIfToFields : function(o){
- this.items.each(function(f){
- Ext.applyIf(f, o);
- });
- return this;
- },
-
- callFieldMethod : function(fnName, args){
- args = args || [];
- this.items.each(function(f){
- if(Ext.isFunction(f[fnName])){
- f[fnName].apply(f, args);
+ setValue : function(v){
+ var checked = this.checked ;
+ this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
+ if(this.rendered){
+ this.el.dom.checked = this.checked;
+ this.el.dom.defaultChecked = this.checked;
+ }
+ if(checked != this.checked){
+ this.fireEvent('check', this, this.checked);
+ if(this.handler){
+ this.handler.call(this.scope || this, this, this.checked);
}
- });
+ }
return this;
}
});
-
-// back compat
-Ext.BasicForm = Ext.form.BasicForm;/**
- * @class Ext.form.FormPanel
- * @extends Ext.Panel
- * Standard form container.
- * - *Layout
- *By default, FormPanel is configured with layout:'form' to use an {@link Ext.layout.FormLayout} - * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers - * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the - * {@link Ext.layout.FormLayout} layout manager.
- * - *BasicForm
- *Although not listed as configuration options of FormPanel, the FormPanel class accepts all - * of the config options required to configure its internal {@link Ext.form.BasicForm} for: - *
Note: If subclassing FormPanel, any configuration options for the BasicForm must be applied to - * the initialConfig property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm} - * configuration settings to this will not affect the BasicForm's configuration.
- * - *Form Validation
- *For information on form validation see the following:
- *Form Submission
- *By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser - * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the - * {@link Ext.form.BasicForm#standardSubmit standardSubmit} option.
- * +Ext.reg('checkbox', Ext.form.Checkbox); +/** + * @class Ext.form.CheckboxGroup + * @extends Ext.form.Field + *A grouping container for {@link Ext.form.Checkbox} controls.
+ *Sample usage:
+ *
+var myCheckboxGroup = new Ext.form.CheckboxGroup({
+ id:'myGroup',
+ xtype: 'checkboxgroup',
+ fieldLabel: 'Single Column',
+ itemCls: 'x-check-group-alt',
+ // Put all controls in a single column with width 100%
+ columns: 1,
+ items: [
+ {boxLabel: 'Item 1', name: 'cb-col-1'},
+ {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
+ {boxLabel: 'Item 3', name: 'cb-col-3'}
+ ]
+});
+ *
* @constructor
+ * Creates a new CheckboxGroup
* @param {Object} config Configuration options
- * @xtype form
+ * @xtype checkboxgroup
*/
-Ext.FormPanel = Ext.extend(Ext.Panel, {
- /**
- * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).
- */
- /**
- * @cfg {Boolean} hideLabels
- * true to hide field labels by default (sets display:none). Defaults to - * false.
- *Also see {@link Ext.Component}.{@link Ext.Component#hideLabel hideLabel}. - */ +Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, { /** - * @cfg {Number} labelPad - * The default padding in pixels for field labels (defaults to 5). labelPad only - * applies if {@link #labelWidth} is also specified, otherwise it will be ignored. + * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects + * to arrange in the group. */ /** - * @cfg {String} labelSeparator - * See {@link Ext.Component}.{@link Ext.Component#labelSeparator labelSeparator} + * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped + * checkbox/radio controls using automatic layout. This config can take several types of values: + *
The controls will be rendered one per column on one row and the width + * of each column will be evenly distributed based on the width of the overall field container. This is the default.
If you specific a number (e.g., 3) that number of columns will be + * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.
You can also specify an array of column widths, mixing integer + * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will + * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float + * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field + * container you should do so.
Buttons in the footer of a FormPanel may be configured with the option formBind: true. This causes - * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on - * the form's valid/invalid state.
+ * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must + * select at least one item in this group") */ + blankText : "You must select at least one item in this group", + // private + defaultType : 'checkbox', - /** - * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75). - */ - minButtonWidth : 75, + // private + groupCls : 'x-form-check-group', - /** - * @cfg {String} labelAlign The label alignment value used for the text-align specification - * for the container. Valid values are "left", "top" or "right" - * (defaults to "left"). This property cascades to child containers and can be - * overridden on any child container (e.g., a fieldset can specify a different labelAlign - * for its fields). - */ - labelAlign : 'left', + // private + initComponent: function(){ + this.addEvents( + /** + * @event change + * Fires when the state of a child checkbox changes. + * @param {Ext.form.CheckboxGroup} this + * @param {Array} checked An array containing the checked boxes. + */ + 'change' + ); + this.on('change', this.validate, this); + Ext.form.CheckboxGroup.superclass.initComponent.call(this); + }, - /** - * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and - * regularly fires the {@link #clientvalidation} event passing that state.When monitoring valid state, the FormPanel enables/disables any of its configured
- * {@link #buttons} which have been configured with formBind: true
depending
- * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to false
+// call with name and value
+myCheckboxGroup.setValue('cb-col-1', true);
+// call with an array of boolean values
+myCheckboxGroup.setValue([true, false, false]);
+// call with an object literal specifying item:value pairs
+myCheckboxGroup.setValue({
+ 'cb-col-2': false,
+ 'cb-col-3': true
+});
+// use comma separated string to set items with name to true (checked)
+myCheckboxGroup.setValue('cb-col-1,cb-col-3');
+ *
+ * See {@link Ext.form.Checkbox#setValue} for additional information.
+ * @param {Mixed} id The checkbox to check, or as described by example shown.
+ * @param {Boolean} value (optional) The value to set the item.
+ * @return {Ext.form.CheckboxGroup} this
*/
- getForm : function(){
- return this.form;
+ setValue: function(){
+ if(this.rendered){
+ this.onSetValue.apply(this, arguments);
+ }else{
+ this.buffered = true;
+ this.value = arguments;
+ }
+ return this;
},
- // private
- onRender : function(ct, position){
- this.initFields();
- Ext.FormPanel.superclass.onRender.call(this, ct, position);
- this.form.initEl(this.body);
+ onSetValue: function(id, value){
+ if(arguments.length == 1){
+ if(Ext.isArray(id)){
+ // an array of boolean values
+ Ext.each(id, function(val, idx){
+ var item = this.items.itemAt(idx);
+ if(item){
+ item.setValue(val);
+ }
+ }, this);
+ }else if(Ext.isObject(id)){
+ // set of name/value pairs
+ for(var i in id){
+ var f = this.getBox(i);
+ if(f){
+ f.setValue(id[i]);
+ }
+ }
+ }else{
+ this.setValueForItem(id);
+ }
+ }else{
+ var f = this.getBox(id);
+ if(f){
+ f.setValue(value);
+ }
+ }
},
-
+
// private
- beforeDestroy : function(){
- this.stopMonitoring();
- Ext.FormPanel.superclass.beforeDestroy.call(this);
- /*
- * Clear the items here to prevent them being destroyed again.
- * Don't move this behaviour to BasicForm because it can be used
- * on it's own.
- */
- this.form.items.clear();
- Ext.destroy(this.form);
+ beforeDestroy: function(){
+ Ext.destroy(this.panel);
+ Ext.form.CheckboxGroup.superclass.beforeDestroy.call(this);
+
},
- // Determine if a Component is usable as a form Field.
- isField : function(c) {
- return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
+ setValueForItem : function(val){
+ val = String(val).split(',');
+ this.eachItem(function(item){
+ if(val.indexOf(item.inputValue)> -1){
+ item.setValue(true);
+ }
+ });
},
// private
- initEvents : function(){
- Ext.FormPanel.superclass.initEvents.call(this);
- // Listeners are required here to catch bubbling events from children.
- this.on({
- scope: this,
- add: this.onAddEvent,
- remove: this.onRemoveEvent
+ getBox : function(id){
+ var box = null;
+ this.eachItem(function(f){
+ if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
+ box = f;
+ return false;
+ }
});
- if(this.monitorValid){ // initialize after render
- this.startMonitoring();
- }
+ return box;
},
-
+
+ /**
+ * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
+ * @return {Array} An array of the selected checkboxes.
+ */
+ getValue : function(){
+ var out = [];
+ this.eachItem(function(item){
+ if(item.checked){
+ out.push(item);
+ }
+ });
+ return out;
+ },
+
// private
- onAdd: function(c){
- Ext.FormPanel.superclass.onAdd.call(this, c);
- this.processAdd(c);
+ eachItem: function(fn){
+ if(this.items && this.items.each){
+ this.items.each(fn, this);
+ }
},
-
+
+ /**
+ * @cfg {String} name
+ * @hide
+ */
+
+ /**
+ * @method getRawValue
+ * @hide
+ */
+ getRawValue : Ext.emptyFn,
+
+ /**
+ * @method setRawValue
+ * @hide
+ */
+ setRawValue : Ext.emptyFn
+
+});
+
+Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
+/**
+ * @class Ext.form.Radio
+ * @extends Ext.form.Checkbox
+ * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
+ * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
+ * @constructor
+ * Creates a new Radio
+ * @param {Object} config Configuration options
+ * @xtype radio
+ */
+Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
+ inputType: 'radio',
+
+ /**
+ * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+ * @method
+ */
+ markInvalid : Ext.emptyFn,
+ /**
+ * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+ * @method
+ */
+ clearInvalid : Ext.emptyFn,
+
+ /**
+ * If this radio is part of a group, it will return the selected value
+ * @return {String}
+ */
+ getGroupValue : function(){
+ var p = this.el.up('form') || Ext.getBody();
+ var c = p.child('input[name='+this.el.dom.name+']:checked', true);
+ return c ? c.value : null;
+ },
+
// private
- onAddEvent: function(ct, c){
- if(ct !== this){
- this.processAdd(c);
+ onClick : function(){
+ if(this.el.dom.checked != this.checked){
+ var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
+ els.each(function(el){
+ if(el.dom.id == this.id){
+ this.setValue(true);
+ }else{
+ Ext.getCmp(el.dom.id).setValue(false);
+ }
+ }, this);
+ }
+ },
+
+ /**
+ * Sets either the checked/unchecked status of this Radio, or, if a string value
+ * is passed, checks a sibling Radio of the same name whose value is the value specified.
+ * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
+ * @return {Ext.form.Field} this
+ */
+ setValue : function(v){
+ if (typeof v == 'boolean') {
+ Ext.form.Radio.superclass.setValue.call(this, v);
+ } else if (this.rendered) {
+ var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
+ if(r){
+ Ext.getCmp(r.id).setValue(true);
+ }
}
+ return this;
},
+
+ // private
+ getCheckEl: function(){
+ if(this.inGroup){
+ return this.el.up('.x-form-radio-group')
+ }
+ return this.el.up('form') || Ext.getBody();
+ }
+});
+Ext.reg('radio', Ext.form.Radio);
+/**
+ * @class Ext.form.RadioGroup
+ * @extends Ext.form.CheckboxGroup
+ * A grouping container for {@link Ext.form.Radio} controls.
+ * @constructor
+ * Creates a new RadioGroup
+ * @param {Object} config Configuration options
+ * @xtype radiogroup
+ */
+Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {
+ /**
+ * @cfg {Array} items An Array of {@link Ext.form.Radio Radio}s or Radio config objects
+ * to arrange in the group.
+ */
+ /**
+ * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
+ * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
+ * be used as the error text.
+ */
+ allowBlank : true,
+ /**
+ * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
+ * (defaults to 'You must select one item in this group')
+ */
+ blankText : 'You must select one item in this group',
// private
- processAdd : function(c){
- // If a single form Field, add it
- if(this.isField(c)){
- this.form.add(c);
- // If a Container, add any Fields it might contain
- }else if(c.findBy){
- this.applySettings(c);
- this.form.add.apply(this.form, c.findBy(this.isField));
- }
- },
+ defaultType : 'radio',
// private
- onRemove: function(c){
- Ext.FormPanel.superclass.onRemove.call(this, c);
- this.processRemove(c);
- },
+ groupCls : 'x-form-radio-group',
- onRemoveEvent: function(ct, c){
- if(ct !== this){
- this.processRemove(c);
- }
- },
-
- // private
- processRemove : function(c){
- // If a single form Field, remove it
- if(this.isField(c)){
- this.form.remove(c);
- // If a Container, remove any Fields it might contain
- }else if(c.findBy){
- Ext.each(c.findBy(this.isField), this.form.remove, this.form);
- }
- },
-
/**
- * Starts monitoring of the valid state of this form. Usually this is done by passing the config
- * option "monitorValid"
+ * @event change
+ * Fires when the state of a child radio changes.
+ * @param {Ext.form.RadioGroup} this
+ * @param {Ext.form.Radio} checked The checked radio
*/
- startMonitoring : function(){
- if(!this.validTask){
- this.validTask = new Ext.util.TaskRunner();
- this.validTask.start({
- run : this.bindHandler,
- interval : this.monitorPoll || 200,
- scope: this
- });
- }
- },
-
+
/**
- * Stops monitoring of the valid state of this form
+ * Gets the selected {@link Ext.form.Radio} in the group, if it exists.
+ * @return {Ext.form.Radio} The selected radio.
*/
- stopMonitoring : function(){
- if(this.validTask){
- this.validTask.stopAll();
- this.validTask = null;
- }
+ getValue : function(){
+ var out = null;
+ this.eachItem(function(item){
+ if(item.checked){
+ out = item;
+ return false;
+ }
+ });
+ return out;
},
-
+
/**
- * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.
- * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)
- */
- load : function(){
- this.form.load.apply(this.form, arguments);
- },
-
- // private
- onDisable : function(){
- Ext.FormPanel.superclass.onDisable.call(this);
- if(this.form){
- this.form.items.each(function(){
- this.disable();
- });
+ * Sets the checked radio in the group.
+ * @param {String/Ext.form.Radio} id The radio to check.
+ * @param {Boolean} value The value to set the radio.
+ * @return {Ext.form.RadioGroup} this
+ */
+ onSetValue : function(id, value){
+ if(arguments.length > 1){
+ var f = this.getBox(id);
+ if(f){
+ f.setValue(value);
+ if(f.checked){
+ this.eachItem(function(item){
+ if (item !== f){
+ item.setValue(false);
+ }
+ });
+ }
+ }
+ }else{
+ this.setValueForItem(id);
}
},
-
+
+ setValueForItem : function(val){
+ val = String(val).split(',')[0];
+ this.eachItem(function(item){
+ item.setValue(val == item.inputValue);
+ });
+ },
+
// private
- onEnable : function(){
- Ext.FormPanel.superclass.onEnable.call(this);
- if(this.form){
- this.form.items.each(function(){
- this.enable();
- });
+ fireChecked : function(){
+ if(!this.checkTask){
+ this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);
}
+ this.checkTask.delay(10);
},
-
+
// private
- bindHandler : function(){
- var valid = true;
- this.form.items.each(function(f){
- if(!f.isValid(true)){
- valid = false;
+ bufferChecked : function(){
+ var out = null;
+ this.eachItem(function(item){
+ if(item.checked){
+ out = item;
return false;
}
});
- if(this.fbar){
- var fitems = this.fbar.items.items;
- for(var i = 0, len = fitems.length; i < len; i++){
- var btn = fitems[i];
- if(btn.formBind === true && btn.disabled === valid){
- btn.setDisabled(!valid);
- }
- }
+ this.fireEvent('change', this, out);
+ },
+
+ onDestroy : function(){
+ if(this.checkTask){
+ this.checkTask.cancel();
+ this.checkTask = null;
}
- this.fireEvent('clientvalidation', this, valid);
+ Ext.form.RadioGroup.superclass.onDestroy.call(this);
}
-});
-Ext.reg('form', Ext.FormPanel);
-Ext.form.FormPanel = Ext.FormPanel;
+});
+Ext.reg('radiogroup', Ext.form.RadioGroup);
/**
- * @class Ext.form.FieldSet
- * @extends Ext.Panel
- * Standard container used for grouping items within a {@link Ext.form.FormPanel form}.
- *
-var form = new Ext.FormPanel({
- title: 'Simple Form with FieldSets',
- labelWidth: 75, // label settings here cascade unless overridden
- url: 'save-form.php',
- frame:true,
- bodyStyle:'padding:5px 5px 0',
- width: 700,
- renderTo: document.body,
- layout:'column', // arrange items in columns
- defaults: { // defaults applied to items
- layout: 'form',
- border: false,
- bodyStyle: 'padding:4px'
- },
- items: [{
- // Fieldset in Column 1
- xtype:'fieldset',
- columnWidth: 0.5,
- title: 'Fieldset 1',
- collapsible: true,
- autoHeight:true,
- defaults: {
- anchor: '-20' // leave room for error icon
- },
- defaultType: 'textfield',
- items :[{
- fieldLabel: 'Field 1'
- }, {
- fieldLabel: 'Field 2'
- }, {
- fieldLabel: 'Field 3'
- }
- ]
- },{
- // Fieldset in Column 2 - Panel inside
- xtype:'fieldset',
- title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header
- autoHeight:true,
- columnWidth: 0.5,
- checkboxToggle: true,
- collapsed: true, // fieldset initially collapsed
- layout:'anchor',
- items :[{
- xtype: 'panel',
- anchor: '100%',
- title: 'Panel inside a fieldset',
- frame: true,
- height: 100
- }]
- }]
-});
- *
+ * @class Ext.form.Hidden
+ * @extends Ext.form.Field
+ * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.
* @constructor
+ * Create a new Hidden field.
* @param {Object} config Configuration options
- * @xtype fieldset
+ * @xtype hidden
*/
-Ext.form.FieldSet = Ext.extend(Ext.Panel, {
- /**
- * @cfg {Mixed} checkboxToggle true to render a checkbox into the fieldset frame just
- * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults
- * to false).
- * A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox. - * If true is specified, the default DomHelper config object used to create the element - * is:
- * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}
- *
- */
- /**
- * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if {@link #checkboxToggle} = true
- * (defaults to '[checkbox id]-checkbox').
- */
- /**
- * @cfg {Boolean} collapsible
- * true to make the fieldset collapsible and have the expand/collapse toggle button automatically
- * rendered into the legend element, false to keep the fieldset statically sized with no collapse
- * button (defaults to false). Another option is to configure {@link #checkboxToggle}.
- */
- /**
- * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
- */
- /**
- * @cfg {String} itemCls A css class to apply to the x-form-item of fields (see
- * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).
- * This property cascades to child containers.
- */
- /**
- * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to 'x-fieldset').
- */
- baseCls : 'x-fieldset',
- /**
- * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to 'form').
- */
- layout : 'form',
- /**
- * @cfg {Boolean} animCollapse
- * true to animate the transition when the panel is collapsed, false to skip the
- * animation (defaults to false).
- */
- animCollapse : false,
-
+Ext.form.Hidden = Ext.extend(Ext.form.Field, {
// private
- onRender : function(ct, position){
- if(!this.el){
- this.el = document.createElement('fieldset');
- this.el.id = this.id;
- if (this.title || this.header || this.checkboxToggle) {
- this.el.appendChild(document.createElement('legend')).className = 'x-fieldset-header';
- }
- }
-
- Ext.form.FieldSet.superclass.onRender.call(this, ct, position);
-
- if(this.checkboxToggle){
- var o = typeof this.checkboxToggle == 'object' ?
- this.checkboxToggle :
- {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};
- this.checkbox = this.header.insertFirst(o);
- this.checkbox.dom.checked = !this.collapsed;
- this.mon(this.checkbox, 'click', this.onCheckClick, this);
- }
- },
+ inputType : 'hidden',
// private
- onCollapse : function(doAnim, animArg){
- if(this.checkbox){
- this.checkbox.dom.checked = false;
- }
- Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);
-
+ onRender : function(){
+ Ext.form.Hidden.superclass.onRender.apply(this, arguments);
},
// private
- onExpand : function(doAnim, animArg){
- if(this.checkbox){
- this.checkbox.dom.checked = true;
- }
- Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);
- },
-
- /**
- * This function is called by the fieldset's checkbox when it is toggled (only applies when
- * checkboxToggle = true). This method should never be called externally, but can be
- * overridden to provide custom behavior when the checkbox is toggled if needed.
- */
- onCheckClick : function(){
- this[this.checkbox.dom.checked ? 'expand' : 'collapse']();
- }
+ initEvents : function(){
+ this.originalValue = this.getValue();
+ },
- /**
- * @cfg {String/Number} activeItem
- * @hide
- */
- /**
- * @cfg {Mixed} applyTo
- * @hide
- */
- /**
- * @cfg {Boolean} bodyBorder
- * @hide
- */
- /**
- * @cfg {Boolean} border
- * @hide
- */
- /**
- * @cfg {Boolean/Number} bufferResize
- * @hide
- */
- /**
- * @cfg {Boolean} collapseFirst
- * @hide
- */
- /**
- * @cfg {String} defaultType
- * @hide
- */
- /**
- * @cfg {String} disabledClass
- * @hide
- */
- /**
- * @cfg {String} elements
- * @hide
- */
- /**
- * @cfg {Boolean} floating
- * @hide
- */
- /**
- * @cfg {Boolean} footer
- * @hide
- */
- /**
- * @cfg {Boolean} frame
- * @hide
- */
- /**
- * @cfg {Boolean} header
- * @hide
- */
- /**
- * @cfg {Boolean} headerAsText
- * @hide
- */
- /**
- * @cfg {Boolean} hideCollapseTool
- * @hide
- */
- /**
- * @cfg {String} iconCls
- * @hide
- */
- /**
- * @cfg {Boolean/String} shadow
- * @hide
- */
- /**
- * @cfg {Number} shadowOffset
- * @hide
- */
- /**
- * @cfg {Boolean} shim
- * @hide
- */
- /**
- * @cfg {Object/Array} tbar
- * @hide
- */
- /**
- * @cfg {Array} tools
- * @hide
- */
- /**
- * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
- * @hide
- */
- /**
- * @cfg {String} xtype
- * @hide
- */
- /**
- * @property header
- * @hide
- */
- /**
- * @property footer
- * @hide
- */
- /**
- * @method focus
- * @hide
- */
- /**
- * @method getBottomToolbar
- * @hide
- */
- /**
- * @method getTopToolbar
- * @hide
- */
- /**
- * @method setIconClass
- * @hide
- */
- /**
- * @event activate
- * @hide
- */
- /**
- * @event beforeclose
- * @hide
- */
- /**
- * @event bodyresize
- * @hide
- */
- /**
- * @event close
- * @hide
- */
- /**
- * @event deactivate
- * @hide
- */
+ // These are all private overrides
+ setSize : Ext.emptyFn,
+ setWidth : Ext.emptyFn,
+ setHeight : Ext.emptyFn,
+ setPosition : Ext.emptyFn,
+ setPagePosition : Ext.emptyFn,
+ markInvalid : Ext.emptyFn,
+ clearInvalid : Ext.emptyFn
});
-Ext.reg('fieldset', Ext.form.FieldSet);
+Ext.reg('hidden', Ext.form.Hidden);/**
+ * @class Ext.form.BasicForm
+ * @extends Ext.util.Observable
+ * Encapsulates the DOM <form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides + * input field management, validation, submission, and form loading services.
+ *By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}. + * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.
+ *File Uploads
+ *{@link #fileUpload File uploads} are not performed using Ajax submission, that + * is they are not performed using XMLHttpRequests. Instead the form is submitted in the standard + * manner with the DOM <form> element temporarily modified to have its + * target set to refer + * to a dynamically generated, hidden <iframe> which is inserted into the document + * but removed after the return data has been gathered.
+ *The server response is parsed by the browser to create the document for the IFRAME. If the + * server is using JSON to send the return object, then the + * Content-Type header + * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.
+ *Characters which are significant to an HTML parser must be sent as HTML entities, so encode + * "<" as "<", "&" as "&" etc.
+ *The response text is retrieved from the document, and a fake XMLHttpRequest object + * is created containing a responseText property in order to conform to the + * requirements of event handlers and callbacks.
+ *Be aware that file upload packets are sent with the content type multipart/form + * and some server technologies (notably JEE) may require some custom processing in order to + * retrieve parameter names and parameter values from the packet content.
+ * @constructor + * @param {Mixed} el The form element or its id + * @param {Object} config Configuration options + */ +Ext.form.BasicForm = Ext.extend(Ext.util.Observable, { + + constructor: function(el, config){ + Ext.apply(this, config); + if(Ext.isString(this.paramOrder)){ + this.paramOrder = this.paramOrder.split(/[\s,|]/); + } + /** + * A {@link Ext.util.MixedCollection MixedCollection} containing all the Ext.form.Fields in this form. + * @type MixedCollection + * @property items + */ + this.items = new Ext.util.MixedCollection(false, function(o){ + return o.getItemId(); + }); + this.addEvents( + /** + * @event beforeaction + * Fires before any action is performed. Return false to cancel the action. + * @param {Form} this + * @param {Action} action The {@link Ext.form.Action} to be performed + */ + 'beforeaction', + /** + * @event actionfailed + * Fires when an action fails. + * @param {Form} this + * @param {Action} action The {@link Ext.form.Action} that failed + */ + 'actionfailed', + /** + * @event actioncomplete + * Fires when an action is completed. + * @param {Form} this + * @param {Action} action The {@link Ext.form.Action} that completed + */ + 'actioncomplete' + ); + + if(el){ + this.initEl(el); + } + Ext.form.BasicForm.superclass.constructor.call(this); + }, + + /** + * @cfg {String} method + * The request method to use (GET or POST) for form actions if one isn't supplied in the action options. + */ + /** + * @cfg {DataReader} reader + * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read + * data when executing 'load' actions. This is optional as there is built-in + * support for processing JSON. For additional information on using an XMLReader + * see the example provided in examples/form/xml-form.html. + */ + /** + * @cfg {DataReader} errorReader + *An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to + * read field error messages returned from 'submit' actions. This is optional + * as there is built-in support for processing JSON.
+ *The Records which provide messages for the invalid Fields must use the + * Field name (or id) as the Record ID, and must contain a field called 'msg' + * which contains the error message.
+ *The errorReader does not have to be a full-blown implementation of a + * DataReader. It simply needs to implement a read(xhr) function + * which returns an Array of Records in an object with the following + * structure:
+{
+ records: recordArray
+}
+
+ */
+ /**
+ * @cfg {String} url
+ * The URL to use for form actions if one isn't supplied in the
+ * {@link #doAction doAction} options
.
+ */
+ /**
+ * @cfg {Boolean} fileUpload
+ * Set to true if this form is a file upload.
+ * File uploads are not performed using normal 'Ajax' techniques, that is they are not + * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the + * DOM <form> element temporarily modified to have its + * target set to refer + * to a dynamically generated, hidden <iframe> which is inserted into the document + * but removed after the return data has been gathered.
+ *The server response is parsed by the browser to create the document for the IFRAME. If the + * server is using JSON to send the return object, then the + * Content-Type header + * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.
+ *Characters which are significant to an HTML parser must be sent as HTML entities, so encode + * "<" as "<", "&" as "&" etc.
+ *The response text is retrieved from the document, and a fake XMLHttpRequest object + * is created containing a responseText property in order to conform to the + * requirements of event handlers and callbacks.
+ *Be aware that file upload packets are sent with the content type multipart/form + * and some server technologies (notably JEE) may require some custom processing in order to + * retrieve parameter names and parameter values from the packet content.
+ */ + /** + * @cfg {Object} baseParams + *Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
+ *Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
+ */ + /** + * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds). + */ + timeout: 30, + + /** + * @cfg {Object} api (Optional) If specified load and submit actions will be handled + * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}. + * Methods which have been imported by Ext.Direct can be specified here to load and submit + * forms. + * Such as the following:
+api: {
+ load: App.ss.MyProfile.load,
+ submit: App.ss.MyProfile.submit
+}
+
+ * Load actions can use {@link #paramOrder}
or {@link #paramsAsHash}
+ * to customize how the load method is invoked.
+ * Submit actions will always use a standard form submit. The formHandler configuration must
+ * be set on the associated server-side method which has been imported by Ext.Direct
A list of params to be executed server side.
+ * Defaults to undefined. Only used for the {@link #api}
+ * load
configuration.
Specify the params in the order in which they must be executed on the + * server-side as either (1) an Array of String values, or (2) a String of params + * delimited by either whitespace, comma, or pipe. For example, + * any of the following would be acceptable:
+paramOrder: ['param1','param2','param3']
+paramOrder: 'param1 param2 param3'
+paramOrder: 'param1,param2,param3'
+paramOrder: 'param1|param2|param'
+
+ */
+ paramOrder: undefined,
+
+ /**
+ * @cfg {Boolean} paramsAsHash Only used for the {@link #api}
+ * load
configuration. Send parameters as a collection of named
+ * arguments (defaults to false). Providing a
+ * {@link #paramOrder} nullifies this configuration.
+ */
+ paramsAsHash: false,
+
+ /**
+ * @cfg {String} waitTitle
+ * The default title to show for the waiting message box (defaults to 'Please Wait...')
+ */
+ waitTitle: 'Please Wait...',
+
+ // private
+ activeAction : null,
+
+ /**
+ * @cfg {Boolean} trackResetOnLoad If set to true, {@link #reset}() resets to the last loaded
+ * or {@link #setValues}() data instead of when the form was first created. Defaults to false.
+ */
+ trackResetOnLoad : false,
+
+ /**
+ * @cfg {Boolean} standardSubmit
+ * If set to true, standard HTML form submits are used instead + * of XHR (Ajax) style form submissions. Defaults to false.
+ *Note: When using standardSubmit
, the
+ * options
to {@link #submit}
are ignored because
+ * Ext's Ajax infrastracture is bypassed. To pass extra parameters (e.g.
+ * baseParams
and params
), utilize hidden fields
+ * to submit extra data, for example:
+new Ext.FormPanel({
+ standardSubmit: true,
+ baseParams: {
+ foo: 'bar'
+ },
+ {@link url}: 'myProcess.php',
+ items: [{
+ xtype: 'textfield',
+ name: 'userName'
+ }],
+ buttons: [{
+ text: 'Save',
+ handler: function(){
+ var fp = this.ownerCt.ownerCt,
+ form = fp.getForm();
+ if (form.isValid()) {
+ // check if there are baseParams and if
+ // hiddent items have been added already
+ if (fp.baseParams && !fp.paramsAdded) {
+ // add hidden items for all baseParams
+ for (i in fp.baseParams) {
+ fp.add({
+ xtype: 'hidden',
+ name: i,
+ value: fp.baseParams[i]
+ });
+ }
+ fp.doLayout();
+ // set a custom flag to prevent re-adding
+ fp.paramsAdded = true;
+ }
+ form.{@link #submit}();
+ }
+ }
+ }]
+});
+ *
+ */
+ /**
+ * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
+ * element by passing it or its id or mask the form itself by passing in true.
+ * @type Mixed
+ * @property waitMsgTarget
+ */
+
+ // private
+ initEl : function(el){
+ this.el = Ext.get(el);
+ this.id = this.el.id || Ext.id();
+ if(!this.standardSubmit){
+ this.el.on('submit', this.onSubmit, this);
+ }
+ this.el.addClass('x-form');
+ },
+
+ /**
+ * Get the HTML form Element
+ * @return Ext.Element
+ */
+ getEl: function(){
+ return this.el;
+ },
+
+ // private
+ onSubmit : function(e){
+ e.stopEvent();
+ },
+
+ // private
+ destroy: function() {
+ this.items.each(function(f){
+ Ext.destroy(f);
+ });
+ if(this.el){
+ this.el.removeAllListeners();
+ this.el.remove();
+ }
+ this.purgeListeners();
+ },
+
+ /**
+ * Returns true if client-side validation on the form is successful.
+ * @return Boolean
+ */
+ isValid : function(){
+ var valid = true;
+ this.items.each(function(f){
+ if(!f.validate()){
+ valid = false;
+ }
+ });
+ return valid;
+ },
+
+ /**
+ * Returns true if any fields in this form have changed from their original values.
+ *Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the + * Fields' original values are updated when the values are loaded by {@link #setValues} + * or {@link #loadRecord}.
+ * @return Boolean + */ + isDirty : function(){ + var dirty = false; + this.items.each(function(f){ + if(f.isDirty()){ + dirty = true; + return false; + } + }); + return dirty; + }, + + /** + * Performs a predefined action ({@link Ext.form.Action.Submit} or + * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action} + * to perform application-specific processing. + * @param {String/Object} actionName The name of the predefined action type, + * or instance of {@link Ext.form.Action} to perform. + * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}. + * All of the config options listed below are supported by both the + * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load} + * actions unless otherwise noted (custom actions could also accept + * other config options):The params to pass + * (defaults to the form's baseParams, or none if not defined)
+ *Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
Note: this is ignored when using the {@link #standardSubmit} option.
+ *The following code:
+myFormPanel.getForm().submit({
+ clientValidation: true,
+ url: 'updateConsignment.php',
+ params: {
+ newStatus: 'delivered'
+ },
+ success: function(form, action) {
+ Ext.Msg.alert('Success', action.result.msg);
+ },
+ failure: function(form, action) {
+ switch (action.failureType) {
+ case Ext.form.Action.CLIENT_INVALID:
+ Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
+ break;
+ case Ext.form.Action.CONNECT_FAILURE:
+ Ext.Msg.alert('Failure', 'Ajax communication failed');
+ break;
+ case Ext.form.Action.SERVER_INVALID:
+ Ext.Msg.alert('Failure', action.result.msg);
+ }
+ }
+});
+
+ * would process the following server response for a successful submission:
+{
+ "success":true, // note this is Boolean, not string
+ "msg":"Consignment updated"
+}
+
+ * and the following server response for a failed submission:
+{
+ "success":false, // note this is Boolean, not string
+ "msg":"You do not have permission to perform this operation"
+}
+
+ * @return {BasicForm} this
+ */
+ submit : function(options){
+ if(this.standardSubmit){
+ var v = this.isValid();
+ if(v){
+ var el = this.el.dom;
+ if(this.url && Ext.isEmpty(el.action)){
+ el.action = this.url;
+ }
+ el.submit();
+ }
+ return v;
+ }
+ var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
+ this.doAction(submitAction, options);
+ return this;
+ },
+
+ /**
+ * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
+ * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
+ * @return {BasicForm} this
+ */
+ load : function(options){
+ var loadAction = String.format('{0}load', this.api ? 'direct' : '');
+ this.doAction(loadAction, options);
+ return this;
+ },
+
+ /**
+ * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
+ * @param {Record} record The record to edit
+ * @return {BasicForm} this
+ */
+ updateRecord : function(record){
+ record.beginEdit();
+ var fs = record.fields;
+ fs.each(function(f){
+ var field = this.findField(f.name);
+ if(field){
+ record.set(f.name, field.getValue());
+ }
+ }, this);
+ record.endEdit();
+ return this;
+ },
+
+ /**
+ * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
+ * {@link Ext.data.Record#data record data}.
+ * See also {@link #trackResetOnLoad}.
+ * @param {Record} record The record to load
+ * @return {BasicForm} this
+ */
+ loadRecord : function(record){
+ this.setValues(record.data);
+ return this;
+ },
+
+ // private
+ beforeAction : function(action){
+ var o = action.options;
+ if(o.waitMsg){
+ if(this.waitMsgTarget === true){
+ this.el.mask(o.waitMsg, 'x-mask-loading');
+ }else if(this.waitMsgTarget){
+ this.waitMsgTarget = Ext.get(this.waitMsgTarget);
+ this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
+ }else{
+ Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
+ }
+ }
+ },
+
+ // private
+ afterAction : function(action, success){
+ this.activeAction = null;
+ var o = action.options;
+ if(o.waitMsg){
+ if(this.waitMsgTarget === true){
+ this.el.unmask();
+ }else if(this.waitMsgTarget){
+ this.waitMsgTarget.unmask();
+ }else{
+ Ext.MessageBox.updateProgress(1);
+ Ext.MessageBox.hide();
+ }
+ }
+ if(success){
+ if(o.reset){
+ this.reset();
+ }
+ Ext.callback(o.success, o.scope, [this, action]);
+ this.fireEvent('actioncomplete', this, action);
+ }else{
+ Ext.callback(o.failure, o.scope, [this, action]);
+ this.fireEvent('actionfailed', this, action);
+ }
+ },
+
+ /**
+ * Find a {@link Ext.form.Field} in this form.
+ * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
+ * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
+ * @return Field
+ */
+ findField : function(id){
+ var field = this.items.get(id);
+ if(!Ext.isObject(field)){
+ this.items.each(function(f){
+ if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
+ field = f;
+ return false;
+ }
+ });
+ }
+ return field || null;
+ },
+
+
+ /**
+ * Mark fields in this form invalid in bulk.
+ * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
+ * @return {BasicForm} this
+ */
+ markInvalid : function(errors){
+ if(Ext.isArray(errors)){
+ for(var i = 0, len = errors.length; i < len; i++){
+ var fieldError = errors[i];
+ var f = this.findField(fieldError.id);
+ if(f){
+ f.markInvalid(fieldError.msg);
+ }
+ }
+ }else{
+ var field, id;
+ for(id in errors){
+ if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
+ field.markInvalid(errors[id]);
+ }
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Set values for fields in this form in bulk.
+ * @param {Array/Object} values Either an array in the form:
+[{id:'clientName', value:'Fred. Olsen Lines'},
+ {id:'portOfLoading', value:'FXT'},
+ {id:'portOfDischarge', value:'OSL'} ]
+ * or an object hash of the form:
+{
+ clientName: 'Fred. Olsen Lines',
+ portOfLoading: 'FXT',
+ portOfDischarge: 'OSL'
+}
+ * @return {BasicForm} this
+ */
+ setValues : function(values){
+ if(Ext.isArray(values)){ // array of objects
+ for(var i = 0, len = values.length; i < len; i++){
+ var v = values[i];
+ var f = this.findField(v.id);
+ if(f){
+ f.setValue(v.value);
+ if(this.trackResetOnLoad){
+ f.originalValue = f.getValue();
+ }
+ }
+ }
+ }else{ // object hash
+ var field, id;
+ for(id in values){
+ if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
+ field.setValue(values[id]);
+ if(this.trackResetOnLoad){
+ field.originalValue = field.getValue();
+ }
+ }
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit. + * If multiple fields exist with the same name they are returned as an array.
+ *Note: The values are collected from all enabled HTML input elements within the form, not from + * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the + * value can potentially be the emptyText of a field.
+ * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object) + * @return {String/Object} + */ + getValues : function(asString){ + var fs = Ext.lib.Ajax.serializeForm(this.el.dom); + if(asString === true){ + return fs; + } + return Ext.urlDecode(fs); + }, + + /** + * Retrieves the fields in the form as a set of key/value pairs, using the {@link Ext.form.Field#getValue getValue()} method. + * If multiple fields exist with the same name they are returned as an array. + * @param {Boolean} dirtyOnly (optional) True to return only fields that are dirty. + * @return {Object} The values in the form + */ + getFieldValues : function(dirtyOnly){ + var o = {}, + n, + key, + val; + this.items.each(function(f){ + if(dirtyOnly !== true || f.isDirty()){ + n = f.getName(); + key = o[n]; + val = f.getValue(); + + if(Ext.isDefined(key)){ + if(Ext.isArray(key)){ + o[n].push(val); + }else{ + o[n] = [key, val]; + } + }else{ + o[n] = val; + } + } + }); + return o; + }, + + /** + * Clears all invalid messages in this form. + * @return {BasicForm} this + */ + clearInvalid : function(){ + this.items.each(function(f){ + f.clearInvalid(); + }); + return this; + }, + + /** + * Resets this form. + * @return {BasicForm} this + */ + reset : function(){ + this.items.each(function(f){ + f.reset(); + }); + return this; + }, + + /** + * Add Ext.form Components to this form's Collection. This does not result in rendering of + * the passed Component, it just enables the form to validate Fields, and distribute values to + * Fields. + *You will not usually call this function. In order to be rendered, a Field must be added + * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}. + * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's + * collection.
+ * @param {Field} field1 + * @param {Field} field2 (optional) + * @param {Field} etc (optional) + * @return {BasicForm} this + */ + add : function(){ + this.items.addAll(Array.prototype.slice.call(arguments, 0)); + return this; + }, + + + /** + * Removes a field from the items collection (does NOT remove its markup). + * @param {Field} field + * @return {BasicForm} this + */ + remove : function(field){ + this.items.remove(field); + return this; + }, + + /** + * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm, + * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id. + * @return {BasicForm} this + */ + render : function(){ + this.items.each(function(f){ + if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists + f.applyToMarkup(f.id); + } + }); + return this; + }, + + /** + * Calls {@link Ext#apply} for all fields in this form with the passed object. + * @param {Object} values + * @return {BasicForm} this + */ + applyToFields : function(o){ + this.items.each(function(f){ + Ext.apply(f, o); + }); + return this; + }, + + /** + * Calls {@link Ext#applyIf} for all field in this form with the passed object. + * @param {Object} values + * @return {BasicForm} this + */ + applyIfToFields : function(o){ + this.items.each(function(f){ + Ext.applyIf(f, o); + }); + return this; + }, + + callFieldMethod : function(fnName, args){ + args = args || []; + this.items.each(function(f){ + if(Ext.isFunction(f[fnName])){ + f[fnName].apply(f, args); + } + }); + return this; + } +}); + +// back compat +Ext.BasicForm = Ext.form.BasicForm;/** + * @class Ext.form.FormPanel + * @extends Ext.Panel + *Standard form container.
+ * + *Layout
+ *By default, FormPanel is configured with layout:'form' to use an {@link Ext.layout.FormLayout} + * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers + * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the + * {@link Ext.layout.FormLayout} layout manager.
+ * + *BasicForm
+ *Although not listed as configuration options of FormPanel, the FormPanel class accepts all + * of the config options required to configure its internal {@link Ext.form.BasicForm} for: + *
Note: If subclassing FormPanel, any configuration options for the BasicForm must be applied to + * the initialConfig property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm} + * configuration settings to this will not affect the BasicForm's configuration.
+ * + *Form Validation
+ *For information on form validation see the following:
+ *Form Submission
+ *By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser + * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the + * {@link Ext.form.BasicForm#standardSubmit standardSubmit} option.
+ * + * @constructor + * @param {Object} config Configuration options + * @xtype form + */ +Ext.FormPanel = Ext.extend(Ext.Panel, { + /** + * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id). + */ + /** + * @cfg {Boolean} hideLabels + *true to hide field labels by default (sets display:none). Defaults to + * false.
+ *Also see {@link Ext.Component}.{@link Ext.Component#hideLabel hideLabel}.
+ */
+ /**
+ * @cfg {Number} labelPad
+ * The default padding in pixels for field labels (defaults to 5). labelPad only
+ * applies if {@link #labelWidth} is also specified, otherwise it will be ignored.
+ */
+ /**
+ * @cfg {String} labelSeparator
+ * See {@link Ext.Component}.{@link Ext.Component#labelSeparator labelSeparator}
+ */
+ /**
+ * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
+ * and can be overridden on any child container (e.g., a fieldset can specify a different labelWidth
+ * for its fields) (defaults to 100).
+ */
+ /**
+ * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
+ */
+ /**
+ * @cfg {Array} buttons
+ * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.
+ *
Buttons in the footer of a FormPanel may be configured with the option formBind: true. This causes + * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on + * the form's valid/invalid state.
+ */ + + + /** + * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75). + */ + minButtonWidth : 75, + + /** + * @cfg {String} labelAlign The label alignment value used for the text-align specification + * for the container. Valid values are "left", "top" or "right" + * (defaults to "left"). This property cascades to child containers and can be + * overridden on any child container (e.g., a fieldset can specify a different labelAlign + * for its fields). + */ + labelAlign : 'left', + + /** + * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and + * regularly fires the {@link #clientvalidation} event passing that state.When monitoring valid state, the FormPanel enables/disables any of its configured
+ * {@link #buttons} which have been configured with formBind: true
depending
+ * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to false
-// Simple example rendered with default options:
-Ext.QuickTips.init(); // enable tooltips
-new Ext.form.HtmlEditor({
- renderTo: Ext.getBody(),
- width: 800,
- height: 300
-});
-
-// Passed via xtype into a container and with custom options:
-Ext.QuickTips.init(); // enable tooltips
-new Ext.Panel({
- title: 'HTML Editor',
- renderTo: Ext.getBody(),
- width: 600,
- height: 300,
- frame: true,
- layout: 'fit',
- items: {
- xtype: 'htmleditor',
- enableColors: false,
- enableAlignments: false
- }
+var form = new Ext.FormPanel({
+ title: 'Simple Form with FieldSets',
+ labelWidth: 75, // label settings here cascade unless overridden
+ url: 'save-form.php',
+ frame:true,
+ bodyStyle:'padding:5px 5px 0',
+ width: 700,
+ renderTo: document.body,
+ layout:'column', // arrange items in columns
+ defaults: { // defaults applied to items
+ layout: 'form',
+ border: false,
+ bodyStyle: 'padding:4px'
+ },
+ items: [{
+ // Fieldset in Column 1
+ xtype:'fieldset',
+ columnWidth: 0.5,
+ title: 'Fieldset 1',
+ collapsible: true,
+ autoHeight:true,
+ defaults: {
+ anchor: '-20' // leave room for error icon
+ },
+ defaultType: 'textfield',
+ items :[{
+ fieldLabel: 'Field 1'
+ }, {
+ fieldLabel: 'Field 2'
+ }, {
+ fieldLabel: 'Field 3'
+ }
+ ]
+ },{
+ // Fieldset in Column 2 - Panel inside
+ xtype:'fieldset',
+ title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header
+ autoHeight:true,
+ columnWidth: 0.5,
+ checkboxToggle: true,
+ collapsed: true, // fieldset initially collapsed
+ layout:'anchor',
+ items :[{
+ xtype: 'panel',
+ anchor: '100%',
+ title: 'Panel inside a fieldset',
+ frame: true,
+ height: 100
+ }]
+ }]
});
-
+ *
* @constructor
- * Create a new HtmlEditor
- * @param {Object} config
- * @xtype htmleditor
- */
-
-Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
- /**
- * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
- */
- enableFormat : true,
- /**
- * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
- */
- enableFontSize : true,
- /**
- * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
- */
- enableColors : true,
- /**
- * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
- */
- enableAlignments : true,
- /**
- * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
- */
- enableLists : true,
- /**
- * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
- */
- enableSourceEdit : true,
- /**
- * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
- */
- enableLinks : true,
- /**
- * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
- */
- enableFont : true,
- /**
- * @cfg {String} createLinkText The default text for the create link prompt
- */
- createLinkText : 'Please enter the URL for the link:',
- /**
- * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
- */
- defaultLinkValue : 'http:/'+'/',
- /**
- * @cfg {Array} fontFamilies An array of available font families
- */
- fontFamilies : [
- 'Arial',
- 'Courier New',
- 'Tahoma',
- 'Times New Roman',
- 'Verdana'
- ],
- defaultFont: 'tahoma',
+ * @param {Object} config Configuration options
+ * @xtype fieldset
+ */
+Ext.form.FieldSet = Ext.extend(Ext.Panel, {
/**
- * @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).
- */
- defaultValue: (Ext.isOpera || Ext.isIE6) ? ' ' : '',
-
- // private properties
- actionMode: 'wrap',
- validationEvent : false,
- deferHeight: true,
- initialized : false,
- activated : false,
- sourceEditMode : false,
- onFocus : Ext.emptyFn,
- iframePad:3,
- hideMode:'offsets',
- defaultAutoCreate : {
- tag: "textarea",
- style:"width:500px;height:300px;",
- autocomplete: "off"
- },
-
- // private
- initComponent : function(){
- this.addEvents(
- /**
- * @event initialize
- * Fires when the editor is fully initialized (including the iframe)
- * @param {HtmlEditor} this
- */
- 'initialize',
- /**
- * @event activate
- * Fires when the editor is first receives the focus. Any insertion must wait
- * until after this event.
- * @param {HtmlEditor} this
- */
- 'activate',
- /**
- * @event beforesync
- * Fires before the textarea is updated with content from the editor iframe. Return false
- * to cancel the sync.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- 'beforesync',
- /**
- * @event beforepush
- * Fires before the iframe editor is updated with content from the textarea. Return false
- * to cancel the push.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- 'beforepush',
- /**
- * @event sync
- * Fires when the textarea is updated with content from the editor iframe.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- 'sync',
- /**
- * @event push
- * Fires when the iframe editor is updated with content from the textarea.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- 'push',
- /**
- * @event editmodechange
- * Fires when the editor switches edit modes
- * @param {HtmlEditor} this
- * @param {Boolean} sourceEdit True if source edit, false if standard editing.
- */
- 'editmodechange'
- )
- },
-
- // private
- createFontOptions : function(){
- var buf = [], fs = this.fontFamilies, ff, lc;
- for(var i = 0, len = fs.length; i< len; i++){
- ff = fs[i];
- lc = ff.toLowerCase();
- buf.push(
- ''
- );
- }
- return buf.join('');
- },
-
- /*
- * Protected method that will not generally be called directly. It
- * is called when the editor creates its toolbar. Override this method if you need to
- * add custom toolbar buttons.
- * @param {HtmlEditor} editor
+ * @cfg {Mixed} checkboxToggle true to render a checkbox into the fieldset frame just
+ * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults
+ * to false).
+ * A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox. + * If true is specified, the default DomHelper config object used to create the element + * is:
+ * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}
+ *
*/
- createToolbar : function(editor){
-
- var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
-
- function btn(id, toggle, handler){
- return {
- itemId : id,
- cls : 'x-btn-icon',
- iconCls: 'x-edit-'+id,
- enableToggle:toggle !== false,
- scope: editor,
- handler:handler||editor.relayBtnCmd,
- clickEvent:'mousedown',
- tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
- overflowText: editor.buttonTips[id].title || undefined,
- tabIndex:-1
- };
- }
-
- // build the toolbar
- var tb = new Ext.Toolbar({
- renderTo:this.wrap.dom.firstChild
- });
-
- // stop form submits
- this.mon(tb.el, 'click', function(e){
- e.preventDefault();
- });
-
- if(this.enableFont && !Ext.isSafari2){
- this.fontSelect = tb.el.createChild({
- tag:'select',
- cls:'x-font-select',
- html: this.createFontOptions()
- });
- this.mon(this.fontSelect, 'change', function(){
- var font = this.fontSelect.dom.value;
- this.relayCmd('fontname', font);
- this.deferFocus();
- }, this);
-
- tb.add(
- this.fontSelect.dom,
- '-'
- );
- }
-
- if(this.enableFormat){
- tb.add(
- btn('bold'),
- btn('italic'),
- btn('underline')
- );
- }
-
- if(this.enableFontSize){
- tb.add(
- '-',
- btn('increasefontsize', false, this.adjustFont),
- btn('decreasefontsize', false, this.adjustFont)
- );
- }
-
- if(this.enableColors){
- tb.add(
- '-', {
- itemId:'forecolor',
- cls:'x-btn-icon',
- iconCls: 'x-edit-forecolor',
- clickEvent:'mousedown',
- tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,
- tabIndex:-1,
- menu : new Ext.menu.ColorMenu({
- allowReselect: true,
- focus: Ext.emptyFn,
- value:'000000',
- plain:true,
- listeners: {
- scope: this,
- select: function(cp, color){
- this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
- this.deferFocus();
- }
- },
- clickEvent:'mousedown'
- })
- }, {
- itemId:'backcolor',
- cls:'x-btn-icon',
- iconCls: 'x-edit-backcolor',
- clickEvent:'mousedown',
- tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,
- tabIndex:-1,
- menu : new Ext.menu.ColorMenu({
- focus: Ext.emptyFn,
- value:'FFFFFF',
- plain:true,
- allowReselect: true,
- listeners: {
- scope: this,
- select: function(cp, color){
- if(Ext.isGecko){
- this.execCmd('useCSS', false);
- this.execCmd('hilitecolor', color);
- this.execCmd('useCSS', true);
- this.deferFocus();
- }else{
- this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
- this.deferFocus();
- }
- }
- },
- clickEvent:'mousedown'
- })
- }
- );
- }
-
- if(this.enableAlignments){
- tb.add(
- '-',
- btn('justifyleft'),
- btn('justifycenter'),
- btn('justifyright')
- );
- }
-
- if(!Ext.isSafari2){
- if(this.enableLinks){
- tb.add(
- '-',
- btn('createlink', false, this.createLink)
- );
- }
-
- if(this.enableLists){
- tb.add(
- '-',
- btn('insertorderedlist'),
- btn('insertunorderedlist')
- );
- }
- if(this.enableSourceEdit){
- tb.add(
- '-',
- btn('sourceedit', true, function(btn){
- this.toggleSourceEdit(!this.sourceEditMode);
- })
- );
- }
- }
-
- this.tb = tb;
- },
-
/**
- * Protected method that will not generally be called directly. It
- * is called when the editor initializes the iframe with HTML contents. Override this method if you
- * want to change the initialization markup of the iframe (e.g. to add stylesheets).
+ * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if {@link #checkboxToggle} = true
+ * (defaults to '[checkbox id]-checkbox').
*/
- getDocMarkup : function(){
- return '';
- },
-
- // private
- getEditorBody : function(){
- return this.doc.body || this.doc.documentElement;
- },
-
- // private
- getDoc : function(){
- return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
- },
-
- // private
- getWin : function(){
- return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
- },
-
- // private
- onRender : function(ct, position){
- Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
- this.el.dom.style.border = '0 none';
- this.el.dom.setAttribute('tabIndex', -1);
- this.el.addClass('x-hidden');
- if(Ext.isIE){ // fix IE 1px bogus margin
- this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
- }
- this.wrap = this.el.wrap({
- cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
- });
-
- this.createToolbar(this);
-
- this.disableItems(true);
- // is this needed?
- // this.tb.doLayout();
-
- this.createIFrame();
-
- if(!this.width){
- var sz = this.el.getSize();
- this.setSize(sz.width, this.height || sz.height);
- }
- this.resizeEl = this.positionEl = this.wrap;
- },
-
- createIFrame: function(){
- var iframe = document.createElement('iframe');
- iframe.name = Ext.id();
- iframe.frameBorder = '0';
- iframe.src = Ext.SSL_SECURE_URL;
- this.wrap.dom.appendChild(iframe);
-
- this.iframe = iframe;
-
- this.monitorTask = Ext.TaskMgr.start({
- run: this.checkDesignMode,
- scope: this,
- interval:100
- });
- },
-
- initFrame : function(){
- Ext.TaskMgr.stop(this.monitorTask);
- this.doc = this.getDoc();
- this.win = this.getWin();
-
- this.doc.open();
- this.doc.write(this.getDocMarkup());
- this.doc.close();
-
- var task = { // must defer to wait for browser to be ready
- run : function(){
- if(this.doc.body || this.doc.readyState == 'complete'){
- Ext.TaskMgr.stop(task);
- this.doc.designMode="on";
- this.initEditor.defer(10, this);
- }
- },
- interval : 10,
- duration:10000,
- scope: this
- };
- Ext.TaskMgr.start(task);
- },
-
-
- checkDesignMode : function(){
- if(this.wrap && this.wrap.dom.offsetWidth){
- var doc = this.getDoc();
- if(!doc){
- return;
- }
- if(!doc.editorInitialized || String(doc.designMode).toLowerCase() != 'on'){
- this.initFrame();
- }
- }
- },
-
- disableItems: function(disabled){
- if(this.fontSelect){
- this.fontSelect.dom.disabled = disabled;
- }
- this.tb.items.each(function(item){
- if(item.getItemId() != 'sourceedit'){
- item.setDisabled(disabled);
- }
- });
- },
-
- // private
- onResize : function(w, h){
- Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
- if(this.el && this.iframe){
- if(Ext.isNumber(w)){
- var aw = w - this.wrap.getFrameWidth('lr');
- this.el.setWidth(aw);
- this.tb.setWidth(aw);
- this.iframe.style.width = Math.max(aw, 0) + 'px';
- }
- if(Ext.isNumber(h)){
- var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
- this.el.setHeight(ah);
- this.iframe.style.height = Math.max(ah, 0) + 'px';
- if(this.doc){
- this.getEditorBody().style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
- }
- }
- }
- },
-
/**
- * Toggles the editor between standard and source edit mode.
- * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
+ * @cfg {Boolean} collapsible
+ * true to make the fieldset collapsible and have the expand/collapse toggle button automatically
+ * rendered into the legend element, false to keep the fieldset statically sized with no collapse
+ * button (defaults to false). Another option is to configure {@link #checkboxToggle}.
*/
- toggleSourceEdit : function(sourceEditMode){
- if(sourceEditMode === undefined){
- sourceEditMode = !this.sourceEditMode;
- }
- this.sourceEditMode = sourceEditMode === true;
- var btn = this.tb.items.get('sourceedit');
- if(btn.pressed !== this.sourceEditMode){
- btn.toggle(this.sourceEditMode);
- if(!btn.xtbHidden){
- return;
- }
- }
- if(this.sourceEditMode){
- this.disableItems(true);
- this.syncValue();
- this.iframe.className = 'x-hidden';
- this.el.removeClass('x-hidden');
- this.el.dom.removeAttribute('tabIndex');
- this.el.focus();
- }else{
- if(this.initialized){
- this.disableItems(false);
- }
- this.pushValue();
- this.iframe.className = '';
- this.el.addClass('x-hidden');
- this.el.dom.setAttribute('tabIndex', -1);
- this.deferFocus();
- }
- var lastSize = this.lastSize;
- if(lastSize){
- delete this.lastSize;
- this.setSize(lastSize);
- }
- this.fireEvent('editmodechange', this, this.sourceEditMode);
- },
-
- // private used internally
- createLink : function(){
- var url = prompt(this.createLinkText, this.defaultLinkValue);
- if(url && url != 'http:/'+'/'){
- this.relayCmd('createlink', url);
- }
- },
-
- // private
- initEvents : function(){
- this.originalValue = this.getValue();
- },
-
/**
- * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
- * @method
+ * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
*/
- markInvalid : Ext.emptyFn,
-
/**
- * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
- * @method
+ * @cfg {String} itemCls A css class to apply to the x-form-item of fields (see
+ * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).
+ * This property cascades to child containers.
*/
- clearInvalid : Ext.emptyFn,
-
- // docs inherit from Field
- setValue : function(v){
- Ext.form.HtmlEditor.superclass.setValue.call(this, v);
- this.pushValue();
- return this;
- },
-
/**
- * Protected method that will not generally be called directly. If you need/want
- * custom HTML cleanup, this is the method you should override.
- * @param {String} html The HTML to be cleaned
- * @return {String} The cleaned HTML
+ * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to 'x-fieldset').
*/
- cleanHtml: function(html) {
- html = String(html);
- if(Ext.isWebKit){ // strip safari nonsense
- html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
- }
-
- /*
- * Neat little hack. Strips out all the non-digit characters from the default
- * value and compares it to the character code of the first character in the string
- * because it can cause encoding issues when posted to the server.
- */
- if(html.charCodeAt(0) == this.defaultValue.replace(/\D/g, '')){
- html = html.substring(1);
- }
- return html;
- },
-
+ baseCls : 'x-fieldset',
/**
- * Protected method that will not generally be called directly. Syncs the contents
- * of the editor iframe with the textarea.
+ * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to 'form').
*/
- syncValue : function(){
- if(this.initialized){
- var bd = this.getEditorBody();
- var html = bd.innerHTML;
- if(Ext.isWebKit){
- var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
- var m = bs.match(/text-align:(.*?);/i);
- if(m && m[1]){
- html = '
-{
- bold : {
- title: 'Bold (Ctrl+B)',
- text: 'Make the selected text bold.',
- cls: 'x-html-editor-tip'
- },
- italic : {
- title: 'Italic (Ctrl+I)',
- text: 'Make the selected text italic.',
- cls: 'x-html-editor-tip'
- },
- ...
-
- * @type Object
+ * This function is called by the fieldset's checkbox when it is toggled (only applies when
+ * checkboxToggle = true). This method should never be called externally, but can be
+ * overridden to provide custom behavior when the checkbox is toggled if needed.
*/
- buttonTips : {
- bold : {
- title: 'Bold (Ctrl+B)',
- text: 'Make the selected text bold.',
- cls: 'x-html-editor-tip'
- },
- italic : {
- title: 'Italic (Ctrl+I)',
- text: 'Make the selected text italic.',
- cls: 'x-html-editor-tip'
- },
- underline : {
- title: 'Underline (Ctrl+U)',
- text: 'Underline the selected text.',
- cls: 'x-html-editor-tip'
- },
- increasefontsize : {
- title: 'Grow Text',
- text: 'Increase the font size.',
- cls: 'x-html-editor-tip'
- },
- decreasefontsize : {
- title: 'Shrink Text',
- text: 'Decrease the font size.',
- cls: 'x-html-editor-tip'
- },
- backcolor : {
- title: 'Text Highlight Color',
- text: 'Change the background color of the selected text.',
- cls: 'x-html-editor-tip'
- },
- forecolor : {
- title: 'Font Color',
- text: 'Change the color of the selected text.',
- cls: 'x-html-editor-tip'
- },
- justifyleft : {
- title: 'Align Text Left',
- text: 'Align text to the left.',
- cls: 'x-html-editor-tip'
- },
- justifycenter : {
- title: 'Center Text',
- text: 'Center text in the editor.',
- cls: 'x-html-editor-tip'
- },
- justifyright : {
- title: 'Align Text Right',
- text: 'Align text to the right.',
- cls: 'x-html-editor-tip'
- },
- insertunorderedlist : {
- title: 'Bullet List',
- text: 'Start a bulleted list.',
- cls: 'x-html-editor-tip'
- },
- insertorderedlist : {
- title: 'Numbered List',
- text: 'Start a numbered list.',
- cls: 'x-html-editor-tip'
- },
- createlink : {
- title: 'Hyperlink',
- text: 'Make the selected text a hyperlink.',
- cls: 'x-html-editor-tip'
- },
- sourceedit : {
- title: 'Source Edit',
- text: 'Switch to source editing mode.',
- cls: 'x-html-editor-tip'
- }
+ onCheckClick : function(){
+ this[this.checkbox.dom.checked ? 'expand' : 'collapse']();
}
- // hide stuff that is not compatible
/**
- * @event blur
+ * @cfg {String/Number} activeItem
+ * @hide
+ */
+ /**
+ * @cfg {Mixed} applyTo
* @hide
*/
/**
- * @event change
+ * @cfg {Boolean} bodyBorder
* @hide
*/
/**
- * @event focus
+ * @cfg {Boolean} border
* @hide
*/
/**
- * @event specialkey
+ * @cfg {Boolean/Number} bufferResize
* @hide
*/
/**
- * @cfg {String} fieldClass @hide
+ * @cfg {Boolean} collapseFirst
+ * @hide
*/
/**
- * @cfg {String} focusClass @hide
+ * @cfg {String} defaultType
+ * @hide
*/
/**
- * @cfg {String} autoCreate @hide
+ * @cfg {String} disabledClass
+ * @hide
*/
/**
- * @cfg {String} inputType @hide
+ * @cfg {String} elements
+ * @hide
*/
/**
- * @cfg {String} invalidClass @hide
+ * @cfg {Boolean} floating
+ * @hide
*/
/**
- * @cfg {String} invalidText @hide
+ * @cfg {Boolean} footer
+ * @hide
*/
/**
- * @cfg {String} msgFx @hide
+ * @cfg {Boolean} frame
+ * @hide
*/
/**
- * @cfg {String} validateOnBlur @hide
+ * @cfg {Boolean} header
+ * @hide
*/
/**
- * @cfg {Boolean} allowDomMove @hide
+ * @cfg {Boolean} headerAsText
+ * @hide
*/
/**
- * @cfg {String} applyTo @hide
+ * @cfg {Boolean} hideCollapseTool
+ * @hide
*/
/**
- * @cfg {String} autoHeight @hide
+ * @cfg {String} iconCls
+ * @hide
*/
/**
- * @cfg {String} autoWidth @hide
+ * @cfg {Boolean/String} shadow
+ * @hide
*/
/**
- * @cfg {String} cls @hide
+ * @cfg {Number} shadowOffset
+ * @hide
*/
/**
- * @cfg {String} disabled @hide
+ * @cfg {Boolean} shim
+ * @hide
*/
/**
- * @cfg {String} disabledClass @hide
+ * @cfg {Object/Array} tbar
+ * @hide
*/
/**
- * @cfg {String} msgTarget @hide
+ * @cfg {Array} tools
+ * @hide
*/
/**
- * @cfg {String} readOnly @hide
+ * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
+ * @hide
*/
/**
- * @cfg {String} style @hide
+ * @cfg {String} xtype
+ * @hide
*/
/**
- * @cfg {String} validationDelay @hide
+ * @property header
+ * @hide
*/
/**
- * @cfg {String} validationEvent @hide
+ * @property footer
+ * @hide
*/
/**
- * @cfg {String} tabIndex @hide
+ * @method focus
+ * @hide
*/
/**
- * @property disabled
+ * @method getBottomToolbar
* @hide
*/
/**
- * @method applyToMarkup
+ * @method getTopToolbar
* @hide
*/
/**
- * @method disable
+ * @method setIconClass
* @hide
*/
/**
- * @method enable
+ * @event activate
* @hide
*/
/**
- * @method validate
+ * @event beforeclose
* @hide
*/
/**
- * @event valid
+ * @event bodyresize
* @hide
*/
/**
- * @method setDisabled
+ * @event close
* @hide
*/
/**
- * @cfg keys
+ * @event deactivate
* @hide
*/
});
+Ext.reg('fieldset', Ext.form.FieldSet);
+/**
+ * @class Ext.form.HtmlEditor
+ * @extends Ext.form.Field
+ * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
+ * automatically hidden when needed. These are noted in the config options where appropriate.
+ *
+// Simple example rendered with default options:
+Ext.QuickTips.init(); // enable tooltips
+new Ext.form.HtmlEditor({
+ renderTo: Ext.getBody(),
+ width: 800,
+ height: 300
+});
+
+// Passed via xtype into a container and with custom options:
+Ext.QuickTips.init(); // enable tooltips
+new Ext.Panel({
+ title: 'HTML Editor',
+ renderTo: Ext.getBody(),
+ width: 600,
+ height: 300,
+ frame: true,
+ layout: 'fit',
+ items: {
+ xtype: 'htmleditor',
+ enableColors: false,
+ enableAlignments: false
+ }
+});
+
+ * @constructor
+ * Create a new HtmlEditor
+ * @param {Object} config
+ * @xtype htmleditor
+ */
+
+Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
+ /**
+ * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
+ */
+ enableFormat : true,
+ /**
+ * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
+ */
+ enableFontSize : true,
+ /**
+ * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
+ */
+ enableColors : true,
+ /**
+ * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
+ */
+ enableAlignments : true,
+ /**
+ * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
+ */
+ enableLists : true,
+ /**
+ * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
+ */
+ enableSourceEdit : true,
+ /**
+ * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
+ */
+ enableLinks : true,
+ /**
+ * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
+ */
+ enableFont : true,
+ /**
+ * @cfg {String} createLinkText The default text for the create link prompt
+ */
+ createLinkText : 'Please enter the URL for the link:',
+ /**
+ * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
+ */
+ defaultLinkValue : 'http:/'+'/',
+ /**
+ * @cfg {Array} fontFamilies An array of available font families
+ */
+ fontFamilies : [
+ 'Arial',
+ 'Courier New',
+ 'Tahoma',
+ 'Times New Roman',
+ 'Verdana'
+ ],
+ defaultFont: 'tahoma',
+ /**
+ * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to (Non-breaking space) in Opera and IE6, (Zero-width space) in all other browsers).
+ */
+ defaultValue: (Ext.isOpera || Ext.isIE6) ? ' ' : '',
+
+ // private properties
+ actionMode: 'wrap',
+ validationEvent : false,
+ deferHeight: true,
+ initialized : false,
+ activated : false,
+ sourceEditMode : false,
+ onFocus : Ext.emptyFn,
+ iframePad:3,
+ hideMode:'offsets',
+ defaultAutoCreate : {
+ tag: "textarea",
+ style:"width:500px;height:300px;",
+ autocomplete: "off"
+ },
+
+ // private
+ initComponent : function(){
+ this.addEvents(
+ /**
+ * @event initialize
+ * Fires when the editor is fully initialized (including the iframe)
+ * @param {HtmlEditor} this
+ */
+ 'initialize',
+ /**
+ * @event activate
+ * Fires when the editor is first receives the focus. Any insertion must wait
+ * until after this event.
+ * @param {HtmlEditor} this
+ */
+ 'activate',
+ /**
+ * @event beforesync
+ * Fires before the textarea is updated with content from the editor iframe. Return false
+ * to cancel the sync.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ 'beforesync',
+ /**
+ * @event beforepush
+ * Fires before the iframe editor is updated with content from the textarea. Return false
+ * to cancel the push.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ 'beforepush',
+ /**
+ * @event sync
+ * Fires when the textarea is updated with content from the editor iframe.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ 'sync',
+ /**
+ * @event push
+ * Fires when the iframe editor is updated with content from the textarea.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ 'push',
+ /**
+ * @event editmodechange
+ * Fires when the editor switches edit modes
+ * @param {HtmlEditor} this
+ * @param {Boolean} sourceEdit True if source edit, false if standard editing.
+ */
+ 'editmodechange'
+ )
+ },
+
+ // private
+ createFontOptions : function(){
+ var buf = [], fs = this.fontFamilies, ff, lc;
+ for(var i = 0, len = fs.length; i< len; i++){
+ ff = fs[i];
+ lc = ff.toLowerCase();
+ buf.push(
+ ''
+ );
+ }
+ return buf.join('');
+ },
+
+ /*
+ * Protected method that will not generally be called directly. It
+ * is called when the editor creates its toolbar. Override this method if you need to
+ * add custom toolbar buttons.
+ * @param {HtmlEditor} editor
+ */
+ createToolbar : function(editor){
+ var items = [];
+ var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
+
+
+ function btn(id, toggle, handler){
+ return {
+ itemId : id,
+ cls : 'x-btn-icon',
+ iconCls: 'x-edit-'+id,
+ enableToggle:toggle !== false,
+ scope: editor,
+ handler:handler||editor.relayBtnCmd,
+ clickEvent:'mousedown',
+ tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
+ overflowText: editor.buttonTips[id].title || undefined,
+ tabIndex:-1
+ };
+ }
+
+
+ if(this.enableFont && !Ext.isSafari2){
+ var fontSelectItem = new Ext.Toolbar.Item({
+ autoEl: {
+ tag:'select',
+ cls:'x-font-select',
+ html: this.createFontOptions()
+ }
+ });
+
+ items.push(
+ fontSelectItem,
+ '-'
+ );
+ }
+
+ if(this.enableFormat){
+ items.push(
+ btn('bold'),
+ btn('italic'),
+ btn('underline')
+ );
+ }
+
+ if(this.enableFontSize){
+ items.push(
+ '-',
+ btn('increasefontsize', false, this.adjustFont),
+ btn('decreasefontsize', false, this.adjustFont)
+ );
+ }
+
+ if(this.enableColors){
+ items.push(
+ '-', {
+ itemId:'forecolor',
+ cls:'x-btn-icon',
+ iconCls: 'x-edit-forecolor',
+ clickEvent:'mousedown',
+ tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,
+ tabIndex:-1,
+ menu : new Ext.menu.ColorMenu({
+ allowReselect: true,
+ focus: Ext.emptyFn,
+ value:'000000',
+ plain:true,
+ listeners: {
+ scope: this,
+ select: function(cp, color){
+ this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
+ this.deferFocus();
+ }
+ },
+ clickEvent:'mousedown'
+ })
+ }, {
+ itemId:'backcolor',
+ cls:'x-btn-icon',
+ iconCls: 'x-edit-backcolor',
+ clickEvent:'mousedown',
+ tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,
+ tabIndex:-1,
+ menu : new Ext.menu.ColorMenu({
+ focus: Ext.emptyFn,
+ value:'FFFFFF',
+ plain:true,
+ allowReselect: true,
+ listeners: {
+ scope: this,
+ select: function(cp, color){
+ if(Ext.isGecko){
+ this.execCmd('useCSS', false);
+ this.execCmd('hilitecolor', color);
+ this.execCmd('useCSS', true);
+ this.deferFocus();
+ }else{
+ this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
+ this.deferFocus();
+ }
+ }
+ },
+ clickEvent:'mousedown'
+ })
+ }
+ );
+ }
+
+ if(this.enableAlignments){
+ items.push(
+ '-',
+ btn('justifyleft'),
+ btn('justifycenter'),
+ btn('justifyright')
+ );
+ }
+
+ if(!Ext.isSafari2){
+ if(this.enableLinks){
+ items.push(
+ '-',
+ btn('createlink', false, this.createLink)
+ );
+ }
+
+ if(this.enableLists){
+ items.push(
+ '-',
+ btn('insertorderedlist'),
+ btn('insertunorderedlist')
+ );
+ }
+ if(this.enableSourceEdit){
+ items.push(
+ '-',
+ btn('sourceedit', true, function(btn){
+ this.toggleSourceEdit(!this.sourceEditMode);
+ })
+ );
+ }
+ }
+
+ // build the toolbar
+ var tb = new Ext.Toolbar({
+ renderTo: this.wrap.dom.firstChild,
+ items: items
+ });
+
+ if (fontSelectItem) {
+ this.fontSelect = fontSelectItem.el;
+
+ this.mon(this.fontSelect, 'change', function(){
+ var font = this.fontSelect.dom.value;
+ this.relayCmd('fontname', font);
+ this.deferFocus();
+ }, this);
+ }
+
+
+ // stop form submits
+ this.mon(tb.el, 'click', function(e){
+ e.preventDefault();
+ });
+
+ this.tb = tb;
+ },
+
+ onDisable: function(){
+ this.wrap.mask();
+ Ext.form.HtmlEditor.superclass.onDisable.call(this);
+ },
+
+ onEnable: function(){
+ this.wrap.unmask();
+ Ext.form.HtmlEditor.superclass.onEnable.call(this);
+ },
+
+ setReadOnly: function(readOnly){
+
+ Ext.form.HtmlEditor.superclass.setReadOnly.call(this, readOnly);
+ if(this.initialized){
+ this.setDesignMode(!readOnly);
+ var bd = this.getEditorBody();
+ if(bd){
+ bd.style.cursor = this.readOnly ? 'default' : 'text';
+ }
+ this.disableItems(readOnly);
+ }
+ },
+
+ /**
+ * Protected method that will not generally be called directly. It
+ * is called when the editor initializes the iframe with HTML contents. Override this method if you
+ * want to change the initialization markup of the iframe (e.g. to add stylesheets).
+ *
+ * Note: IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility
+ */
+ getDocMarkup : function(){
+ return '';
+ },
+
+ // private
+ getEditorBody : function(){
+ var doc = this.getDoc();
+ return doc.body || doc.documentElement;
+ },
+
+ // private
+ getDoc : function(){
+ return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
+ },
+
+ // private
+ getWin : function(){
+ return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
+ },
+
+ // private
+ onRender : function(ct, position){
+ Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
+ this.el.dom.style.border = '0 none';
+ this.el.dom.setAttribute('tabIndex', -1);
+ this.el.addClass('x-hidden');
+ if(Ext.isIE){ // fix IE 1px bogus margin
+ this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
+ }
+ this.wrap = this.el.wrap({
+ cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
+ });
+
+ this.createToolbar(this);
+
+ this.disableItems(true);
+
+ this.tb.doLayout();
+
+ this.createIFrame();
+
+ if(!this.width){
+ var sz = this.el.getSize();
+ this.setSize(sz.width, this.height || sz.height);
+ }
+ this.resizeEl = this.positionEl = this.wrap;
+ },
+
+ createIFrame: function(){
+ var iframe = document.createElement('iframe');
+ iframe.name = Ext.id();
+ iframe.frameBorder = '0';
+ iframe.style.overflow = 'auto';
+
+ this.wrap.dom.appendChild(iframe);
+ this.iframe = iframe;
+
+ this.monitorTask = Ext.TaskMgr.start({
+ run: this.checkDesignMode,
+ scope: this,
+ interval:100
+ });
+ },
+
+ initFrame : function(){
+ Ext.TaskMgr.stop(this.monitorTask);
+ var doc = this.getDoc();
+ this.win = this.getWin();
+
+ doc.open();
+ doc.write(this.getDocMarkup());
+ doc.close();
+
+ var task = { // must defer to wait for browser to be ready
+ run : function(){
+ var doc = this.getDoc();
+ if(doc.body || doc.readyState == 'complete'){
+ Ext.TaskMgr.stop(task);
+ this.setDesignMode(true);
+ this.initEditor.defer(10, this);
+ }
+ },
+ interval : 10,
+ duration:10000,
+ scope: this
+ };
+ Ext.TaskMgr.start(task);
+ },
+
+
+ checkDesignMode : function(){
+ if(this.wrap && this.wrap.dom.offsetWidth){
+ var doc = this.getDoc();
+ if(!doc){
+ return;
+ }
+ if(!doc.editorInitialized || this.getDesignMode() != 'on'){
+ this.initFrame();
+ }
+ }
+ },
+
+ /* private
+ * set current design mode. To enable, mode can be true or 'on', off otherwise
+ */
+ setDesignMode : function(mode){
+ var doc ;
+ if(doc = this.getDoc()){
+ if(this.readOnly){
+ mode = false;
+ }
+ doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';
+ }
+
+ },
+
+ // private
+ getDesignMode : function(){
+ var doc = this.getDoc();
+ if(!doc){ return ''; }
+ return String(doc.designMode).toLowerCase();
+
+ },
+
+ disableItems: function(disabled){
+ if(this.fontSelect){
+ this.fontSelect.dom.disabled = disabled;
+ }
+ this.tb.items.each(function(item){
+ if(item.getItemId() != 'sourceedit'){
+ item.setDisabled(disabled);
+ }
+ });
+ },
+
+ // private
+ onResize : function(w, h){
+ Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
+ if(this.el && this.iframe){
+ if(Ext.isNumber(w)){
+ var aw = w - this.wrap.getFrameWidth('lr');
+ this.el.setWidth(aw);
+ this.tb.setWidth(aw);
+ this.iframe.style.width = Math.max(aw, 0) + 'px';
+ }
+ if(Ext.isNumber(h)){
+ var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
+ this.el.setHeight(ah);
+ this.iframe.style.height = Math.max(ah, 0) + 'px';
+ var bd = this.getEditorBody();
+ if(bd){
+ bd.style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
+ }
+ }
+ }
+ },
+
+ /**
+ * Toggles the editor between standard and source edit mode.
+ * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
+ */
+ toggleSourceEdit : function(sourceEditMode){
+ if(sourceEditMode === undefined){
+ sourceEditMode = !this.sourceEditMode;
+ }
+ this.sourceEditMode = sourceEditMode === true;
+ var btn = this.tb.getComponent('sourceedit');
+
+ if(btn.pressed !== this.sourceEditMode){
+ btn.toggle(this.sourceEditMode);
+ if(!btn.xtbHidden){
+ return;
+ }
+ }
+ if(this.sourceEditMode){
+ this.disableItems(true);
+ this.syncValue();
+ this.iframe.className = 'x-hidden';
+ this.el.removeClass('x-hidden');
+ this.el.dom.removeAttribute('tabIndex');
+ this.el.focus();
+ }else{
+ if(this.initialized){
+ this.disableItems(this.readOnly);
+ }
+ this.pushValue();
+ this.iframe.className = '';
+ this.el.addClass('x-hidden');
+ this.el.dom.setAttribute('tabIndex', -1);
+ this.deferFocus();
+ }
+ var lastSize = this.lastSize;
+ if(lastSize){
+ delete this.lastSize;
+ this.setSize(lastSize);
+ }
+ this.fireEvent('editmodechange', this, this.sourceEditMode);
+ },
+
+ // private used internally
+ createLink : function(){
+ var url = prompt(this.createLinkText, this.defaultLinkValue);
+ if(url && url != 'http:/'+'/'){
+ this.relayCmd('createlink', url);
+ }
+ },
+
+ // private
+ initEvents : function(){
+ this.originalValue = this.getValue();
+ },
+
+ /**
+ * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+ * @method
+ */
+ markInvalid : Ext.emptyFn,
+
+ /**
+ * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+ * @method
+ */
+ clearInvalid : Ext.emptyFn,
+
+ // docs inherit from Field
+ setValue : function(v){
+ Ext.form.HtmlEditor.superclass.setValue.call(this, v);
+ this.pushValue();
+ return this;
+ },
+
+ /**
+ * Protected method that will not generally be called directly. If you need/want
+ * custom HTML cleanup, this is the method you should override.
+ * @param {String} html The HTML to be cleaned
+ * @return {String} The cleaned HTML
+ */
+ cleanHtml: function(html) {
+ html = String(html);
+ if(Ext.isWebKit){ // strip safari nonsense
+ html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
+ }
+
+ /*
+ * Neat little hack. Strips out all the non-digit characters from the default
+ * value and compares it to the character code of the first character in the string
+ * because it can cause encoding issues when posted to the server.
+ */
+ if(html.charCodeAt(0) == this.defaultValue.replace(/\D/g, '')){
+ html = html.substring(1);
+ }
+ return html;
+ },
+
+ /**
+ * Protected method that will not generally be called directly. Syncs the contents
+ * of the editor iframe with the textarea.
+ */
+ syncValue : function(){
+ if(this.initialized){
+ var bd = this.getEditorBody();
+ var html = bd.innerHTML;
+ if(Ext.isWebKit){
+ var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
+ var m = bs.match(/text-align:(.*?);/i);
+ if(m && m[1]){
+ html = '
+{
+ bold : {
+ title: 'Bold (Ctrl+B)',
+ text: 'Make the selected text bold.',
+ cls: 'x-html-editor-tip'
+ },
+ italic : {
+ title: 'Italic (Ctrl+I)',
+ text: 'Make the selected text italic.',
+ cls: 'x-html-editor-tip'
+ },
+ ...
+
+ * @type Object
+ */
+ buttonTips : {
+ bold : {
+ title: 'Bold (Ctrl+B)',
+ text: 'Make the selected text bold.',
+ cls: 'x-html-editor-tip'
+ },
+ italic : {
+ title: 'Italic (Ctrl+I)',
+ text: 'Make the selected text italic.',
+ cls: 'x-html-editor-tip'
+ },
+ underline : {
+ title: 'Underline (Ctrl+U)',
+ text: 'Underline the selected text.',
+ cls: 'x-html-editor-tip'
+ },
+ increasefontsize : {
+ title: 'Grow Text',
+ text: 'Increase the font size.',
+ cls: 'x-html-editor-tip'
+ },
+ decreasefontsize : {
+ title: 'Shrink Text',
+ text: 'Decrease the font size.',
+ cls: 'x-html-editor-tip'
+ },
+ backcolor : {
+ title: 'Text Highlight Color',
+ text: 'Change the background color of the selected text.',
+ cls: 'x-html-editor-tip'
+ },
+ forecolor : {
+ title: 'Font Color',
+ text: 'Change the color of the selected text.',
+ cls: 'x-html-editor-tip'
+ },
+ justifyleft : {
+ title: 'Align Text Left',
+ text: 'Align text to the left.',
+ cls: 'x-html-editor-tip'
+ },
+ justifycenter : {
+ title: 'Center Text',
+ text: 'Center text in the editor.',
+ cls: 'x-html-editor-tip'
+ },
+ justifyright : {
+ title: 'Align Text Right',
+ text: 'Align text to the right.',
+ cls: 'x-html-editor-tip'
+ },
+ insertunorderedlist : {
+ title: 'Bullet List',
+ text: 'Start a bulleted list.',
+ cls: 'x-html-editor-tip'
+ },
+ insertorderedlist : {
+ title: 'Numbered List',
+ text: 'Start a numbered list.',
+ cls: 'x-html-editor-tip'
+ },
+ createlink : {
+ title: 'Hyperlink',
+ text: 'Make the selected text a hyperlink.',
+ cls: 'x-html-editor-tip'
+ },
+ sourceedit : {
+ title: 'Source Edit',
+ text: 'Switch to source editing mode.',
+ cls: 'x-html-editor-tip'
+ }
+ }
+
+ // hide stuff that is not compatible
+ /**
+ * @event blur
+ * @hide
+ */
+ /**
+ * @event change
+ * @hide
+ */
+ /**
+ * @event focus
+ * @hide
+ */
+ /**
+ * @event specialkey
+ * @hide
+ */
+ /**
+ * @cfg {String} fieldClass @hide
+ */
+ /**
+ * @cfg {String} focusClass @hide
+ */
+ /**
+ * @cfg {String} autoCreate @hide
+ */
+ /**
+ * @cfg {String} inputType @hide
+ */
+ /**
+ * @cfg {String} invalidClass @hide
+ */
+ /**
+ * @cfg {String} invalidText @hide
+ */
+ /**
+ * @cfg {String} msgFx @hide
+ */
+ /**
+ * @cfg {String} validateOnBlur @hide
+ */
+ /**
+ * @cfg {Boolean} allowDomMove @hide
+ */
+ /**
+ * @cfg {String} applyTo @hide
+ */
+ /**
+ * @cfg {String} autoHeight @hide
+ */
+ /**
+ * @cfg {String} autoWidth @hide
+ */
+ /**
+ * @cfg {String} cls @hide
+ */
+ /**
+ * @cfg {String} disabled @hide
+ */
+ /**
+ * @cfg {String} disabledClass @hide
+ */
+ /**
+ * @cfg {String} msgTarget @hide
+ */
+ /**
+ * @cfg {String} readOnly @hide
+ */
+ /**
+ * @cfg {String} style @hide
+ */
+ /**
+ * @cfg {String} validationDelay @hide
+ */
+ /**
+ * @cfg {String} validationEvent @hide
+ */
+ /**
+ * @cfg {String} tabIndex @hide
+ */
+ /**
+ * @property disabled
+ * @hide
+ */
+ /**
+ * @method applyToMarkup
+ * @hide
+ */
+ /**
+ * @method disable
+ * @hide
+ */
+ /**
+ * @method enable
+ * @hide
+ */
+ /**
+ * @method validate
+ * @hide
+ */
+ /**
+ * @event valid
+ * @hide
+ */
+ /**
+ * @method setDisabled
+ * @hide
+ */
+ /**
+ * @cfg keys
+ * @hide
+ */
+});
Ext.reg('htmleditor', Ext.form.HtmlEditor);/**
* @class Ext.form.TimeField
* @extends Ext.form.ComboBox
@@ -6852,15 +7159,15 @@ Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {
/**
* @cfg {Date/String} minValue
* The minimum allowed time. Can be either a Javascript date object with a valid time value or a string
- * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to null).
+ * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
*/
- minValue : null,
+ minValue : undefined,
/**
* @cfg {Date/String} maxValue
* The maximum allowed time. Can be either a Javascript date object with a valid time value or a string
- * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to null).
+ * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
*/
- maxValue : null,
+ maxValue : undefined,
/**
* @cfg {String} minText
* The error text to display when the date in the cell is before minValue (defaults to
@@ -6912,26 +7219,67 @@ Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {
// private
initComponent : function(){
- if(typeof this.minValue == "string"){
- this.minValue = this.parseDate(this.minValue);
+ if(Ext.isDefined(this.minValue)){
+ this.setMinValue(this.minValue, true);
}
- if(typeof this.maxValue == "string"){
- this.maxValue = this.parseDate(this.maxValue);
+ if(Ext.isDefined(this.maxValue)){
+ this.setMaxValue(this.maxValue, true);
}
-
if(!this.store){
- var min = this.parseDate(this.minValue) || new Date(this.initDate).clearTime();
- var max = this.parseDate(this.maxValue) || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1);
- var times = [];
- while(min <= max){
- times.push(min.dateFormat(this.format));
- min = min.add('mi', this.increment);
- }
- this.store = times;
+ this.generateStore(true);
}
Ext.form.TimeField.superclass.initComponent.call(this);
},
+
+ /**
+ * Replaces any existing {@link #minValue} with the new time and refreshes the store.
+ * @param {Date/String} value The minimum time that can be selected
+ */
+ setMinValue: function(value, /* private */ initial){
+ this.setLimit(value, true, initial);
+ return this;
+ },
+
+ /**
+ * Replaces any existing {@link #maxValue} with the new time and refreshes the store.
+ * @param {Date/String} value The maximum time that can be selected
+ */
+ setMaxValue: function(value, /* private */ initial){
+ this.setLimit(value, false, initial);
+ return this;
+ },
+
+ // private
+ generateStore: function(initial){
+ var min = this.minValue || new Date(this.initDate).clearTime(),
+ max = this.maxValue || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1),
+ times = [];
+
+ while(min <= max){
+ times.push(min.dateFormat(this.format));
+ min = min.add('mi', this.increment);
+ }
+ this.bindStore(times, initial);
+ },
+ // private
+ setLimit: function(value, isMin, initial){
+ var d;
+ if(Ext.isString(value)){
+ d = this.parseDate(value);
+ }else if(Ext.isDate(value)){
+ d = value;
+ }
+ if(d){
+ var val = new Date(this.initDate).clearTime();
+ val.setHours(d.getHours(), d.getMinutes(), isMin ? 0 : 59, 0);
+ this[isMin ? 'minValue' : 'maxValue'] = val;
+ if(!initial){
+ this.generateStore();
+ }
+ }
+ },
+
// inherited docs
getValue : function(){
var v = Ext.form.TimeField.superclass.getValue.call(this);
@@ -7006,773 +7354,773 @@ Ext.form.Label = Ext.extend(Ext.BoxComponent, {
this.el.setAttribute('for', this.forId);
}
}
- Ext.form.Label.superclass.onRender.call(this, ct, position);
+ Ext.form.Label.superclass.onRender.call(this, ct, position);
+ },
+
+ /**
+ * Updates the label's innerHTML with the specified string.
+ * @param {String} text The new label text
+ * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
+ * to the label (defaults to true which encodes the value). This might be useful if you want to include
+ * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
+ * @return {Label} this
+ */
+ setText : function(t, encode){
+ var e = encode === false;
+ this[!e ? 'text' : 'html'] = t;
+ delete this[e ? 'text' : 'html'];
+ if(this.rendered){
+ this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
+ }
+ return this;
+ }
+});
+
+Ext.reg('label', Ext.form.Label);/**
+ * @class Ext.form.Action
+ * The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.
+ *Instances of this class are only created by a {@link Ext.form.BasicForm Form} when + * the Form needs to perform an action such as submit or load. The Configuration options + * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit}, + * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}
+ *The instance of Action which performed the action is passed to the success + * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit}, + * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}), + * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and + * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.
+ */ +Ext.form.Action = function(form, options){ + this.form = form; + this.options = options || {}; +}; + +/** + * Failure type returned when client side validation of the Form fails + * thus aborting a submit action. Client side validation is performed unless + * {@link #clientValidation} is explicitly set to false. + * @type {String} + * @static + */ +Ext.form.Action.CLIENT_INVALID = 'client'; +/** + *Failure type returned when server side processing fails and the {@link #result}'s + * success property is set to false.
+ *In the case of a form submission, field-specific error messages may be returned in the + * {@link #result}'s errors property.
+ * @type {String} + * @static + */ +Ext.form.Action.SERVER_INVALID = 'server'; +/** + * Failure type returned when a communication error happens when attempting + * to send a request to the remote server. The {@link #response} may be examined to + * provide further information. + * @type {String} + * @static + */ +Ext.form.Action.CONNECT_FAILURE = 'connect'; +/** + * Failure type returned when the response's success + * property is set to false, or no field values are returned in the response's + * data property. + * @type {String} + * @static + */ +Ext.form.Action.LOAD_FAILURE = 'load'; + +Ext.form.Action.prototype = { +/** + * @cfg {String} url The URL that the Action is to invoke. + */ +/** + * @cfg {Boolean} reset When set to true, causes the Form to be + * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens + * before the {@link #success} callback is called and before the Form's + * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires. + */ +/** + * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the + * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method. + */ +/** + * @cfg {Mixed} paramsExtra parameter values to pass. These are added to the Form's + * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's + * input fields.
+ *Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
+ */ +/** + * @cfg {Number} timeout The number of seconds to wait for a server response before + * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified, + * defaults to the configured {@link Ext.form.BasicForm#timeout timeout} of the + * {@link Ext.form.BasicForm form}. + */ +/** + * @cfg {Function} success The function to call when a valid success return packet is recieved. + * The function is passed the following parameters:
+var fp = new Ext.form.FormPanel({
+...
+buttons: [{
+ text: 'Save',
+ formBind: true,
+ handler: function(){
+ if(fp.getForm().isValid()){
+ fp.getForm().submit({
+ url: 'form-submit.php',
+ waitMsg: 'Submitting your data...',
+ success: function(form, action){
+ // server responded with success = true
+ var result = action.{@link #result};
+ },
+ failure: function(form, action){
+ if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
+ Ext.Msg.alert('Error',
+ 'Status:'+action.{@link #response}.status+': '+
+ action.{@link #response}.statusText);
+ }
+ if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
+ // server responded with success = false
+ Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
+ }
+ }
+ });
+ }
+ }
+},{
+ text: 'Reset',
+ handler: function(){
+ fp.getForm().reset();
+ }
+}]
+ *
+ * @property failureType
+ * @type {String}
+ */
+ /**
+ * The XMLHttpRequest object used to perform the action.
+ * @property response
+ * @type {Object}
+ */
+ /**
+ * The decoded response object containing a boolean success property and
+ * other, action-specific properties.
+ * @property result
+ * @type {Object}
+ */
+
+ // interface method
+ run : function(options){
+
+ },
+
+ // interface method
+ success : function(response){
+
+ },
+
+ // interface method
+ handleResponse : function(response){
+
+ },
+
+ // default connection failure
+ failure : function(response){
+ this.response = response;
+ this.failureType = Ext.form.Action.CONNECT_FAILURE;
+ this.form.afterAction(this, false);
+ },
+
+ // private
+ // shared code among all Actions to validate that there was a response
+ // with either responseText or responseXml
+ processResponse : function(response){
+ this.response = response;
+ if(!response.responseText && !response.responseXML){
+ return true;
+ }
+ this.result = this.handleResponse(response);
+ return this.result;
+ },
+
+ // utility functions used internally
+ getUrl : function(appendParams){
+ var url = this.options.url || this.form.url || this.form.el.dom.action;
+ if(appendParams){
+ var p = this.getParams();
+ if(p){
+ url = Ext.urlAppend(url, p);
+ }
+ }
+ return url;
+ },
+
+ // private
+ getMethod : function(){
+ return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
+ },
+
+ // private
+ getParams : function(){
+ var bp = this.form.baseParams;
+ var p = this.options.params;
+ if(p){
+ if(typeof p == "object"){
+ p = Ext.urlEncode(Ext.applyIf(p, bp));
+ }else if(typeof p == 'string' && bp){
+ p += '&' + Ext.urlEncode(bp);
+ }
+ }else if(bp){
+ p = Ext.urlEncode(bp);
+ }
+ return p;
+ },
+
+ // private
+ createCallback : function(opts){
+ var opts = opts || {};
+ return {
+ success: this.success,
+ failure: this.failure,
+ scope: this,
+ timeout: (opts.timeout*1000) || (this.form.timeout*1000),
+ upload: this.form.fileUpload ? this.success : undefined
+ };
+ }
+};
+
+/**
+ * @class Ext.form.Action.Submit
+ * @extends Ext.form.Action
+ * A class which handles submission of data from {@link Ext.form.BasicForm Form}s + * and processes the returned response.
+ *Instances of this class are only created by a {@link Ext.form.BasicForm Form} when + * {@link Ext.form.BasicForm#submit submit}ting.
+ *Response Packet Criteria
+ *A response packet may contain: + *
success
property : Boolean
+ * success
property is required.errors
property : Object
+ * errors
property,
+ * which is optional, contains error messages for invalid fields.JSON Packets
+ *By default, response packets are assumed to be JSON, so a typical response + * packet may look like this:
+{
+ success: false,
+ errors: {
+ clientCode: "Client not found",
+ portOfLoading: "This field must not be null"
+ }
+}
+ * Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback + * or event handler methods. The object decoded from this JSON is available in the + * {@link Ext.form.Action#result result} property.
+ *Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:
+ errorReader: new Ext.data.XmlReader({
+ record : 'field',
+ success: '@success'
+ }, [
+ 'id', 'msg'
+ ]
+ )
+
+ * then the results may be sent back in XML format:
+<?xml version="1.0" encoding="UTF-8"?>
+<message success="false">
+<errors>
+ <field>
+ <id>clientCode</id>
+ <msg><![CDATA[Code not found. <br /><i>This is a test validation message from the server </i>]]></msg>
+ </field>
+ <field>
+ <id>portOfLoading</id>
+ <msg><![CDATA[Port not found. <br /><i>This is a test validation message from the server </i>]]></msg>
+ </field>
+</errors>
+</message>
+
+ * Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback + * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.
+ */ +Ext.form.Action.Submit = function(form, options){ + Ext.form.Action.Submit.superclass.constructor.call(this, form, options); +}; + +Ext.extend(Ext.form.Action.Submit, Ext.form.Action, { + /** + * @cfg {Ext.data.DataReader} errorReaderOptional. JSON is interpreted with + * no need for an errorReader.
+ *A Reader which reads a single record from the returned data. The DataReader's + * success property specifies how submission success is determined. The Record's + * data provides the error messages to apply to any invalid form Fields.
+ */ + /** + * @cfg {boolean} clientValidation Determines whether a Form's fields are validated + * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission. + * Pass false in the Form's submit options to prevent this. If not defined, pre-submission field validation + * is performed. + */ + type : 'submit', + + // private + run : function(){ + var o = this.options; + var method = this.getMethod(); + var isGet = method == 'GET'; + if(o.clientValidation === false || this.form.isValid()){ + Ext.Ajax.request(Ext.apply(this.createCallback(o), { + form:this.form.el.dom, + url:this.getUrl(isGet), + method: method, + headers: o.headers, + params:!isGet ? this.getParams() : null, + isUpload: this.form.fileUpload + })); + }else if (o.clientValidation !== false){ // client validation failed + this.failureType = Ext.form.Action.CLIENT_INVALID; + this.form.afterAction(this, false); + } + }, + + // private + success : function(response){ + var result = this.processResponse(response); + if(result === true || result.success){ + this.form.afterAction(this, true); + return; + } + if(result.errors){ + this.form.markInvalid(result.errors); + } + this.failureType = Ext.form.Action.SERVER_INVALID; + this.form.afterAction(this, false); + }, + + // private + handleResponse : function(response){ + if(this.form.errorReader){ + var rs = this.form.errorReader.read(response); + var errors = []; + if(rs.records){ + for(var i = 0, len = rs.records.length; i < len; i++) { + var r = rs.records[i]; + errors[i] = r.data; + } + } + if(errors.length < 1){ + errors = null; + } + return { + success : rs.success, + errors : errors + }; + } + return Ext.decode(response.responseText); + } +}); + + +/** + * @class Ext.form.Action.Load + * @extends Ext.form.Action + *A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.
+ *Instances of this class are only created by a {@link Ext.form.BasicForm Form} when + * {@link Ext.form.BasicForm#load load}ing.
+ *Response Packet Criteria
+ *A response packet must contain: + *
success
property : Booleandata
property : Objectdata
property contains the values of Fields to load.
+ * The individual value object for each Field is passed to the Field's
+ * {@link Ext.form.Field#setValue setValue} method.JSON Packets
+ *By default, response packets are assumed to be JSON, so for the following form load call:
+var myFormPanel = new Ext.form.FormPanel({
+ title: 'Client and routing info',
+ items: [{
+ fieldLabel: 'Client',
+ name: 'clientName'
+ }, {
+ fieldLabel: 'Port of loading',
+ name: 'portOfLoading'
+ }, {
+ fieldLabel: 'Port of discharge',
+ name: 'portOfDischarge'
+ }]
+});
+myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
+ url: '/getRoutingInfo.php',
+ params: {
+ consignmentRef: myConsignmentRef
+ },
+ failure: function(form, action) {
+ Ext.Msg.alert("Load failed", action.result.errorMessage);
+ }
+});
+
+ * a success response packet may look like this:
+{
+ success: true,
+ data: {
+ clientName: "Fred. Olsen Lines",
+ portOfLoading: "FXT",
+ portOfDischarge: "OSL"
+ }
+}
+ * while a failure response packet may look like this:
+{
+ success: false,
+ errorMessage: "Consignment reference not found"
+}
+ * Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s + * callback or event handler methods. The object decoded from this JSON is available in the + * {@link Ext.form.Action#result result} property.
+ */ +Ext.form.Action.Load = function(form, options){ + Ext.form.Action.Load.superclass.constructor.call(this, form, options); + this.reader = this.form.reader; +}; + +Ext.extend(Ext.form.Action.Load, Ext.form.Action, { + // private + type : 'load', + + // private + run : function(){ + Ext.Ajax.request(Ext.apply( + this.createCallback(this.options), { + method:this.getMethod(), + url:this.getUrl(false), + headers: this.options.headers, + params:this.getParams() + })); + }, + + // private + success : function(response){ + var result = this.processResponse(response); + if(result === true || !result.success || !result.data){ + this.failureType = Ext.form.Action.LOAD_FAILURE; + this.form.afterAction(this, false); + return; + } + this.form.clearInvalid(); + this.form.setValues(result.data); + this.form.afterAction(this, true); + }, + + // private + handleResponse : function(response){ + if(this.form.reader){ + var rs = this.form.reader.read(response); + var data = rs.records && rs.records[0] ? rs.records[0].data : null; + return { + success : rs.success, + data : data + }; + } + return Ext.decode(response.responseText); + } +}); + + + +/** + * @class Ext.form.Action.DirectLoad + * @extends Ext.form.Action.Load + *Provides Ext.direct support for loading form data.
+ *This example illustrates usage of Ext.Direct to load a form through Ext.Direct.
+ *
+var myFormPanel = new Ext.form.FormPanel({
+ // configs for FormPanel
+ title: 'Basic Information',
+ renderTo: document.body,
+ width: 300, height: 160,
+ padding: 10,
+
+ // configs apply to child items
+ defaults: {anchor: '100%'},
+ defaultType: 'textfield',
+ items: [{
+ fieldLabel: 'Name',
+ name: 'name'
+ },{
+ fieldLabel: 'Email',
+ name: 'email'
+ },{
+ fieldLabel: 'Company',
+ name: 'company'
+ }],
+
+ // configs for BasicForm
+ api: {
+ // The server-side method to call for load() requests
+ load: Profile.getBasicInfo,
+ // The server-side must mark the submit handler as a 'formHandler'
+ submit: Profile.updateBasicInfo
+ },
+ // specify the order for the passed params
+ paramOrder: ['uid', 'foo']
+});
+
+// load the form
+myFormPanel.getForm().load({
+ // pass 2 arguments to server side getBasicInfo method (len=2)
+ params: {
+ foo: 'bar',
+ uid: 34
+ }
+});
+ *
+ * The data packet sent to the server will resemble something like:
+ *
+[
+ {
+ "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
+ "data":[34,"bar"] // note the order of the params
+ }
+]
+ *
+ * The form will process a data packet returned by the server that is similar
+ * to the following format:
+ *
+[
+ {
+ "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
+ "result":{
+ "success":true,
+ "data":{
+ "name":"Fred Flintstone",
+ "company":"Slate Rock and Gravel",
+ "email":"fred.flintstone@slaterg.com"
+ }
+ }
+ }
+]
+ *
+ */
+Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
+ constructor: function(form, opts) {
+ Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
+ },
+ type : 'directload',
+
+ run : function(){
+ var args = this.getParams();
+ args.push(this.success, this);
+ this.form.api.load.apply(window, args);
+ },
+
+ getParams : function() {
+ var buf = [], o = {};
+ var bp = this.form.baseParams;
+ var p = this.options.params;
+ Ext.apply(o, p, bp);
+ var paramOrder = this.form.paramOrder;
+ if(paramOrder){
+ for(var i = 0, len = paramOrder.length; i < len; i++){
+ buf.push(o[paramOrder[i]]);
+ }
+ }else if(this.form.paramsAsHash){
+ buf.push(o);
+ }
+ return buf;
+ },
+ // Direct actions have already been processed and therefore
+ // we can directly set the result; Direct Actions do not have
+ // a this.response property.
+ processResponse : function(result) {
+ this.result = result;
+ return result;
},
+
+ success : function(response, trans){
+ if(trans.type == Ext.Direct.exceptions.SERVER){
+ response = {};
+ }
+ Ext.form.Action.DirectLoad.superclass.success.call(this, response);
+ }
+});
- /**
- * Updates the label's innerHTML with the specified string.
- * @param {String} text The new label text
- * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
- * to the label (defaults to true which encodes the value). This might be useful if you want to include
- * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
- * @return {Label} this
- */
- setText : function(t, encode){
- var e = encode === false;
- this[!e ? 'text' : 'html'] = t;
- delete this[e ? 'text' : 'html'];
- if(this.rendered){
- this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
+/**
+ * @class Ext.form.Action.DirectSubmit
+ * @extends Ext.form.Action.Submit
+ * Provides Ext.direct support for submitting form data.
+ *This example illustrates usage of Ext.Direct to submit a form through Ext.Direct.
+ *
+var myFormPanel = new Ext.form.FormPanel({
+ // configs for FormPanel
+ title: 'Basic Information',
+ renderTo: document.body,
+ width: 300, height: 160,
+ padding: 10,
+ buttons:[{
+ text: 'Submit',
+ handler: function(){
+ myFormPanel.getForm().submit({
+ params: {
+ foo: 'bar',
+ uid: 34
+ }
+ });
}
- return this;
+ }],
+
+ // configs apply to child items
+ defaults: {anchor: '100%'},
+ defaultType: 'textfield',
+ items: [{
+ fieldLabel: 'Name',
+ name: 'name'
+ },{
+ fieldLabel: 'Email',
+ name: 'email'
+ },{
+ fieldLabel: 'Company',
+ name: 'company'
+ }],
+
+ // configs for BasicForm
+ api: {
+ // The server-side method to call for load() requests
+ load: Profile.getBasicInfo,
+ // The server-side must mark the submit handler as a 'formHandler'
+ submit: Profile.updateBasicInfo
+ },
+ // specify the order for the passed params
+ paramOrder: ['uid', 'foo']
+});
+ *
+ * The data packet sent to the server will resemble something like:
+ *
+{
+ "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
+ "result":{
+ "success":true,
+ "id":{
+ "extAction":"Profile","extMethod":"updateBasicInfo",
+ "extType":"rpc","extTID":"6","extUpload":"false",
+ "name":"Aaron Conran","email":"aaron@extjs.com","company":"Ext JS, LLC"
+ }
+ }
+}
+ *
+ * The form will process a data packet returned by the server that is similar
+ * to the following:
+ *
+// sample success packet (batched requests)
+[
+ {
+ "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,
+ "result":{
+ "success":true
+ }
+ }
+]
+
+// sample failure packet (one request)
+{
+ "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
+ "result":{
+ "errors":{
+ "email":"already taken"
+ },
+ "success":false,
+ "foo":"bar"
+ }
+}
+ *
+ * Also see the discussion in {@link Ext.form.Action.DirectLoad}.
+ */
+Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
+ constructor : function(form, opts) {
+ Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
+ },
+ type : 'directsubmit',
+ // override of Submit
+ run : function(){
+ var o = this.options;
+ if(o.clientValidation === false || this.form.isValid()){
+ // tag on any additional params to be posted in the
+ // form scope
+ this.success.params = this.getParams();
+ this.form.api.submit(this.form.el.dom, this.success, this);
+ }else if (o.clientValidation !== false){ // client validation failed
+ this.failureType = Ext.form.Action.CLIENT_INVALID;
+ this.form.afterAction(this, false);
+ }
+ },
+
+ getParams : function() {
+ var o = {};
+ var bp = this.form.baseParams;
+ var p = this.options.params;
+ Ext.apply(o, p, bp);
+ return o;
+ },
+ // Direct actions have already been processed and therefore
+ // we can directly set the result; Direct Actions do not have
+ // a this.response property.
+ processResponse : function(result) {
+ this.result = result;
+ return result;
+ },
+
+ success : function(response, trans){
+ if(trans.type == Ext.Direct.exceptions.SERVER){
+ response = {};
+ }
+ Ext.form.Action.DirectSubmit.superclass.success.call(this, response);
}
});
-Ext.reg('label', Ext.form.Label);/**
- * @class Ext.form.Action
- * The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.
- *Instances of this class are only created by a {@link Ext.form.BasicForm Form} when - * the Form needs to perform an action such as submit or load. The Configuration options - * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit}, - * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}
- *The instance of Action which performed the action is passed to the success - * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit}, - * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}), - * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and - * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.
- */ -Ext.form.Action = function(form, options){ - this.form = form; - this.options = options || {}; -}; - -/** - * Failure type returned when client side validation of the Form fails - * thus aborting a submit action. Client side validation is performed unless - * {@link #clientValidation} is explicitly set to false. - * @type {String} - * @static - */ -Ext.form.Action.CLIENT_INVALID = 'client'; -/** - *Failure type returned when server side processing fails and the {@link #result}'s - * success property is set to false.
- *In the case of a form submission, field-specific error messages may be returned in the - * {@link #result}'s errors property.
- * @type {String} - * @static - */ -Ext.form.Action.SERVER_INVALID = 'server'; -/** - * Failure type returned when a communication error happens when attempting - * to send a request to the remote server. The {@link #response} may be examined to - * provide further information. - * @type {String} - * @static - */ -Ext.form.Action.CONNECT_FAILURE = 'connect'; -/** - * Failure type returned when the response's success - * property is set to false, or no field values are returned in the response's - * data property. - * @type {String} - * @static - */ -Ext.form.Action.LOAD_FAILURE = 'load'; - -Ext.form.Action.prototype = { -/** - * @cfg {String} url The URL that the Action is to invoke. - */ -/** - * @cfg {Boolean} reset When set to true, causes the Form to be - * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens - * before the {@link #success} callback is called and before the Form's - * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires. - */ -/** - * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the - * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method. - */ -/** - * @cfg {Mixed} paramsExtra parameter values to pass. These are added to the Form's - * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's - * input fields.
- *Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
- */ -/** - * @cfg {Number} timeout The number of seconds to wait for a server response before - * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified, - * defaults to the configured {@link Ext.form.BasicForm#timeout timeout} of the - * {@link Ext.form.BasicForm form}. - */ -/** - * @cfg {Function} success The function to call when a valid success return packet is recieved. - * The function is passed the following parameters:
-var fp = new Ext.form.FormPanel({
-...
-buttons: [{
- text: 'Save',
- formBind: true,
- handler: function(){
- if(fp.getForm().isValid()){
- fp.getForm().submit({
- url: 'form-submit.php',
- waitMsg: 'Submitting your data...',
- success: function(form, action){
- // server responded with success = true
- var result = action.{@link #result};
- },
- failure: function(form, action){
- if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
- Ext.Msg.alert('Error',
- 'Status:'+action.{@link #response}.status+': '+
- action.{@link #response}.statusText);
- }
- if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
- // server responded with success = false
- Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
- }
- }
- });
- }
- }
-},{
- text: 'Reset',
- handler: function(){
- fp.getForm().reset();
- }
-}]
- *
- * @property failureType
- * @type {String}
- */
- /**
- * The XMLHttpRequest object used to perform the action.
- * @property response
- * @type {Object}
- */
- /**
- * The decoded response object containing a boolean success property and
- * other, action-specific properties.
- * @property result
- * @type {Object}
- */
-
- // interface method
- run : function(options){
-
- },
-
- // interface method
- success : function(response){
-
- },
-
- // interface method
- handleResponse : function(response){
-
- },
-
- // default connection failure
- failure : function(response){
- this.response = response;
- this.failureType = Ext.form.Action.CONNECT_FAILURE;
- this.form.afterAction(this, false);
- },
-
- // private
- // shared code among all Actions to validate that there was a response
- // with either responseText or responseXml
- processResponse : function(response){
- this.response = response;
- if(!response.responseText && !response.responseXML){
- return true;
- }
- this.result = this.handleResponse(response);
- return this.result;
- },
-
- // utility functions used internally
- getUrl : function(appendParams){
- var url = this.options.url || this.form.url || this.form.el.dom.action;
- if(appendParams){
- var p = this.getParams();
- if(p){
- url = Ext.urlAppend(url, p);
- }
- }
- return url;
- },
-
- // private
- getMethod : function(){
- return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
- },
-
- // private
- getParams : function(){
- var bp = this.form.baseParams;
- var p = this.options.params;
- if(p){
- if(typeof p == "object"){
- p = Ext.urlEncode(Ext.applyIf(p, bp));
- }else if(typeof p == 'string' && bp){
- p += '&' + Ext.urlEncode(bp);
- }
- }else if(bp){
- p = Ext.urlEncode(bp);
- }
- return p;
- },
-
- // private
- createCallback : function(opts){
- var opts = opts || {};
- return {
- success: this.success,
- failure: this.failure,
- scope: this,
- timeout: (opts.timeout*1000) || (this.form.timeout*1000),
- upload: this.form.fileUpload ? this.success : undefined
- };
- }
-};
-
-/**
- * @class Ext.form.Action.Submit
- * @extends Ext.form.Action
- * A class which handles submission of data from {@link Ext.form.BasicForm Form}s - * and processes the returned response.
- *Instances of this class are only created by a {@link Ext.form.BasicForm Form} when - * {@link Ext.form.BasicForm#submit submit}ting.
- *Response Packet Criteria
- *A response packet may contain: - *
success
property : Boolean
- * success
property is required.errors
property : Object
- * errors
property,
- * which is optional, contains error messages for invalid fields.JSON Packets
- *By default, response packets are assumed to be JSON, so a typical response - * packet may look like this:
-{
- success: false,
- errors: {
- clientCode: "Client not found",
- portOfLoading: "This field must not be null"
- }
-}
- * Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback - * or event handler methods. The object decoded from this JSON is available in the - * {@link Ext.form.Action#result result} property.
- *Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:
- errorReader: new Ext.data.XmlReader({
- record : 'field',
- success: '@success'
- }, [
- 'id', 'msg'
- ]
- )
-
- * then the results may be sent back in XML format:
-<?xml version="1.0" encoding="UTF-8"?>
-<message success="false">
-<errors>
- <field>
- <id>clientCode</id>
- <msg><![CDATA[Code not found. <br /><i>This is a test validation message from the server </i>]]></msg>
- </field>
- <field>
- <id>portOfLoading</id>
- <msg><![CDATA[Port not found. <br /><i>This is a test validation message from the server </i>]]></msg>
- </field>
-</errors>
-</message>
-
- * Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback - * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.
- */ -Ext.form.Action.Submit = function(form, options){ - Ext.form.Action.Submit.superclass.constructor.call(this, form, options); -}; - -Ext.extend(Ext.form.Action.Submit, Ext.form.Action, { - /** - * @cfg {Ext.data.DataReader} errorReaderOptional. JSON is interpreted with - * no need for an errorReader.
- *A Reader which reads a single record from the returned data. The DataReader's - * success property specifies how submission success is determined. The Record's - * data provides the error messages to apply to any invalid form Fields.
- */ - /** - * @cfg {boolean} clientValidation Determines whether a Form's fields are validated - * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission. - * Pass false in the Form's submit options to prevent this. If not defined, pre-submission field validation - * is performed. - */ - type : 'submit', - - // private - run : function(){ - var o = this.options; - var method = this.getMethod(); - var isGet = method == 'GET'; - if(o.clientValidation === false || this.form.isValid()){ - Ext.Ajax.request(Ext.apply(this.createCallback(o), { - form:this.form.el.dom, - url:this.getUrl(isGet), - method: method, - headers: o.headers, - params:!isGet ? this.getParams() : null, - isUpload: this.form.fileUpload - })); - }else if (o.clientValidation !== false){ // client validation failed - this.failureType = Ext.form.Action.CLIENT_INVALID; - this.form.afterAction(this, false); - } - }, - - // private - success : function(response){ - var result = this.processResponse(response); - if(result === true || result.success){ - this.form.afterAction(this, true); - return; - } - if(result.errors){ - this.form.markInvalid(result.errors); - } - this.failureType = Ext.form.Action.SERVER_INVALID; - this.form.afterAction(this, false); - }, - - // private - handleResponse : function(response){ - if(this.form.errorReader){ - var rs = this.form.errorReader.read(response); - var errors = []; - if(rs.records){ - for(var i = 0, len = rs.records.length; i < len; i++) { - var r = rs.records[i]; - errors[i] = r.data; - } - } - if(errors.length < 1){ - errors = null; - } - return { - success : rs.success, - errors : errors - }; - } - return Ext.decode(response.responseText); - } -}); - - -/** - * @class Ext.form.Action.Load - * @extends Ext.form.Action - *A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.
- *Instances of this class are only created by a {@link Ext.form.BasicForm Form} when - * {@link Ext.form.BasicForm#load load}ing.
- *Response Packet Criteria
- *A response packet must contain: - *
success
property : Booleandata
property : Objectdata
property contains the values of Fields to load.
- * The individual value object for each Field is passed to the Field's
- * {@link Ext.form.Field#setValue setValue} method.JSON Packets
- *By default, response packets are assumed to be JSON, so for the following form load call:
-var myFormPanel = new Ext.form.FormPanel({
- title: 'Client and routing info',
- items: [{
- fieldLabel: 'Client',
- name: 'clientName'
- }, {
- fieldLabel: 'Port of loading',
- name: 'portOfLoading'
- }, {
- fieldLabel: 'Port of discharge',
- name: 'portOfDischarge'
- }]
-});
-myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
- url: '/getRoutingInfo.php',
- params: {
- consignmentRef: myConsignmentRef
- },
- failure: function(form, action) {
- Ext.Msg.alert("Load failed", action.result.errorMessage);
- }
-});
-
- * a success response packet may look like this:
-{
- success: true,
- data: {
- clientName: "Fred. Olsen Lines",
- portOfLoading: "FXT",
- portOfDischarge: "OSL"
- }
-}
- * while a failure response packet may look like this:
-{
- success: false,
- errorMessage: "Consignment reference not found"
-}
- * Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s - * callback or event handler methods. The object decoded from this JSON is available in the - * {@link Ext.form.Action#result result} property.
- */ -Ext.form.Action.Load = function(form, options){ - Ext.form.Action.Load.superclass.constructor.call(this, form, options); - this.reader = this.form.reader; -}; - -Ext.extend(Ext.form.Action.Load, Ext.form.Action, { - // private - type : 'load', - - // private - run : function(){ - Ext.Ajax.request(Ext.apply( - this.createCallback(this.options), { - method:this.getMethod(), - url:this.getUrl(false), - headers: this.options.headers, - params:this.getParams() - })); - }, - - // private - success : function(response){ - var result = this.processResponse(response); - if(result === true || !result.success || !result.data){ - this.failureType = Ext.form.Action.LOAD_FAILURE; - this.form.afterAction(this, false); - return; - } - this.form.clearInvalid(); - this.form.setValues(result.data); - this.form.afterAction(this, true); - }, - - // private - handleResponse : function(response){ - if(this.form.reader){ - var rs = this.form.reader.read(response); - var data = rs.records && rs.records[0] ? rs.records[0].data : null; - return { - success : rs.success, - data : data - }; - } - return Ext.decode(response.responseText); - } -}); - - - -/** - * @class Ext.form.Action.DirectLoad - * @extends Ext.form.Action.Load - *Provides Ext.direct support for loading form data.
- *This example illustrates usage of Ext.Direct to load a form through Ext.Direct.
- *
-var myFormPanel = new Ext.form.FormPanel({
- // configs for FormPanel
- title: 'Basic Information',
- renderTo: document.body,
- width: 300, height: 160,
- padding: 10,
-
- // configs apply to child items
- defaults: {anchor: '100%'},
- defaultType: 'textfield',
- items: [{
- fieldLabel: 'Name',
- name: 'name'
- },{
- fieldLabel: 'Email',
- name: 'email'
- },{
- fieldLabel: 'Company',
- name: 'company'
- }],
-
- // configs for BasicForm
- api: {
- // The server-side method to call for load() requests
- load: Profile.getBasicInfo,
- // The server-side must mark the submit handler as a 'formHandler'
- submit: Profile.updateBasicInfo
- },
- // specify the order for the passed params
- paramOrder: ['uid', 'foo']
-});
-
-// load the form
-myFormPanel.getForm().load({
- // pass 2 arguments to server side getBasicInfo method (len=2)
- params: {
- foo: 'bar',
- uid: 34
- }
-});
- *
- * The data packet sent to the server will resemble something like:
- *
-[
- {
- "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
- "data":[34,"bar"] // note the order of the params
- }
-]
- *
- * The form will process a data packet returned by the server that is similar
- * to the following format:
- *
-[
- {
- "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
- "result":{
- "success":true,
- "data":{
- "name":"Fred Flintstone",
- "company":"Slate Rock and Gravel",
- "email":"fred.flintstone@slaterg.com"
- }
- }
- }
-]
- *
- */
-Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
- constructor: function(form, opts) {
- Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
- },
- type : 'directload',
-
- run : function(){
- var args = this.getParams();
- args.push(this.success, this);
- this.form.api.load.apply(window, args);
- },
-
- getParams : function() {
- var buf = [], o = {};
- var bp = this.form.baseParams;
- var p = this.options.params;
- Ext.apply(o, p, bp);
- var paramOrder = this.form.paramOrder;
- if(paramOrder){
- for(var i = 0, len = paramOrder.length; i < len; i++){
- buf.push(o[paramOrder[i]]);
- }
- }else if(this.form.paramsAsHash){
- buf.push(o);
- }
- return buf;
- },
- // Direct actions have already been processed and therefore
- // we can directly set the result; Direct Actions do not have
- // a this.response property.
- processResponse : function(result) {
- this.result = result;
- return result;
- },
-
- success : function(response, trans){
- if(trans.type == Ext.Direct.exceptions.SERVER){
- response = {};
- }
- Ext.form.Action.DirectLoad.superclass.success.call(this, response);
- }
-});
-
-/**
- * @class Ext.form.Action.DirectSubmit
- * @extends Ext.form.Action.Submit
- * Provides Ext.direct support for submitting form data.
- *This example illustrates usage of Ext.Direct to submit a form through Ext.Direct.
- *
-var myFormPanel = new Ext.form.FormPanel({
- // configs for FormPanel
- title: 'Basic Information',
- renderTo: document.body,
- width: 300, height: 160,
- padding: 10,
- buttons:[{
- text: 'Submit',
- handler: function(){
- myFormPanel.getForm().submit({
- params: {
- foo: 'bar',
- uid: 34
- }
- });
- }
- }],
-
- // configs apply to child items
- defaults: {anchor: '100%'},
- defaultType: 'textfield',
- items: [{
- fieldLabel: 'Name',
- name: 'name'
- },{
- fieldLabel: 'Email',
- name: 'email'
- },{
- fieldLabel: 'Company',
- name: 'company'
- }],
-
- // configs for BasicForm
- api: {
- // The server-side method to call for load() requests
- load: Profile.getBasicInfo,
- // The server-side must mark the submit handler as a 'formHandler'
- submit: Profile.updateBasicInfo
- },
- // specify the order for the passed params
- paramOrder: ['uid', 'foo']
-});
- *
- * The data packet sent to the server will resemble something like:
- *
-{
- "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
- "result":{
- "success":true,
- "id":{
- "extAction":"Profile","extMethod":"updateBasicInfo",
- "extType":"rpc","extTID":"6","extUpload":"false",
- "name":"Aaron Conran","email":"aaron@extjs.com","company":"Ext JS, LLC"
- }
- }
-}
- *
- * The form will process a data packet returned by the server that is similar
- * to the following:
- *
-// sample success packet (batched requests)
-[
- {
- "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,
- "result":{
- "success":true
- }
- }
-]
-
-// sample failure packet (one request)
-{
- "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
- "result":{
- "errors":{
- "email":"already taken"
- },
- "success":false,
- "foo":"bar"
- }
-}
- *
- * Also see the discussion in {@link Ext.form.Action.DirectLoad}.
- */
-Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
- constructor : function(form, opts) {
- Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
- },
- type : 'directsubmit',
- // override of Submit
- run : function(){
- var o = this.options;
- if(o.clientValidation === false || this.form.isValid()){
- // tag on any additional params to be posted in the
- // form scope
- this.success.params = this.getParams();
- this.form.api.submit(this.form.el.dom, this.success, this);
- }else if (o.clientValidation !== false){ // client validation failed
- this.failureType = Ext.form.Action.CLIENT_INVALID;
- this.form.afterAction(this, false);
- }
- },
-
- getParams : function() {
- var o = {};
- var bp = this.form.baseParams;
- var p = this.options.params;
- Ext.apply(o, p, bp);
- return o;
- },
- // Direct actions have already been processed and therefore
- // we can directly set the result; Direct Actions do not have
- // a this.response property.
- processResponse : function(result) {
- this.result = result;
- return result;
- },
-
- success : function(response, trans){
- if(trans.type == Ext.Direct.exceptions.SERVER){
- response = {};
- }
- Ext.form.Action.DirectSubmit.superclass.success.call(this, response);
- }
-});
-
-Ext.form.Action.ACTION_TYPES = {
- 'load' : Ext.form.Action.Load,
- 'submit' : Ext.form.Action.Submit,
- 'directload' : Ext.form.Action.DirectLoad,
- 'directsubmit' : Ext.form.Action.DirectSubmit
-};
+Ext.form.Action.ACTION_TYPES = {
+ 'load' : Ext.form.Action.Load,
+ 'submit' : Ext.form.Action.Submit,
+ 'directload' : Ext.form.Action.DirectLoad,
+ 'directsubmit' : Ext.form.Action.DirectSubmit
+};
/**
* @class Ext.form.VTypes
* This is a singleton object which contains a set of commonly used field validation functions. @@ -7811,7 +8159,7 @@ Ext.form.VTypes = function(){ // closure these in so they are only created once. var alpha = /^[a-zA-Z_]+$/, alphanum = /^[a-zA-Z0-9_]+$/, - email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,4}$/, + email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/, url = /(((^https?)|(^ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@`~=%!]*)(\.\w{2,})?)*\/?)/i; // All these messages and functions are configurable