X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/25ef3491bd9ae007ff1fc2b0d7943e6eaaccf775..6e39d509471fe9b4e2660e0d1631b350d0c66f40:/pkgs/pkg-forms-debug.js diff --git a/pkgs/pkg-forms-debug.js b/pkgs/pkg-forms-debug.js index a866f1e3..aa61db0d 100644 --- a/pkgs/pkg-forms-debug.js +++ b/pkgs/pkg-forms-debug.js @@ -1,5 +1,5 @@ /*! - * Ext JS Library 3.0.3 + * Ext JS Library 3.1.0 * Copyright(c) 2006-2009 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(); @@ -400,11 +430,21 @@ var form = new Ext.form.FormPanel({ validateValue : function(value){ return true; }, + + /** + * 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 || ''; + }, /** - * 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 + *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',
+
+ removeMode: 'container',
+
+ 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;
+ this.updateEditState();
+ },
+
+ 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;
- Ext.fly(ts).setWidth(this.el.getWidth());
- if(v.length < 1){
+ },
+
+ /**
+ * @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);
+ },
+
+ // 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
+ }
+});
+
+// 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);
+ }
+ }
+ },
+
+ 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.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.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 ?
+ '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.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
+ beforeDestroy: function(){
+ Ext.destroy(this.panel);
+ Ext.form.CheckboxGroup.superclass.beforeDestroy.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
@@ -4141,118 +4290,122 @@ Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
}
});
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.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
+ 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
@@ -4321,9 +4474,9 @@ Ext.form.BasicForm = function(el, config){
this.paramOrder = this.paramOrder.split(/[\s,|]/);
}
/**
- * @property items
- * A {@link Ext.util.MixedCollection MixedCollection) containing all the Ext.form.Fields in this form.
+ * 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();
@@ -4464,7 +4617,7 @@ paramOrder: 'param1|param2|param'
* {@link #paramOrder} nullifies this configuration.
*/
paramsAsHash: false,
-
+
/**
* @cfg {String} waitTitle
* The default title to show for the waiting message box (defaults to 'Please Wait...')
@@ -4915,10 +5068,33 @@ myFormPanel.getForm().submit({
return Ext.urlDecode(fs);
},
- getFieldValues : function(){
- var o = {};
+ /**
+ * 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){
- o[f.getName()] = f.getValue();
+ 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;
},
@@ -5028,13 +5204,13 @@ 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: @@ -5042,11 +5218,11 @@ Ext.BasicForm = Ext.form.BasicForm;/** *
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 {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 @@ -5130,7 +5306,7 @@ Ext.FormPanel = Ext.extend(Ext.Panel, { monitorPoll : 200, /** - * @cfg {String} layout Defaults to 'form'. Normally this configuration property should not be altered. + * @cfg {String} layout Defaults to 'form'. Normally this configuration property should not be altered. * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}. */ layout : 'form', @@ -5150,7 +5326,7 @@ Ext.FormPanel = Ext.extend(Ext.Panel, { this.bodyCfg.enctype = 'multipart/form-data'; } this.initItems(); - + this.addEvents( /** * @event clientvalidation @@ -5187,7 +5363,7 @@ Ext.FormPanel = Ext.extend(Ext.Panel, { }; this.items.each(fn, this); }, - + // private applySettings: function(c){ var ct = c.ownerCt; @@ -5217,21 +5393,20 @@ Ext.FormPanel = Ext.extend(Ext.Panel, { Ext.FormPanel.superclass.onRender.call(this, ct, position); this.form.initEl(this.body); }, - + // 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); + this.form.items.clear(); + Ext.FormPanel.superclass.beforeDestroy.call(this); }, - // Determine if a Component is usable as a form Field. + // Determine if a Component is usable as a form Field. isField : function(c) { return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid; }, @@ -5249,52 +5424,55 @@ Ext.FormPanel = Ext.extend(Ext.Panel, { this.startMonitoring(); } }, - + // private onAdd: function(c){ - Ext.FormPanel.superclass.onAdd.call(this, c); + Ext.FormPanel.superclass.onAdd.call(this, c); this.processAdd(c); }, - + // private onAddEvent: function(ct, c){ if(ct !== this){ this.processAdd(c); } }, - + // private processAdd : function(c){ - // If a single form Field, add it + // If a single form Field, add it if(this.isField(c)){ this.form.add(c); - // If a Container, add any Fields it might contain + // 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)); } }, - + // private onRemove: function(c){ Ext.FormPanel.superclass.onRemove.call(this, c); this.processRemove(c); }, - + onRemoveEvent: function(ct, c){ if(ct !== this){ this.processRemove(c); } }, - + // private processRemove : function(c){ - // If a single form Field, remove it + // If a single form Field, remove it if(this.isField(c)){ - this.form.remove(c); - // If a Container, remove any Fields it might contain + this.form.remove(c); + // If a Container, its already destroyed by the time it gets here. Remove any references to destroyed fields. }else if(c.findBy){ - Ext.each(c.findBy(this.isField), this.form.remove, this.form); + var isDestroyed = function(o) { + return !!o.isDestroyed; + } + this.form.items.filterBy(isDestroyed, this.form).each(this.form.remove, this.form); } }, @@ -5328,7 +5506,7 @@ Ext.FormPanel = Ext.extend(Ext.Panel, { * @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); + this.form.load.apply(this.form, arguments); }, // private @@ -5375,7 +5553,6 @@ Ext.FormPanel = Ext.extend(Ext.Panel, { Ext.reg('form', Ext.FormPanel); Ext.form.FormPanel = Ext.FormPanel; - /** * @class Ext.form.FieldSet * @extends Ext.Panel @@ -5433,1405 +5610,1463 @@ var form = new Ext.FormPanel({ }] }); * - * @constructor - * @param {Object} config Configuration options - * @xtype fieldset - */ -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,
-
- // 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);
- }
- },
-
- // private
- onCollapse : function(doAnim, animArg){
- if(this.checkbox){
- this.checkbox.dom.checked = false;
- }
- Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);
-
- },
-
- // 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']();
- }
-
- /**
- * @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
- */
-});
-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,
+ * @constructor
+ * @param {Object} config Configuration options
+ * @xtype fieldset
+ */
+Ext.form.FieldSet = Ext.extend(Ext.Panel, {
/**
- * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
+ * @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'}
+ *
*/
- enableSourceEdit : true,
/**
- * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
+ * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if {@link #checkboxToggle} = true
+ * (defaults to '[checkbox id]-checkbox').
*/
- enableLinks : true,
/**
- * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
+ * @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}.
*/
- enableFont : true,
/**
- * @cfg {String} createLinkText The default text for the create link prompt
+ * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
*/
- createLinkText : 'Please enter the URL for the link:',
/**
- * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
+ * @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.
*/
- defaultLinkValue : 'http:/'+'/',
/**
- * @cfg {Array} fontFamilies An array of available font families
+ * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to 'x-fieldset').
*/
- fontFamilies : [
- 'Arial',
- 'Courier New',
- 'Tahoma',
- 'Times New Roman',
- 'Verdana'
- ],
- defaultFont: 'tahoma',
+ baseCls : 'x-fieldset',
/**
- * @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 {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to 'form').
*/
- 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;
- },
-
+ layout : 'form',
/**
- * 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 {Boolean} animCollapse
+ * true to animate the transition when the panel is collapsed, false to skip the
+ * animation (defaults to false).
*/
- 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];
- },
+ animCollapse : false,
// 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
- */
- 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
- */
- 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
+ * 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){
+ if(this.initialized){
+ var newDM = readOnly ? 'off' : 'on',
+ doc = this.getDoc();
+ if(String(doc.designMode).toLowerCase() != newDM){
+ doc.designMode = newDM;
+ }
+ this.disableItems(!readOnly);
+ }
+ Ext.form.HtmlEditor.superclass.setReadOnly.call(this, 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).
+ */
+ 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);
+ // 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);
+ 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);
+ 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';
+ 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.readOnly){
+ 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
+ */
+ 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 +7087,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 +7147,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 +7282,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 +8087,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