X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/ee06f37b0f6f6d94cd05a6ffae556660f7c4a2bc..c930e9176a5a85509c5b0230e2bff5c22a591432:/pkgs/cmp-foundation-debug.js diff --git a/pkgs/cmp-foundation-debug.js b/pkgs/cmp-foundation-debug.js new file mode 100644 index 00000000..94a91ae5 --- /dev/null +++ b/pkgs/cmp-foundation-debug.js @@ -0,0 +1,11287 @@ +/*! + * Ext JS Library 3.0.0 + * Copyright(c) 2006-2009 Ext JS, LLC + * licensing@extjs.com + * http://www.extjs.com/license + */ +/** + * @class Ext.ComponentMgr + *

Provides a registry of all Components (instances of {@link Ext.Component} or any subclass + * thereof) on a page so that they can be easily accessed by {@link Ext.Component component} + * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).

+ *

This object also provides a registry of available Component classes + * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}. + * The {@link Ext.Component#xtype xtype} provides a way to avoid instantiating child Components + * when creating a full, nested config object for a complete Ext page.

+ *

A child Component may be specified simply as a config object + * as long as the correct {@link Ext.Component#xtype xtype} is specified so that if and when the Component + * needs rendering, the correct type can be looked up for lazy instantiation.

+ *

For a list of all available {@link Ext.Component#xtype xtypes}, see {@link Ext.Component}.

+ * @singleton + */ +Ext.ComponentMgr = function(){ + var all = new Ext.util.MixedCollection(); + var types = {}; + var ptypes = {}; + + return { + /** + * Registers a component. + * @param {Ext.Component} c The component + */ + register : function(c){ + all.add(c); + }, + + /** + * Unregisters a component. + * @param {Ext.Component} c The component + */ + unregister : function(c){ + all.remove(c); + }, + + /** + * Returns a component by {@link Ext.Component#id id}. + * For additional details see {@link Ext.util.MixedCollection#get}. + * @param {String} id The component {@link Ext.Component#id id} + * @return Ext.Component The Component, undefined if not found, or null if a + * Class was found. + */ + get : function(id){ + return all.get(id); + }, + + /** + * Registers a function that will be called when a specified component is added to ComponentMgr + * @param {String} id The component {@link Ext.Component#id id} + * @param {Function} fn The callback function + * @param {Object} scope The scope of the callback + */ + onAvailable : function(id, fn, scope){ + all.on("add", function(index, o){ + if(o.id == id){ + fn.call(scope || o, o); + all.un("add", fn, scope); + } + }); + }, + + /** + * The MixedCollection used internally for the component cache. An example usage may be subscribing to + * events on the MixedCollection to monitor addition or removal. Read-only. + * @type {MixedCollection} + */ + all : all, + + /** + * Checks if a Component type is registered. + * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up + * @return {Boolean} Whether the type is registered. + */ + isRegistered : function(xtype){ + return types[xtype] !== undefined; + }, + + /** + *

Registers a new Component constructor, keyed by a new + * {@link Ext.Component#xtype}.

+ *

Use this method (or its alias {@link Ext#reg Ext.reg}) to register new + * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying + * child Components. + * see {@link Ext.Container#items}

+ * @param {String} xtype The mnemonic string by which the Component class may be looked up. + * @param {Constructor} cls The new Component class. + */ + registerType : function(xtype, cls){ + types[xtype] = cls; + cls.xtype = xtype; + }, + + /** + * Creates a new Component from the specified config object using the + * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate. + * @param {Object} config A configuration object for the Component you wish to create. + * @param {Constructor} defaultType The constructor to provide the default Component type if + * the config object does not contain a xtype. (Optional if the config contains a xtype). + * @return {Ext.Component} The newly instantiated Component. + */ + create : function(config, defaultType){ + return config.render ? config : new types[config.xtype || defaultType](config); + }, + + /** + *

Registers a new Plugin constructor, keyed by a new + * {@link Ext.Component#ptype}.

+ *

Use this method (or its alias {@link Ext#preg Ext.preg}) to register new + * plugins for {@link Ext.Component}s so that lazy instantiation may be used when specifying + * Plugins.

+ * @param {String} ptype The mnemonic string by which the Plugin class may be looked up. + * @param {Constructor} cls The new Plugin class. + */ + registerPlugin : function(ptype, cls){ + ptypes[ptype] = cls; + cls.ptype = ptype; + }, + + /** + * Creates a new Plugin from the specified config object using the + * config object's {@link Ext.component#ptype ptype} to determine the class to instantiate. + * @param {Object} config A configuration object for the Plugin you wish to create. + * @param {Constructor} defaultType The constructor to provide the default Plugin type if + * the config object does not contain a ptype. (Optional if the config contains a ptype). + * @return {Ext.Component} The newly instantiated Plugin. + */ + createPlugin : function(config, defaultType){ + return new ptypes[config.ptype || defaultType](config); + } + }; +}(); + +/** + * Shorthand for {@link Ext.ComponentMgr#registerType} + * @param {String} xtype The {@link Ext.component#xtype mnemonic string} by which the Component class + * may be looked up. + * @param {Constructor} cls The new Component class. + * @member Ext + * @method reg + */ +Ext.reg = Ext.ComponentMgr.registerType; // this will be called a lot internally, shorthand to keep the bytes down +/** + * Shorthand for {@link Ext.ComponentMgr#registerPlugin} + * @param {String} ptype The {@link Ext.component#ptype mnemonic string} by which the Plugin class + * may be looked up. + * @param {Constructor} cls The new Plugin class. + * @member Ext + * @method preg + */ +Ext.preg = Ext.ComponentMgr.registerPlugin; +Ext.create = Ext.ComponentMgr.create; +/** + * @class Ext.Component + * @extends Ext.util.Observable + *

Base class for all Ext components. All subclasses of Component may participate in the automated + * Ext component lifecycle of creation, rendering and destruction which is provided by the {@link Ext.Container Container} class. + * Components may be added to a Container through the {@link Ext.Container#items items} config option at the time the Container is created, + * or they may be added dynamically via the {@link Ext.Container#add add} method.

+ *

The Component base class has built-in support for basic hide/show and enable/disable behavior.

+ *

All Components are registered with the {@link Ext.ComponentMgr} on construction so that they can be referenced at any time via + * {@link Ext#getCmp}, passing the {@link #id}.

+ *

All user-developed visual widgets that are required to participate in automated lifecycle and size management should subclass Component (or + * {@link Ext.BoxComponent} if managed box model handling is required, ie height and width management).

+ *

See the Creating new UI controls tutorial for details on how + * and to either extend or augment ExtJs base classes to create custom Components.

+ *

Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the + * xtype like {@link #getXType} and {@link #isXType}. This is the list of all valid xtypes:

+ *
+xtype            Class
+-------------    ------------------
+box              {@link Ext.BoxComponent}
+button           {@link Ext.Button}
+buttongroup      {@link Ext.ButtonGroup}
+colorpalette     {@link Ext.ColorPalette}
+component        {@link Ext.Component}
+container        {@link Ext.Container}
+cycle            {@link Ext.CycleButton}
+dataview         {@link Ext.DataView}
+datepicker       {@link Ext.DatePicker}
+editor           {@link Ext.Editor}
+editorgrid       {@link Ext.grid.EditorGridPanel}
+flash            {@link Ext.FlashComponent}
+grid             {@link Ext.grid.GridPanel}
+listview         {@link Ext.ListView}
+panel            {@link Ext.Panel}
+progress         {@link Ext.ProgressBar}
+propertygrid     {@link Ext.grid.PropertyGrid}
+slider           {@link Ext.Slider}
+spacer           {@link Ext.Spacer}
+splitbutton      {@link Ext.SplitButton}
+tabpanel         {@link Ext.TabPanel}
+treepanel        {@link Ext.tree.TreePanel}
+viewport         {@link Ext.ViewPort}
+window           {@link Ext.Window}
+
+Toolbar components
+---------------------------------------
+paging           {@link Ext.PagingToolbar}
+toolbar          {@link Ext.Toolbar}
+tbbutton         {@link Ext.Toolbar.Button}        (deprecated; use button)
+tbfill           {@link Ext.Toolbar.Fill}
+tbitem           {@link Ext.Toolbar.Item}
+tbseparator      {@link Ext.Toolbar.Separator}
+tbspacer         {@link Ext.Toolbar.Spacer}
+tbsplit          {@link Ext.Toolbar.SplitButton}   (deprecated; use splitbutton)
+tbtext           {@link Ext.Toolbar.TextItem}
+
+Menu components
+---------------------------------------
+menu             {@link Ext.menu.Menu}
+colormenu        {@link Ext.menu.ColorMenu}
+datemenu         {@link Ext.menu.DateMenu}
+menubaseitem     {@link Ext.menu.BaseItem}
+menucheckitem    {@link Ext.menu.CheckItem}
+menuitem         {@link Ext.menu.Item}
+menuseparator    {@link Ext.menu.Separator}
+menutextitem     {@link Ext.menu.TextItem}
+
+Form components
+---------------------------------------
+form             {@link Ext.FormPanel}
+checkbox         {@link Ext.form.Checkbox}
+checkboxgroup    {@link Ext.form.CheckboxGroup}
+combo            {@link Ext.form.ComboBox}
+datefield        {@link Ext.form.DateField}
+displayfield     {@link Ext.form.DisplayField}
+field            {@link Ext.form.Field}
+fieldset         {@link Ext.form.FieldSet}
+hidden           {@link Ext.form.Hidden}
+htmleditor       {@link Ext.form.HtmlEditor}
+label            {@link Ext.form.Label}
+numberfield      {@link Ext.form.NumberField}
+radio            {@link Ext.form.Radio}
+radiogroup       {@link Ext.form.RadioGroup}
+textarea         {@link Ext.form.TextArea}
+textfield        {@link Ext.form.TextField}
+timefield        {@link Ext.form.TimeField}
+trigger          {@link Ext.form.TriggerField}
+
+Chart components
+---------------------------------------
+chart            {@link Ext.chart.Chart}
+barchart         {@link Ext.chart.BarChart}
+cartesianchart   {@link Ext.chart.CartesianChart}
+columnchart      {@link Ext.chart.ColumnChart}
+linechart        {@link Ext.chart.LineChart}
+piechart         {@link Ext.chart.PieChart}
+
+Store xtypes
+---------------------------------------
+arraystore       {@link Ext.data.ArrayStore}
+directstore      {@link Ext.data.DirectStore}
+groupingstore    {@link Ext.data.GroupingStore}
+jsonstore        {@link Ext.data.JsonStore}
+simplestore      {@link Ext.data.SimpleStore}      (deprecated; use arraystore)
+store            {@link Ext.data.Store}
+xmlstore         {@link Ext.data.XmlStore}
+
+ * @constructor + * @param {Ext.Element/String/Object} config The configuration options may be specified as either: + *
+ */ +Ext.Component = function(config){ + config = config || {}; + if(config.initialConfig){ + if(config.isAction){ // actions + this.baseAction = config; + } + config = config.initialConfig; // component cloning / action set up + }else if(config.tagName || config.dom || Ext.isString(config)){ // element object + config = {applyTo: config, id: config.id || config}; + } + + /** + * This Component's initial configuration specification. Read-only. + * @type Object + * @property initialConfig + */ + this.initialConfig = config; + + Ext.apply(this, config); + this.addEvents( + /** + * @event disable + * Fires after the component is disabled. + * @param {Ext.Component} this + */ + 'disable', + /** + * @event enable + * Fires after the component is enabled. + * @param {Ext.Component} this + */ + 'enable', + /** + * @event beforeshow + * Fires before the component is shown by calling the {@link #show} method. + * Return false from an event handler to stop the show. + * @param {Ext.Component} this + */ + 'beforeshow', + /** + * @event show + * Fires after the component is shown when calling the {@link #show} method. + * @param {Ext.Component} this + */ + 'show', + /** + * @event beforehide + * Fires before the component is hidden by calling the {@link #hide} method. + * Return false from an event handler to stop the hide. + * @param {Ext.Component} this + */ + 'beforehide', + /** + * @event hide + * Fires after the component is hidden. + * Fires after the component is hidden when calling the {@link #hide} method. + * @param {Ext.Component} this + */ + 'hide', + /** + * @event beforerender + * Fires before the component is {@link #rendered}. Return false from an + * event handler to stop the {@link #render}. + * @param {Ext.Component} this + */ + 'beforerender', + /** + * @event render + * Fires after the component markup is {@link #rendered}. + * @param {Ext.Component} this + */ + 'render', + /** + * @event afterrender + *

Fires after the component rendering is finished.

+ *

The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed + * by any afterRender method defined for the Component, and, if {@link #stateful}, after state + * has been restored.

+ * @param {Ext.Component} this + */ + 'afterrender', + /** + * @event beforedestroy + * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the {@link #destroy}. + * @param {Ext.Component} this + */ + 'beforedestroy', + /** + * @event destroy + * Fires after the component is {@link #destroy}ed. + * @param {Ext.Component} this + */ + 'destroy', + /** + * @event beforestaterestore + * Fires before the state of the component is restored. Return false from an event handler to stop the restore. + * @param {Ext.Component} this + * @param {Object} state The hash of state values returned from the StateProvider. If this + * event is not vetoed, then the state object is passed to applyState. By default, + * that simply copies property values into this Component. The method maybe overriden to + * provide custom state restoration. + */ + 'beforestaterestore', + /** + * @event staterestore + * Fires after the state of the component is restored. + * @param {Ext.Component} this + * @param {Object} state The hash of state values returned from the StateProvider. This is passed + * to applyState. By default, that simply copies property values into this + * Component. The method maybe overriden to provide custom state restoration. + */ + 'staterestore', + /** + * @event beforestatesave + * Fires before the state of the component is saved to the configured state provider. Return false to stop the save. + * @param {Ext.Component} this + * @param {Object} state The hash of state values. This is determined by calling + * getState() on the Component. This method must be provided by the + * developer to return whetever representation of state is required, by default, Ext.Component + * has a null implementation. + */ + 'beforestatesave', + /** + * @event statesave + * Fires after the state of the component is saved to the configured state provider. + * @param {Ext.Component} this + * @param {Object} state The hash of state values. This is determined by calling + * getState() on the Component. This method must be provided by the + * developer to return whetever representation of state is required, by default, Ext.Component + * has a null implementation. + */ + 'statesave' + ); + this.getId(); + Ext.ComponentMgr.register(this); + Ext.Component.superclass.constructor.call(this); + + if(this.baseAction){ + this.baseAction.addComponent(this); + } + + this.initComponent(); + + if(this.plugins){ + if(Ext.isArray(this.plugins)){ + for(var i = 0, len = this.plugins.length; i < len; i++){ + this.plugins[i] = this.initPlugin(this.plugins[i]); + } + }else{ + this.plugins = this.initPlugin(this.plugins); + } + } + + if(this.stateful !== false){ + this.initState(config); + } + + if(this.applyTo){ + this.applyToMarkup(this.applyTo); + delete this.applyTo; + }else if(this.renderTo){ + this.render(this.renderTo); + delete this.renderTo; + } +}; + +// private +Ext.Component.AUTO_ID = 1000; + +Ext.extend(Ext.Component, Ext.util.Observable, { + // Configs below are used for all Components when rendered by FormLayout. + /** + * @cfg {String} fieldLabel

The label text to display next to this Component (defaults to '').

+ *

Note: this config is only used when this Component is rendered by a Container which + * has been configured to use the {@link Ext.layout.FormLayout FormLayout} layout manager (e.g. + * {@link Ext.form.FormPanel} or specifying layout:'form').


+ *

Also see {@link #hideLabel} and + * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.

+ * Example use:

+new Ext.FormPanel({
+    height: 100,
+    renderTo: Ext.getBody(),
+    items: [{
+        xtype: 'textfield',
+        fieldLabel: 'Name'
+    }]
+});
+
+ */ + /** + * @cfg {String} labelStyle

A CSS style specification string to apply directly to this field's + * label. Defaults to the container's labelStyle value if set (e.g., + * {@link Ext.layout.FormLayout#labelStyle} , or '').

+ *

Note: see the note for {@link #clearCls}.


+ *

Also see {@link #hideLabel} and + * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.

+ * Example use:

+new Ext.FormPanel({
+    height: 100,
+    renderTo: Ext.getBody(),
+    items: [{
+        xtype: 'textfield',
+        fieldLabel: 'Name',
+        labelStyle: 'font-weight:bold;'
+    }]
+});
+
+ */ + /** + * @cfg {String} labelSeparator

The separator to display after the text of each + * {@link #fieldLabel}. This property may be configured at various levels. + * The order of precedence is: + *

+ * To display no separator for this field's label specify empty string ''.

+ *

Note: see the note for {@link #clearCls}.


+ *

Also see {@link #hideLabel} and + * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.

+ * Example use:

+new Ext.FormPanel({
+    height: 100,
+    renderTo: Ext.getBody(),
+    layoutConfig: {
+        labelSeparator: '~'   // layout config has lowest priority (defaults to ':')
+    },
+    {@link Ext.layout.FormLayout#labelSeparator labelSeparator}: '>>',     // config at container level
+    items: [{
+        xtype: 'textfield',
+        fieldLabel: 'Field 1',
+        labelSeparator: '...' // field/component level config supersedes others
+    },{
+        xtype: 'textfield',
+        fieldLabel: 'Field 2' // labelSeparator will be '='
+    }]
+});
+
+ */ + /** + * @cfg {Boolean} hideLabel

true to completely hide the label element + * ({@link #fieldLabel label} and {@link #labelSeparator separator}). Defaults to false. + * By default, even if you do not specify a {@link #fieldLabel} the space will still be + * reserved so that the field will line up with other fields that do have labels. + * Setting this to true will cause the field to not reserve that space.

+ *

Note: see the note for {@link #clearCls}.


+ * Example use:

+new Ext.FormPanel({
+    height: 100,
+    renderTo: Ext.getBody(),
+    items: [{
+        xtype: 'textfield'
+        hideLabel: true
+    }]
+});
+
+ */ + /** + * @cfg {String} clearCls

The CSS class used to to apply to the special clearing div rendered + * directly after each form field wrapper to provide field clearing (defaults to + * 'x-form-clear-left').

+ *

Note: this config is only used when this Component is rendered by a Container + * which has been configured to use the {@link Ext.layout.FormLayout FormLayout} layout + * manager (e.g. {@link Ext.form.FormPanel} or specifying layout:'form') and either a + * {@link #fieldLabel} is specified or isFormField=true is specified.


+ *

See {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} also.

+ */ + /** + * @cfg {String} itemCls

An additional CSS class to apply to the div wrapping the form item + * element of this field. If supplied, itemCls at the field level will override + * the default itemCls supplied at the container level. The value specified for + * itemCls will be added to the default class ('x-form-item').

+ *

Since it is applied to the item wrapper (see + * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}), it allows + * you to write standard CSS rules that can apply to the field, the label (if specified), or + * any other element within the markup for the field.

+ *

Note: see the note for {@link #fieldLabel}.


+ * Example use:

+// Apply a style to the field's label:
+<style>
+    .required .x-form-item-label {font-weight:bold;color:red;}
+</style>
+
+new Ext.FormPanel({
+	height: 100,
+	renderTo: Ext.getBody(),
+	items: [{
+		xtype: 'textfield',
+		fieldLabel: 'Name',
+		itemCls: 'required' //this label will be styled
+	},{
+		xtype: 'textfield',
+		fieldLabel: 'Favorite Color'
+	}]
+});
+
+ */ + + // Configs below are used for all Components when rendered by AnchorLayout. + /** + * @cfg {String} anchor

Note: this config is only used when this Component is rendered + * by a Container which has been configured to use an {@link Ext.layout.AnchorLayout AnchorLayout} + * based layout manager, for example:

+ *

See {@link Ext.layout.AnchorLayout}.{@link Ext.layout.AnchorLayout#anchor anchor} also.

+ */ + + /** + * @cfg {String} id + *

The unique id of this component (defaults to an {@link #getId auto-assigned id}). + * You should assign an id if you need to be able to access the component later and you do + * not have an object reference available (e.g., using {@link Ext}.{@link Ext#getCmp getCmp}).

+ *

Note that this id will also be used as the element id for the containing HTML element + * that is rendered to the page for this component. This allows you to write id-based CSS + * rules to style the specific instance of this component uniquely, and also to select + * sub-elements using this component's id as the parent.

+ *

Note: to avoid complications imposed by a unique id also see + * {@link #itemId} and {@link #ref}.

+ *

Note: to access the container of an item see {@link #ownerCt}.

+ */ + /** + * @cfg {String} itemId + *

An itemId can be used as an alternative way to get a reference to a component + * when no object reference is available. Instead of using an {@link #id} with + * {@link Ext}.{@link Ext#getCmp getCmp}, use itemId with + * {@link Ext.Container}.{@link Ext.Container#getComponent getComponent} which will retrieve + * itemId's or {@link #id}'s. Since itemId's are an index to the + * container's internal MixedCollection, the itemId is scoped locally to the container -- + * avoiding potential conflicts with {@link Ext.ComponentMgr} which requires a unique + * {@link #id}.

+ *

+var c = new Ext.Panel({ //
+    {@link Ext.BoxComponent#height height}: 300,
+    {@link #renderTo}: document.body,
+    {@link Ext.Container#layout layout}: 'auto',
+    {@link Ext.Container#items items}: [
+        {
+            itemId: 'p1',
+            {@link Ext.Panel#title title}: 'Panel 1',
+            {@link Ext.BoxComponent#height height}: 150
+        },
+        {
+            itemId: 'p2',
+            {@link Ext.Panel#title title}: 'Panel 2',
+            {@link Ext.BoxComponent#height height}: 150
+        }
+    ]
+})
+p1 = c.{@link Ext.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
+p2 = p1.{@link #ownerCt}.{@link Ext.Container#getComponent getComponent}('p2'); // reference via a sibling
+     * 
+ *

Also see {@link #id} and {@link #ref}.

+ *

Note: to access the container of an item see {@link #ownerCt}.

+ */ + /** + * @cfg {String} xtype + * The registered xtype to create. This config option is not used when passing + * a config object into a constructor. This config option is used only when + * lazy instantiation is being used, and a child item of a Container is being + * specified not as a fully instantiated Component, but as a Component config + * object. The xtype will be looked up at render time up to determine what + * type of child Component to create.

+ * The predefined xtypes are listed {@link Ext.Component here}. + *

+ * If you subclass Components to create your own Components, you may register + * them using {@link Ext.ComponentMgr#registerType} in order to be able to + * take advantage of lazy instantiation and rendering. + */ + /** + * @cfg {String} ptype + * The registered ptype to create. This config option is not used when passing + * a config object into a constructor. This config option is used only when + * lazy instantiation is being used, and a Plugin is being + * specified not as a fully instantiated Component, but as a Component config + * object. The ptype will be looked up at render time up to determine what + * type of Plugin to create.

+ * If you create your own Plugins, you may register them using + * {@link Ext.ComponentMgr#registerPlugin} in order to be able to + * take advantage of lazy instantiation and rendering. + */ + /** + * @cfg {String} cls + * An optional extra CSS class that will be added to this component's Element (defaults to ''). This can be + * useful for adding customized styles to the component or any of its children using standard CSS rules. + */ + /** + * @cfg {String} overCls + * An optional extra CSS class that will be added to this component's Element when the mouse moves + * over the Element, and removed when the mouse moves out. (defaults to ''). This can be + * useful for adding customized 'active' or 'hover' styles to the component or any of its children using standard CSS rules. + */ + /** + * @cfg {String} style + * A custom style specification to be applied to this component's Element. Should be a valid argument to + * {@link Ext.Element#applyStyles}. + *

+new Ext.Panel({
+    title: 'Some Title',
+    renderTo: Ext.getBody(),
+    width: 400, height: 300,
+    layout: 'form',
+    items: [{
+        xtype: 'textarea',
+        style: {
+            width: '95%',
+            marginBottom: '10px'
+        }
+    },
+        new Ext.Button({
+            text: 'Send',
+            minWidth: '100',
+            style: {
+                marginBottom: '10px'
+            }
+        })
+    ]
+});
+     * 
+ */ + /** + * @cfg {String} ctCls + *

An optional extra CSS class that will be added to this component's container. This can be useful for + * adding customized styles to the container or any of its children using standard CSS rules. See + * {@link Ext.layout.ContainerLayout}.{@link Ext.layout.ContainerLayout#extraCls extraCls} also.

+ *

Note: ctCls defaults to '' except for the following class + * which assigns a value by default: + *

+ * To configure the above Class with an extra CSS class append to the default. For example, + * for BoxLayout (Hbox and Vbox):

+     * ctCls: 'x-box-layout-ct custom-class'
+     * 
+ *

+ */ + /** + * @cfg {Boolean} disabled + * Render this component disabled (default is false). + */ + disabled : false, + /** + * @cfg {Boolean} hidden + * Render this component hidden (default is false). If true, the + * {@link #hide} method will be called internally. + */ + hidden : false, + /** + * @cfg {Object/Array} plugins + * An object or array of objects that will provide custom functionality for this component. The only + * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component. + * When a component is created, if any plugins are available, the component will call the init method on each + * plugin, passing a reference to itself. Each plugin can then call methods or respond to events on the + * component as needed to provide its functionality. + */ + /** + * @cfg {Mixed} applyTo + *

Specify the id of the element, a DOM element or an existing Element corresponding to a DIV + * that is already present in the document that specifies some structural markup for this + * component.

+ */ + /** + * @cfg {Mixed} renderTo + *

Specify the id of the element, a DOM element or an existing Element that this component + * will be rendered into.

+ *

See {@link #render} also.

+ */ + /** + * @cfg {Boolean} stateful + *

A flag which causes the Component to attempt to restore the state of + * internal properties from a saved state on startup. The component must have + * either a {@link #stateId} or {@link #id} assigned + * for state to be managed. Auto-generated ids are not guaranteed to be stable + * across page loads and cannot be relied upon to save and restore the same + * state for a component.

+ *

For state saving to work, the state manager's provider must have been + * set to an implementation of {@link Ext.state.Provider} which overrides the + * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get} + * methods to save and recall name/value pairs. A built-in implementation, + * {@link Ext.state.CookieProvider} is available.

+ *

To set the state provider for the current page:

+ *

+Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
+    expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
+}));
+     * 
+ *

A stateful Component attempts to save state when one of the events + * listed in the {@link #stateEvents} configuration fires.

+ *

To save state, a stateful Component first serializes its state by + * calling getState. By default, this function does + * nothing. The developer must provide an implementation which returns an + * object hash which represents the Component's restorable state.

+ *

The value yielded by getState is passed to {@link Ext.state.Manager#set} + * which uses the configured {@link Ext.state.Provider} to save the object + * keyed by the Component's {@link stateId}, or, if that is not + * specified, its {@link #id}.

+ *

During construction, a stateful Component attempts to restore + * its state by calling {@link Ext.state.Manager#get} passing the + * {@link #stateId}, or, if that is not specified, the + * {@link #id}.

+ *

The resulting object is passed to applyState. + * The default implementation of applyState simply copies + * properties into the object, but a developer may override this to support + * more behaviour.

+ *

You can perform extra processing on state save and restore by attaching + * handlers to the {@link #beforestaterestore}, {@link #staterestore}, + * {@link #beforestatesave} and {@link #statesave} events.

+ */ + /** + * @cfg {String} stateId + * The unique id for this component to use for state management purposes + * (defaults to the component id if one was set, otherwise null if the + * component is using a generated id). + *

See {@link #stateful} for an explanation of saving and + * restoring Component state.

+ */ + /** + * @cfg {Array} stateEvents + *

An array of events that, when fired, should trigger this component to + * save its state (defaults to none). stateEvents may be any type + * of event supported by this component, including browser or custom events + * (e.g., ['click', 'customerchange']).

+ *

See {@link #stateful} for an explanation of saving and + * restoring Component state.

+ */ + /** + * @cfg {Mixed} autoEl + *

A tag name or {@link Ext.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will + * encapsulate this Component.

+ *

You do not normally need to specify this. For the base classes {@link Ext.Component}, {@link Ext.BoxComponent}, + * and {@link Ext.Container}, this defaults to 'div'. The more complex Ext classes use a more complex + * DOM structure created by their own onRender methods.

+ *

This is intended to allow the developer to create application-specific utility Components encapsulated by + * different DOM elements. Example usage:


+{
+    xtype: 'box',
+    autoEl: {
+        tag: 'img',
+        src: 'http://www.example.com/example.jpg'
+    }
+}, {
+    xtype: 'box',
+    autoEl: {
+        tag: 'blockquote',
+        html: 'autoEl is cool!'
+    }
+}, {
+    xtype: 'container',
+    autoEl: 'ul',
+    cls: 'ux-unordered-list',
+    items: {
+        xtype: 'box',
+        autoEl: 'li',
+        html: 'First list item'
+    }
+}
+
+ */ + autoEl : 'div', + + /** + * @cfg {String} disabledClass + * CSS class added to the component when it is disabled (defaults to 'x-item-disabled'). + */ + disabledClass : 'x-item-disabled', + /** + * @cfg {Boolean} allowDomMove + * Whether the component can move the Dom node when rendering (defaults to true). + */ + allowDomMove : true, + /** + * @cfg {Boolean} autoShow + * True if the component should check for hidden classes (e.g. 'x-hidden' or 'x-hide-display') and remove + * them on render (defaults to false). + */ + autoShow : false, + /** + * @cfg {String} hideMode + *

How this component should be hidden. Supported values are 'visibility' + * (css visibility), 'offsets' (negative offset position) and 'display' + * (css display).

+ *

Note: the default of 'display' is generally preferred + * since items are automatically laid out when they are first shown (no sizing + * is done while hidden).

+ */ + hideMode : 'display', + /** + * @cfg {Boolean} hideParent + * True to hide and show the component's container when hide/show is called on the component, false to hide + * and show the component itself (defaults to false). For example, this can be used as a shortcut for a hide + * button on a window by setting hide:true on the button when adding it to its parent container. + */ + hideParent : false, + /** + *

The {@link Ext.Element} which encapsulates this Component. Read-only.

+ *

This will usually be a <DIV> element created by the class's onRender method, but + * that may be overridden using the {@link #autoEl} config.

+ *

Note: this element will not be available until this Component has been rendered.


+ *

To add listeners for DOM events to this Component (as opposed to listeners + * for this Component's own Observable events), see the {@link Ext.util.Observable#listeners listeners} + * config for a suggestion, or use a render listener directly:


+new Ext.Panel({
+    title: 'The Clickable Panel',
+    listeners: {
+        render: function(p) {
+            // Append the Panel to the click handler's argument list.
+            p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
+        },
+        single: true  // Remove the listener after first invocation
+    }
+});
+
+ *

See also {@link #getEl getEl}

+ * @type Ext.Element + * @property el + */ + /** + * The component's owner {@link Ext.Container} (defaults to undefined, and is set automatically when + * the component is added to a container). Read-only. + *

Note: to access items within the container see {@link #itemId}.

+ * @type Ext.Container + * @property ownerCt + */ + /** + * True if this component is hidden. Read-only. + * @type Boolean + * @property + */ + /** + * True if this component is disabled. Read-only. + * @type Boolean + * @property + */ + /** + * True if this component has been rendered. Read-only. + * @type Boolean + * @property + */ + rendered : false, + + // private + ctype : 'Ext.Component', + + // private + actionMode : 'el', + + // private + getActionEl : function(){ + return this[this.actionMode]; + }, + + initPlugin : function(p){ + if(p.ptype && !Ext.isFunction(p.init)){ + p = Ext.ComponentMgr.createPlugin(p); + }else if(Ext.isString(p)){ + p = Ext.ComponentMgr.createPlugin({ + ptype: p + }); + } + p.init(this); + return p; + }, + + /* // protected + * Function to be implemented by Component subclasses to be part of standard component initialization flow (it is empty by default). + *

+// Traditional constructor:
+Ext.Foo = function(config){
+    // call superclass constructor:
+    Ext.Foo.superclass.constructor.call(this, config);
+
+    this.addEvents({
+        // add events
+    });
+};
+Ext.extend(Ext.Foo, Ext.Bar, {
+   // class body
+}
+
+// initComponent replaces the constructor:
+Ext.Foo = Ext.extend(Ext.Bar, {
+    initComponent : function(){
+        // call superclass initComponent
+        Ext.Container.superclass.initComponent.call(this);
+
+        this.addEvents({
+            // add events
+        });
+    }
+}
+
+ */ + initComponent : Ext.emptyFn, + + /** + *

Render this Component into the passed HTML element.

+ *

If you are using a {@link Ext.Container Container} object to house this Component, then + * do not use the render method.

+ *

A Container's child Components are rendered by that Container's + * {@link Ext.Container#layout layout} manager when the Container is first rendered.

+ *

Certain layout managers allow dynamic addition of child components. Those that do + * include {@link Ext.layout.CardLayout}, {@link Ext.layout.AnchorLayout}, + * {@link Ext.layout.FormLayout}, {@link Ext.layout.TableLayout}.

+ *

If the Container is already rendered when a new child Component is added, you may need to call + * the Container's {@link Ext.Container#doLayout doLayout} to refresh the view which causes any + * unrendered child Components to be rendered. This is required so that you can add multiple + * child components if needed while only refreshing the layout once.

+ *

When creating complex UIs, it is important to remember that sizing and positioning + * of child items is the responsibility of the Container's {@link Ext.Container#layout layout} manager. + * If you expect child items to be sized in response to user interactions, you must + * configure the Container with a layout manager which creates and manages the type of layout you + * have in mind.

+ *

Omitting the Container's {@link Ext.Container#layout layout} config means that a basic + * layout manager is used which does nothing but render child components sequentially into the + * Container. No sizing or positioning will be performed in this situation.

+ * @param {Element/HTMLElement/String} container (optional) The element this Component should be + * rendered into. If it is being created from existing markup, this should be omitted. + * @param {String/Number} position (optional) The element ID or DOM node index within the container before + * which this component will be inserted (defaults to appending to the end of the container) + */ + render : function(container, position){ + if(!this.rendered && this.fireEvent('beforerender', this) !== false){ + if(!container && this.el){ + this.el = Ext.get(this.el); + container = this.el.dom.parentNode; + this.allowDomMove = false; + } + this.container = Ext.get(container); + if(this.ctCls){ + this.container.addClass(this.ctCls); + } + this.rendered = true; + if(position !== undefined){ + if(Ext.isNumber(position)){ + position = this.container.dom.childNodes[position]; + }else{ + position = Ext.getDom(position); + } + } + this.onRender(this.container, position || null); + if(this.autoShow){ + this.el.removeClass(['x-hidden','x-hide-' + this.hideMode]); + } + if(this.cls){ + this.el.addClass(this.cls); + delete this.cls; + } + if(this.style){ + this.el.applyStyles(this.style); + delete this.style; + } + if(this.overCls){ + this.el.addClassOnOver(this.overCls); + } + this.fireEvent('render', this); + this.afterRender(this.container); + if(this.hidden){ + // call this so we don't fire initial hide events. + this.doHide(); + } + if(this.disabled){ + // pass silent so the event doesn't fire the first time. + this.disable(true); + } + + if(this.stateful !== false){ + this.initStateEvents(); + } + this.initRef(); + this.fireEvent('afterrender', this); + } + return this; + }, + + initRef : function(){ + /** + * @cfg {String} ref + *

A path specification, relative to the Component's {@link #ownerCt} specifying into which + * ancestor Container to place a named reference to this Component.

+ *

The ancestor axis can be traversed by using '/' characters in the path. + * For example, to put a reference to a Toolbar Button into the Panel which owns the Toolbar:


+var myGrid = new Ext.grid.EditorGridPanel({
+    title: 'My EditorGridPanel',
+    store: myStore,
+    colModel: myColModel,
+    tbar: [{
+        text: 'Save',
+        handler: saveChanges,
+        disabled: true,
+        ref: '../saveButton'
+    }],
+    listeners: {
+        afteredit: function() {
+//          The button reference is in the GridPanel
+            myGrid.saveButton.enable();
+        }
+    }
+});
+
+ *

In the code above, if the ref had been 'saveButton' the reference would + * have been placed into the Toolbar. Each '/' in the ref moves up one level from the + * Component's {@link #ownerCt}.

+ */ + if(this.ref){ + var levels = this.ref.split('/'); + var last = levels.length, i = 0; + var t = this; + while(i < last){ + if(t.ownerCt){ + t = t.ownerCt; + } + i++; + } + t[levels[--i]] = this; + } + }, + + // private + initState : function(config){ + if(Ext.state.Manager){ + var id = this.getStateId(); + if(id){ + var state = Ext.state.Manager.get(id); + if(state){ + if(this.fireEvent('beforestaterestore', this, state) !== false){ + this.applyState(state); + this.fireEvent('staterestore', this, state); + } + } + } + } + }, + + // private + getStateId : function(){ + return this.stateId || ((this.id.indexOf('ext-comp-') == 0 || this.id.indexOf('ext-gen') == 0) ? null : this.id); + }, + + // private + initStateEvents : function(){ + if(this.stateEvents){ + for(var i = 0, e; e = this.stateEvents[i]; i++){ + this.on(e, this.saveState, this, {delay:100}); + } + } + }, + + // private + applyState : function(state, config){ + if(state){ + Ext.apply(this, state); + } + }, + + // private + getState : function(){ + return null; + }, + + // private + saveState : function(){ + if(Ext.state.Manager && this.stateful !== false){ + var id = this.getStateId(); + if(id){ + var state = this.getState(); + if(this.fireEvent('beforestatesave', this, state) !== false){ + Ext.state.Manager.set(id, state); + this.fireEvent('statesave', this, state); + } + } + } + }, + + /** + * Apply this component to existing markup that is valid. With this function, no call to render() is required. + * @param {String/HTMLElement} el + */ + applyToMarkup : function(el){ + this.allowDomMove = false; + this.el = Ext.get(el); + this.render(this.el.dom.parentNode); + }, + + /** + * Adds a CSS class to the component's underlying element. + * @param {string} cls The CSS class name to add + * @return {Ext.Component} this + */ + addClass : function(cls){ + if(this.el){ + this.el.addClass(cls); + }else{ + this.cls = this.cls ? this.cls + ' ' + cls : cls; + } + return this; + }, + + /** + * Removes a CSS class from the component's underlying element. + * @param {string} cls The CSS class name to remove + * @return {Ext.Component} this + */ + removeClass : function(cls){ + if(this.el){ + this.el.removeClass(cls); + }else if(this.cls){ + this.cls = this.cls.split(' ').remove(cls).join(' '); + } + return this; + }, + + // private + // default function is not really useful + onRender : function(ct, position){ + if(!this.el && this.autoEl){ + if(Ext.isString(this.autoEl)){ + this.el = document.createElement(this.autoEl); + }else{ + var div = document.createElement('div'); + Ext.DomHelper.overwrite(div, this.autoEl); + this.el = div.firstChild; + } + if (!this.el.id) { + this.el.id = this.getId(); + } + } + if(this.el){ + this.el = Ext.get(this.el); + if(this.allowDomMove !== false){ + ct.dom.insertBefore(this.el.dom, position); + } + } + }, + + // private + getAutoCreate : function(){ + var cfg = Ext.isObject(this.autoCreate) ? + this.autoCreate : Ext.apply({}, this.defaultAutoCreate); + if(this.id && !cfg.id){ + cfg.id = this.id; + } + return cfg; + }, + + // private + afterRender : Ext.emptyFn, + + /** + * Destroys this component by purging any event listeners, removing the component's element from the DOM, + * removing the component from its {@link Ext.Container} (if applicable) and unregistering it from + * {@link Ext.ComponentMgr}. Destruction is generally handled automatically by the framework and this method + * should usually not need to be called directly. + * + */ + destroy : function(){ + if(this.fireEvent('beforedestroy', this) !== false){ + this.beforeDestroy(); + if(this.rendered){ + this.el.removeAllListeners(); + this.el.remove(); + if(this.actionMode == 'container' || this.removeMode == 'container'){ + this.container.remove(); + } + } + this.onDestroy(); + Ext.ComponentMgr.unregister(this); + this.fireEvent('destroy', this); + this.purgeListeners(); + } + }, + + // private + beforeDestroy : Ext.emptyFn, + + // private + onDestroy : Ext.emptyFn, + + /** + *

Returns the {@link Ext.Element} which encapsulates this Component.

+ *

This will usually be a <DIV> element created by the class's onRender method, but + * that may be overridden using the {@link #autoEl} config.

+ *

Note: this element will not be available until this Component has been rendered.


+ *

To add listeners for DOM events to this Component (as opposed to listeners + * for this Component's own Observable events), see the {@link #listeners} config for a suggestion, + * or use a render listener directly:


+new Ext.Panel({
+    title: 'The Clickable Panel',
+    listeners: {
+        render: function(p) {
+            // Append the Panel to the click handler's argument list.
+            p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
+        },
+        single: true  // Remove the listener after first invocation
+    }
+});
+
+ * @return {Ext.Element} The Element which encapsulates this Component. + */ + getEl : function(){ + return this.el; + }, + + /** + * Returns the id of this component or automatically generates and + * returns an id if an id is not defined yet:

+     * 'ext-comp-' + (++Ext.Component.AUTO_ID)
+     * 
+ * @return {String} id + */ + getId : function(){ + return this.id || (this.id = 'ext-comp-' + (++Ext.Component.AUTO_ID)); + }, + + /** + * Returns the {@link #itemId} of this component. If an + * {@link #itemId} was not assigned through configuration the + * id is returned using {@link #getId}. + * @return {String} + */ + getItemId : function(){ + return this.itemId || this.getId(); + }, + + /** + * Try to focus this component. + * @param {Boolean} selectText (optional) If applicable, true to also select the text in this component + * @param {Boolean/Number} delay (optional) Delay the focus this number of milliseconds (true for 10 milliseconds) + * @return {Ext.Component} this + */ + focus : function(selectText, delay){ + if(delay){ + this.focus.defer(Ext.isNumber(delay) ? delay : 10, this, [selectText, false]); + return; + } + if(this.rendered){ + this.el.focus(); + if(selectText === true){ + this.el.dom.select(); + } + } + return this; + }, + + // private + blur : function(){ + if(this.rendered){ + this.el.blur(); + } + return this; + }, + + /** + * Disable this component and fire the 'disable' event. + * @return {Ext.Component} this + */ + disable : function(/* private */ silent){ + if(this.rendered){ + this.onDisable(); + } + this.disabled = true; + if(silent !== true){ + this.fireEvent('disable', this); + } + return this; + }, + + // private + onDisable : function(){ + this.getActionEl().addClass(this.disabledClass); + this.el.dom.disabled = true; + }, + + /** + * Enable this component and fire the 'enable' event. + * @return {Ext.Component} this + */ + enable : function(){ + if(this.rendered){ + this.onEnable(); + } + this.disabled = false; + this.fireEvent('enable', this); + return this; + }, + + // private + onEnable : function(){ + this.getActionEl().removeClass(this.disabledClass); + this.el.dom.disabled = false; + }, + + /** + * Convenience function for setting disabled/enabled by boolean. + * @param {Boolean} disabled + * @return {Ext.Component} this + */ + setDisabled : function(disabled){ + return this[disabled ? 'disable' : 'enable'](); + }, + + /** + * Show this component. Listen to the '{@link #beforeshow}' event and return + * false to cancel showing the component. Fires the '{@link #show}' + * event after showing the component. + * @return {Ext.Component} this + */ + show : function(){ + if(this.fireEvent('beforeshow', this) !== false){ + this.hidden = false; + if(this.autoRender){ + this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender); + } + if(this.rendered){ + this.onShow(); + } + this.fireEvent('show', this); + } + return this; + }, + + // private + onShow : function(){ + this.getVisibiltyEl().removeClass('x-hide-' + this.hideMode); + }, + + /** + * Hide this component. Listen to the '{@link #beforehide}' event and return + * false to cancel hiding the component. Fires the '{@link #hide}' + * event after hiding the component. Note this method is called internally if + * the component is configured to be {@link #hidden}. + * @return {Ext.Component} this + */ + hide : function(){ + if(this.fireEvent('beforehide', this) !== false){ + this.doHide(); + this.fireEvent('hide', this); + } + return this; + }, + + // private + doHide: function(){ + this.hidden = true; + if(this.rendered){ + this.onHide(); + } + }, + + // private + onHide : function(){ + this.getVisibiltyEl().addClass('x-hide-' + this.hideMode); + }, + + // private + getVisibiltyEl : function(){ + return this.hideParent ? this.container : this.getActionEl(); + }, + + /** + * Convenience function to hide or show this component by boolean. + * @param {Boolean} visible True to show, false to hide + * @return {Ext.Component} this + */ + setVisible : function(visible){ + return this[visible ? 'show' : 'hide'](); + }, + + /** + * Returns true if this component is visible. + * @return {Boolean} True if this component is visible, false otherwise. + */ + isVisible : function(){ + return this.rendered && this.getVisibiltyEl().isVisible(); + }, + + /** + * Clone the current component using the original config values passed into this instance by default. + * @param {Object} overrides A new config containing any properties to override in the cloned version. + * An id property can be passed on this object, otherwise one will be generated to avoid duplicates. + * @return {Ext.Component} clone The cloned copy of this component + */ + cloneConfig : function(overrides){ + overrides = overrides || {}; + var id = overrides.id || Ext.id(); + var cfg = Ext.applyIf(overrides, this.initialConfig); + cfg.id = id; // prevent dup id + return new this.constructor(cfg); + }, + + /** + * Gets the xtype for this component as registered with {@link Ext.ComponentMgr}. For a list of all + * available xtypes, see the {@link Ext.Component} header. Example usage: + *

+var t = new Ext.form.TextField();
+alert(t.getXType());  // alerts 'textfield'
+
+ * @return {String} The xtype + */ + getXType : function(){ + return this.constructor.xtype; + }, + + /** + *

Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended + * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).

+ *

If using your own subclasses, be aware that a Component must register its own xtype + * to participate in determination of inherited xtypes.

+ *

For a list of all available xtypes, see the {@link Ext.Component} header.

+ *

Example usage:

+ *

+var t = new Ext.form.TextField();
+var isText = t.isXType('textfield');        // true
+var isBoxSubclass = t.isXType('box');       // true, descended from BoxComponent
+var isBoxInstance = t.isXType('box', true); // false, not a direct BoxComponent instance
+
+ * @param {String} xtype The xtype to check for this Component + * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is + * the default), or true to check whether this Component is directly of the specified xtype. + * @return {Boolean} True if this component descends from the specified xtype, false otherwise. + */ + isXType : function(xtype, shallow){ + //assume a string by default + if (Ext.isFunction(xtype)){ + xtype = xtype.xtype; //handle being passed the class, e.g. Ext.Component + }else if (Ext.isObject(xtype)){ + xtype = xtype.constructor.xtype; //handle being passed an instance + } + + return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1 : this.constructor.xtype == xtype; + }, + + /** + *

Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all + * available xtypes, see the {@link Ext.Component} header.

+ *

If using your own subclasses, be aware that a Component must register its own xtype + * to participate in determination of inherited xtypes.

+ *

Example usage:

+ *

+var t = new Ext.form.TextField();
+alert(t.getXTypes());  // alerts 'component/box/field/textfield'
+
+ * @return {String} The xtype hierarchy string + */ + getXTypes : function(){ + var tc = this.constructor; + if(!tc.xtypes){ + var c = [], sc = this; + while(sc && sc.constructor.xtype){ + c.unshift(sc.constructor.xtype); + sc = sc.constructor.superclass; + } + tc.xtypeChain = c; + tc.xtypes = c.join('/'); + } + return tc.xtypes; + }, + + /** + * Find a container above this component at any level by a custom function. If the passed function returns + * true, the container will be returned. + * @param {Function} fn The custom function to call with the arguments (container, this component). + * @return {Ext.Container} The first Container for which the custom function returns true + */ + findParentBy : function(fn) { + for (var p = this.ownerCt; (p != null) && !fn(p, this); p = p.ownerCt); + return p || null; + }, + + /** + * Find a container above this component at any level by xtype or class + * @param {String/Class} xtype The xtype string for a component, or the class of the component directly + * @return {Ext.Container} The first Container which matches the given xtype or class + */ + findParentByType : function(xtype) { + return Ext.isFunction(xtype) ? + this.findParentBy(function(p){ + return p.constructor === xtype; + }) : + this.findParentBy(function(p){ + return p.constructor.xtype === xtype; + }); + }, + + getDomPositionEl : function(){ + return this.getPositionEl ? this.getPositionEl() : this.getEl(); + }, + + // private + purgeListeners : function(){ + Ext.Component.superclass.purgeListeners.call(this); + if(this.mons){ + this.on('beforedestroy', this.clearMons, this, {single: true}); + } + }, + + // private + clearMons : function(){ + Ext.each(this.mons, function(m){ + m.item.un(m.ename, m.fn, m.scope); + }, this); + this.mons = []; + }, + + // internal function for auto removal of assigned event handlers on destruction + mon : function(item, ename, fn, scope, opt){ + if(!this.mons){ + this.mons = []; + this.on('beforedestroy', this.clearMons, this, {single: true}); + } + + if(Ext.isObject(ename)){ + var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/; + + var o = ename; + for(var e in o){ + if(propRe.test(e)){ + continue; + } + if(Ext.isFunction(o[e])){ + // shared options + this.mons.push({ + item: item, ename: e, fn: o[e], scope: o.scope + }); + item.on(e, o[e], o.scope, o); + }else{ + // individual options + this.mons.push({ + item: item, ename: e, fn: o[e], scope: o.scope + }); + item.on(e, o[e]); + } + } + return; + } + + + this.mons.push({ + item: item, ename: ename, fn: fn, scope: scope + }); + item.on(ename, fn, scope, opt); + }, + + // protected, opposite of mon + mun : function(item, ename, fn, scope){ + var found, mon; + for(var i = 0, len = this.mons.length; i < len; ++i){ + mon = this.mons[i]; + if(item === mon.item && ename == mon.ename && fn === mon.fn && scope === mon.scope){ + this.mons.splice(i, 1); + item.un(ename, fn, scope); + found = true; + break; + } + } + return found; + }, + + /** + * Returns the next component in the owning container + * @return Ext.Component + */ + nextSibling : function(){ + if(this.ownerCt){ + var index = this.ownerCt.items.indexOf(this); + if(index != -1 && index+1 < this.ownerCt.items.getCount()){ + return this.ownerCt.items.itemAt(index+1); + } + } + return null; + }, + + /** + * Returns the previous component in the owning container + * @return Ext.Component + */ + previousSibling : function(){ + if(this.ownerCt){ + var index = this.ownerCt.items.indexOf(this); + if(index > 0){ + return this.ownerCt.items.itemAt(index-1); + } + } + return null; + }, + + /** + * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy. + * @return {Ext.Container} the Container which owns this Component. + */ + getBubbleTarget : function(){ + return this.ownerCt; + } +}); + +Ext.reg('component', Ext.Component); +/** + * @class Ext.Action + *

An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it + * can be usefully shared among multiple components. Actions let you share handlers, configuration options and UI + * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button} + * and {@link Ext.menu.Menu} components).

+ *

Aside from supporting the config object interface, any component that needs to use Actions must also support + * the following method list, as these will be called as needed by the Action class: setText(string), setIconCls(string), + * setDisabled(boolean), setVisible(boolean) and setHandler(function).

+ * Example usage:
+ *

+// Define the shared action.  Each component below will have the same
+// display text and icon, and will display the same message on click.
+var action = new Ext.Action({
+    {@link #text}: 'Do something',
+    {@link #handler}: function(){
+        Ext.Msg.alert('Click', 'You did something.');
+    },
+    {@link #iconCls}: 'do-something',
+    {@link #itemId}: 'myAction'
+});
+
+var panel = new Ext.Panel({
+    title: 'Actions',
+    width: 500,
+    height: 300,
+    tbar: [
+        // Add the action directly to a toolbar as a menu button
+        action,
+        {
+            text: 'Action Menu',
+            // Add the action to a menu as a text item
+            menu: [action]
+        }
+    ],
+    items: [
+        // Add the action to the panel body as a standard button
+        new Ext.Button(action)
+    ],
+    renderTo: Ext.getBody()
+});
+
+// Change the text for all components using the action
+action.setText('Something else');
+
+// Reference an action through a container using the itemId
+var btn = panel.getComponent('myAction');
+var aRef = btn.baseAction;
+aRef.setText('New text');
+
+ * @constructor + * @param {Object} config The configuration options + */ +Ext.Action = function(config){ + this.initialConfig = config; + this.itemId = config.itemId = (config.itemId || config.id || Ext.id()); + this.items = []; +} + +Ext.Action.prototype = { + /** + * @cfg {String} text The text to set for all components using this action (defaults to ''). + */ + /** + * @cfg {String} iconCls + * The CSS class selector that specifies a background image to be used as the header icon for + * all components using this action (defaults to ''). + *

An example of specifying a custom icon class would be something like: + *


+// specify the property in the config for the class:
+     ...
+     iconCls: 'do-something'
+
+// css class that specifies background image to be used as the icon image:
+.do-something { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
+
+ */ + /** + * @cfg {Boolean} disabled True to disable all components using this action, false to enable them (defaults to false). + */ + /** + * @cfg {Boolean} hidden True to hide all components using this action, false to show them (defaults to false). + */ + /** + * @cfg {Function} handler The function that will be invoked by each component tied to this action + * when the component's primary event is triggered (defaults to undefined). + */ + /** + * @cfg {String} itemId + * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}. + */ + /** + * @cfg {Object} scope The scope in which the {@link #handler} function will execute. + */ + + // private + isAction : true, + + /** + * Sets the text to be displayed by all components using this action. + * @param {String} text The text to display + */ + setText : function(text){ + this.initialConfig.text = text; + this.callEach('setText', [text]); + }, + + /** + * Gets the text currently displayed by all components using this action. + */ + getText : function(){ + return this.initialConfig.text; + }, + + /** + * Sets the icon CSS class for all components using this action. The class should supply + * a background image that will be used as the icon image. + * @param {String} cls The CSS class supplying the icon image + */ + setIconClass : function(cls){ + this.initialConfig.iconCls = cls; + this.callEach('setIconClass', [cls]); + }, + + /** + * Gets the icon CSS class currently used by all components using this action. + */ + getIconClass : function(){ + return this.initialConfig.iconCls; + }, + + /** + * Sets the disabled state of all components using this action. Shortcut method + * for {@link #enable} and {@link #disable}. + * @param {Boolean} disabled True to disable the component, false to enable it + */ + setDisabled : function(v){ + this.initialConfig.disabled = v; + this.callEach('setDisabled', [v]); + }, + + /** + * Enables all components using this action. + */ + enable : function(){ + this.setDisabled(false); + }, + + /** + * Disables all components using this action. + */ + disable : function(){ + this.setDisabled(true); + }, + + /** + * Returns true if the components using this action are currently disabled, else returns false. + */ + isDisabled : function(){ + return this.initialConfig.disabled; + }, + + /** + * Sets the hidden state of all components using this action. Shortcut method + * for {@link #hide} and {@link #show}. + * @param {Boolean} hidden True to hide the component, false to show it + */ + setHidden : function(v){ + this.initialConfig.hidden = v; + this.callEach('setVisible', [!v]); + }, + + /** + * Shows all components using this action. + */ + show : function(){ + this.setHidden(false); + }, + + /** + * Hides all components using this action. + */ + hide : function(){ + this.setHidden(true); + }, + + /** + * Returns true if the components using this action are currently hidden, else returns false. + */ + isHidden : function(){ + return this.initialConfig.hidden; + }, + + /** + * Sets the function that will be called by each component using this action when its primary event is triggered. + * @param {Function} fn The function that will be invoked by the action's components. The function + * will be called with no arguments. + * @param {Object} scope The scope in which the function will execute + */ + setHandler : function(fn, scope){ + this.initialConfig.handler = fn; + this.initialConfig.scope = scope; + this.callEach('setHandler', [fn, scope]); + }, + + /** + * Executes the specified function once for each component currently tied to this action. The function passed + * in should accept a single argument that will be an object that supports the basic Action config/method interface. + * @param {Function} fn The function to execute for each component + * @param {Object} scope The scope in which the function will execute + */ + each : function(fn, scope){ + Ext.each(this.items, fn, scope); + }, + + // private + callEach : function(fnName, args){ + var cs = this.items; + for(var i = 0, len = cs.length; i < len; i++){ + cs[i][fnName].apply(cs[i], args); + } + }, + + // private + addComponent : function(comp){ + this.items.push(comp); + comp.on('destroy', this.removeComponent, this); + }, + + // private + removeComponent : function(comp){ + this.items.remove(comp); + }, + + /** + * Executes this action manually using the handler function specified in the original config object + * or the handler function set with {@link #setHandler}. Any arguments passed to this + * function will be passed on to the handler function. + * @param {Mixed} arg1 (optional) Variable number of arguments passed to the handler function + * @param {Mixed} arg2 (optional) + * @param {Mixed} etc... (optional) + */ + execute : function(){ + this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments); + } +}; +/** + * @class Ext.Layer + * @extends Ext.Element + * An extended {@link Ext.Element} object that supports a shadow and shim, constrain to viewport and + * automatic maintaining of shadow/shim positions. + * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true) + * @cfg {String/Boolean} shadow True to automatically create an {@link Ext.Shadow}, or a string indicating the + * shadow's display {@link Ext.Shadow#mode}. False to disable the shadow. (defaults to false) + * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: 'div', cls: 'x-layer'}). + * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true) + * @cfg {String} cls CSS class to add to the element + * @cfg {Number} zindex Starting z-index (defaults to 11000) + * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 4) + * @cfg {Boolean} useDisplay + * Defaults to use css offsets to hide the Layer. Specify true + * to use css style 'display:none;' to hide the Layer. + * @constructor + * @param {Object} config An object with config options. + * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it. + */ +(function(){ +Ext.Layer = function(config, existingEl){ + config = config || {}; + var dh = Ext.DomHelper; + var cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body; + if(existingEl){ + this.dom = Ext.getDom(existingEl); + } + if(!this.dom){ + var o = config.dh || {tag: 'div', cls: 'x-layer'}; + this.dom = dh.append(pel, o); + } + if(config.cls){ + this.addClass(config.cls); + } + this.constrain = config.constrain !== false; + this.setVisibilityMode(Ext.Element.VISIBILITY); + if(config.id){ + this.id = this.dom.id = config.id; + }else{ + this.id = Ext.id(this.dom); + } + this.zindex = config.zindex || this.getZIndex(); + this.position('absolute', this.zindex); + if(config.shadow){ + this.shadowOffset = config.shadowOffset || 4; + this.shadow = new Ext.Shadow({ + offset : this.shadowOffset, + mode : config.shadow + }); + }else{ + this.shadowOffset = 0; + } + this.useShim = config.shim !== false && Ext.useShims; + this.useDisplay = config.useDisplay; + this.hide(); +}; + +var supr = Ext.Element.prototype; + +// shims are shared among layer to keep from having 100 iframes +var shims = []; + +Ext.extend(Ext.Layer, Ext.Element, { + + getZIndex : function(){ + return this.zindex || parseInt((this.getShim() || this).getStyle('z-index'), 10) || 11000; + }, + + getShim : function(){ + if(!this.useShim){ + return null; + } + if(this.shim){ + return this.shim; + } + var shim = shims.shift(); + if(!shim){ + shim = this.createShim(); + shim.enableDisplayMode('block'); + shim.dom.style.display = 'none'; + shim.dom.style.visibility = 'visible'; + } + var pn = this.dom.parentNode; + if(shim.dom.parentNode != pn){ + pn.insertBefore(shim.dom, this.dom); + } + shim.setStyle('z-index', this.getZIndex()-2); + this.shim = shim; + return shim; + }, + + hideShim : function(){ + if(this.shim){ + this.shim.setDisplayed(false); + shims.push(this.shim); + delete this.shim; + } + }, + + disableShadow : function(){ + if(this.shadow){ + this.shadowDisabled = true; + this.shadow.hide(); + this.lastShadowOffset = this.shadowOffset; + this.shadowOffset = 0; + } + }, + + enableShadow : function(show){ + if(this.shadow){ + this.shadowDisabled = false; + this.shadowOffset = this.lastShadowOffset; + delete this.lastShadowOffset; + if(show){ + this.sync(true); + } + } + }, + + // private + // this code can execute repeatedly in milliseconds (i.e. during a drag) so + // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls) + sync : function(doShow){ + var sw = this.shadow; + if(!this.updating && this.isVisible() && (sw || this.useShim)){ + var sh = this.getShim(); + + var w = this.getWidth(), + h = this.getHeight(); + + var l = this.getLeft(true), + t = this.getTop(true); + + if(sw && !this.shadowDisabled){ + if(doShow && !sw.isVisible()){ + sw.show(this); + }else{ + sw.realign(l, t, w, h); + } + if(sh){ + if(doShow){ + sh.show(); + } + // fit the shim behind the shadow, so it is shimmed too + var a = sw.adjusts, s = sh.dom.style; + s.left = (Math.min(l, l+a.l))+'px'; + s.top = (Math.min(t, t+a.t))+'px'; + s.width = (w+a.w)+'px'; + s.height = (h+a.h)+'px'; + } + }else if(sh){ + if(doShow){ + sh.show(); + } + sh.setSize(w, h); + sh.setLeftTop(l, t); + } + + } + }, + + // private + destroy : function(){ + this.hideShim(); + if(this.shadow){ + this.shadow.hide(); + } + this.removeAllListeners(); + Ext.removeNode(this.dom); + Ext.Element.uncache(this.id); + }, + + remove : function(){ + this.destroy(); + }, + + // private + beginUpdate : function(){ + this.updating = true; + }, + + // private + endUpdate : function(){ + this.updating = false; + this.sync(true); + }, + + // private + hideUnders : function(negOffset){ + if(this.shadow){ + this.shadow.hide(); + } + this.hideShim(); + }, + + // private + constrainXY : function(){ + if(this.constrain){ + var vw = Ext.lib.Dom.getViewWidth(), + vh = Ext.lib.Dom.getViewHeight(); + var s = Ext.getDoc().getScroll(); + + var xy = this.getXY(); + var x = xy[0], y = xy[1]; + var so = this.shadowOffset; + var w = this.dom.offsetWidth+so, h = this.dom.offsetHeight+so; + // only move it if it needs it + var moved = false; + // first validate right/bottom + if((x + w) > vw+s.left){ + x = vw - w - so; + moved = true; + } + if((y + h) > vh+s.top){ + y = vh - h - so; + moved = true; + } + // then make sure top/left isn't negative + if(x < s.left){ + x = s.left; + moved = true; + } + if(y < s.top){ + y = s.top; + moved = true; + } + if(moved){ + if(this.avoidY){ + var ay = this.avoidY; + if(y <= ay && (y+h) >= ay){ + y = ay-h-5; + } + } + xy = [x, y]; + this.storeXY(xy); + supr.setXY.call(this, xy); + this.sync(); + } + } + return this; + }, + + isVisible : function(){ + return this.visible; + }, + + // private + showAction : function(){ + this.visible = true; // track visibility to prevent getStyle calls + if(this.useDisplay === true){ + this.setDisplayed(''); + }else if(this.lastXY){ + supr.setXY.call(this, this.lastXY); + }else if(this.lastLT){ + supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]); + } + }, + + // private + hideAction : function(){ + this.visible = false; + if(this.useDisplay === true){ + this.setDisplayed(false); + }else{ + this.setLeftTop(-10000,-10000); + } + }, + + // overridden Element method + setVisible : function(v, a, d, c, e){ + if(v){ + this.showAction(); + } + if(a && v){ + var cb = function(){ + this.sync(true); + if(c){ + c(); + } + }.createDelegate(this); + supr.setVisible.call(this, true, true, d, cb, e); + }else{ + if(!v){ + this.hideUnders(true); + } + var cb = c; + if(a){ + cb = function(){ + this.hideAction(); + if(c){ + c(); + } + }.createDelegate(this); + } + supr.setVisible.call(this, v, a, d, cb, e); + if(v){ + this.sync(true); + }else if(!a){ + this.hideAction(); + } + } + return this; + }, + + storeXY : function(xy){ + delete this.lastLT; + this.lastXY = xy; + }, + + storeLeftTop : function(left, top){ + delete this.lastXY; + this.lastLT = [left, top]; + }, + + // private + beforeFx : function(){ + this.beforeAction(); + return Ext.Layer.superclass.beforeFx.apply(this, arguments); + }, + + // private + afterFx : function(){ + Ext.Layer.superclass.afterFx.apply(this, arguments); + this.sync(this.isVisible()); + }, + + // private + beforeAction : function(){ + if(!this.updating && this.shadow){ + this.shadow.hide(); + } + }, + + // overridden Element method + setLeft : function(left){ + this.storeLeftTop(left, this.getTop(true)); + supr.setLeft.apply(this, arguments); + this.sync(); + return this; + }, + + setTop : function(top){ + this.storeLeftTop(this.getLeft(true), top); + supr.setTop.apply(this, arguments); + this.sync(); + return this; + }, + + setLeftTop : function(left, top){ + this.storeLeftTop(left, top); + supr.setLeftTop.apply(this, arguments); + this.sync(); + return this; + }, + + setXY : function(xy, a, d, c, e){ + this.fixDisplay(); + this.beforeAction(); + this.storeXY(xy); + var cb = this.createCB(c); + supr.setXY.call(this, xy, a, d, cb, e); + if(!a){ + cb(); + } + return this; + }, + + // private + createCB : function(c){ + var el = this; + return function(){ + el.constrainXY(); + el.sync(true); + if(c){ + c(); + } + }; + }, + + // overridden Element method + setX : function(x, a, d, c, e){ + this.setXY([x, this.getY()], a, d, c, e); + return this; + }, + + // overridden Element method + setY : function(y, a, d, c, e){ + this.setXY([this.getX(), y], a, d, c, e); + return this; + }, + + // overridden Element method + setSize : function(w, h, a, d, c, e){ + this.beforeAction(); + var cb = this.createCB(c); + supr.setSize.call(this, w, h, a, d, cb, e); + if(!a){ + cb(); + } + return this; + }, + + // overridden Element method + setWidth : function(w, a, d, c, e){ + this.beforeAction(); + var cb = this.createCB(c); + supr.setWidth.call(this, w, a, d, cb, e); + if(!a){ + cb(); + } + return this; + }, + + // overridden Element method + setHeight : function(h, a, d, c, e){ + this.beforeAction(); + var cb = this.createCB(c); + supr.setHeight.call(this, h, a, d, cb, e); + if(!a){ + cb(); + } + return this; + }, + + // overridden Element method + setBounds : function(x, y, w, h, a, d, c, e){ + this.beforeAction(); + var cb = this.createCB(c); + if(!a){ + this.storeXY([x, y]); + supr.setXY.call(this, [x, y]); + supr.setSize.call(this, w, h, a, d, cb, e); + cb(); + }else{ + supr.setBounds.call(this, x, y, w, h, a, d, cb, e); + } + return this; + }, + + /** + * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically + * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow + * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index). + * @param {Number} zindex The new z-index to set + * @return {this} The Layer + */ + setZIndex : function(zindex){ + this.zindex = zindex; + this.setStyle('z-index', zindex + 2); + if(this.shadow){ + this.shadow.setZIndex(zindex + 1); + } + if(this.shim){ + this.shim.setStyle('z-index', zindex); + } + return this; + } +}); +})();/** + * @class Ext.Shadow + * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned, + * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced + * functionality that can also provide the same shadow effect, see the {@link Ext.Layer} class. + * @constructor + * Create a new Shadow + * @param {Object} config The config object + */ +Ext.Shadow = function(config){ + Ext.apply(this, config); + if(typeof this.mode != "string"){ + this.mode = this.defaultMode; + } + var o = this.offset, a = {h: 0}; + var rad = Math.floor(this.offset/2); + switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows + case "drop": + a.w = 0; + a.l = a.t = o; + a.t -= 1; + if(Ext.isIE){ + a.l -= this.offset + rad; + a.t -= this.offset + rad; + a.w -= rad; + a.h -= rad; + a.t += 1; + } + break; + case "sides": + a.w = (o*2); + a.l = -o; + a.t = o-1; + if(Ext.isIE){ + a.l -= (this.offset - rad); + a.t -= this.offset + rad; + a.l += 1; + a.w -= (this.offset - rad)*2; + a.w -= rad + 1; + a.h -= 1; + } + break; + case "frame": + a.w = a.h = (o*2); + a.l = a.t = -o; + a.t += 1; + a.h -= 2; + if(Ext.isIE){ + a.l -= (this.offset - rad); + a.t -= (this.offset - rad); + a.l += 1; + a.w -= (this.offset + rad + 1); + a.h -= (this.offset + rad); + a.h += 1; + } + break; + }; + + this.adjusts = a; +}; + +Ext.Shadow.prototype = { + /** + * @cfg {String} mode + * The shadow display mode. Supports the following options:
+ */ + /** + * @cfg {String} offset + * The number of pixels to offset the shadow from the element (defaults to 4) + */ + offset: 4, + + // private + defaultMode: "drop", + + /** + * Displays the shadow under the target element + * @param {Mixed} targetEl The id or element under which the shadow should display + */ + show : function(target){ + target = Ext.get(target); + if(!this.el){ + this.el = Ext.Shadow.Pool.pull(); + if(this.el.dom.nextSibling != target.dom){ + this.el.insertBefore(target); + } + } + this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1); + if(Ext.isIE){ + this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")"; + } + this.realign( + target.getLeft(true), + target.getTop(true), + target.getWidth(), + target.getHeight() + ); + this.el.dom.style.display = "block"; + }, + + /** + * Returns true if the shadow is visible, else false + */ + isVisible : function(){ + return this.el ? true : false; + }, + + /** + * Direct alignment when values are already available. Show must be called at least once before + * calling this method to ensure it is initialized. + * @param {Number} left The target element left position + * @param {Number} top The target element top position + * @param {Number} width The target element width + * @param {Number} height The target element height + */ + realign : function(l, t, w, h){ + if(!this.el){ + return; + } + var a = this.adjusts, d = this.el.dom, s = d.style; + var iea = 0; + s.left = (l+a.l)+"px"; + s.top = (t+a.t)+"px"; + var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px"; + if(s.width != sws || s.height != shs){ + s.width = sws; + s.height = shs; + if(!Ext.isIE){ + var cn = d.childNodes; + var sww = Math.max(0, (sw-12))+"px"; + cn[0].childNodes[1].style.width = sww; + cn[1].childNodes[1].style.width = sww; + cn[2].childNodes[1].style.width = sww; + cn[1].style.height = Math.max(0, (sh-12))+"px"; + } + } + }, + + /** + * Hides this shadow + */ + hide : function(){ + if(this.el){ + this.el.dom.style.display = "none"; + Ext.Shadow.Pool.push(this.el); + delete this.el; + } + }, + + /** + * Adjust the z-index of this shadow + * @param {Number} zindex The new z-index + */ + setZIndex : function(z){ + this.zIndex = z; + if(this.el){ + this.el.setStyle("z-index", z); + } + } +}; + +// Private utility class that manages the internal Shadow cache +Ext.Shadow.Pool = function(){ + var p = []; + var markup = Ext.isIE ? + '
' : + '
'; + return { + pull : function(){ + var sh = p.shift(); + if(!sh){ + sh = Ext.get(Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup)); + sh.autoBoxAdjust = false; + } + return sh; + }, + + push : function(sh){ + p.push(sh); + } + }; +}();/** + * @class Ext.BoxComponent + * @extends Ext.Component + *

Base class for any {@link Ext.Component Component} that is to be sized as a box, using width and height.

+ *

BoxComponent provides automatic box model adjustments for sizing and positioning and will work correctly + * within the Component rendering model.

+ *

A BoxComponent may be created as a custom Component which encapsulates any HTML element, either a pre-existing + * element, or one that is created to your specifications at render time. Usually, to participate in layouts, + * a Component will need to be a BoxComponent in order to have its width and height managed.

+ *

To use a pre-existing element as a BoxComponent, configure it so that you preset the el property to the + * element to reference:


+var pageHeader = new Ext.BoxComponent({
+    el: 'my-header-div'
+});
+ * This may then be {@link Ext.Container#add added} to a {@link Ext.Container Container} as a child item.

+ *

To create a BoxComponent based around a HTML element to be created at render time, use the + * {@link Ext.Component#autoEl autoEl} config option which takes the form of a + * {@link Ext.DomHelper DomHelper} specification:


+var myImage = new Ext.BoxComponent({
+    autoEl: {
+        tag: 'img',
+        src: '/images/my-image.jpg'
+    }
+});

+ * @constructor + * @param {Ext.Element/String/Object} config The configuration options. + * @xtype box + */ +Ext.BoxComponent = Ext.extend(Ext.Component, { + + // Configs below are used for all Components when rendered by BorderLayout. + /** + * @cfg {String} region

Note: this config is only used when this BoxComponent is rendered + * by a Container which has been configured to use the {@link Ext.layout.BorderLayout BorderLayout} + * layout manager (e.g. specifying layout:'border').


+ *

See {@link Ext.layout.BorderLayout} also.

+ */ + // margins config is used when a BoxComponent is rendered by BorderLayout or BoxLayout. + /** + * @cfg {Object} margins

Note: this config is only used when this BoxComponent is rendered + * by a Container which has been configured to use the {@link Ext.layout.BorderLayout BorderLayout} + * or one of the two {@link Ext.layout.BoxLayout BoxLayout} subclasses.

+ *

An object containing margins to apply to this BoxComponent in the + * format:


+{
+    top: (top margin),
+    right: (right margin),
+    bottom: (bottom margin),
+    left: (left margin)
+}
+ *

May also be a string containing space-separated, numeric margin values. The order of the + * sides associated with each value matches the way CSS processes margin values:

+ *

+ *

Defaults to:


+     * {top:0, right:0, bottom:0, left:0}
+     * 
+ */ + /** + * @cfg {Number} x + * The local x (left) coordinate for this component if contained within a positioning container. + */ + /** + * @cfg {Number} y + * The local y (top) coordinate for this component if contained within a positioning container. + */ + /** + * @cfg {Number} pageX + * The page level x coordinate for this component if contained within a positioning container. + */ + /** + * @cfg {Number} pageY + * The page level y coordinate for this component if contained within a positioning container. + */ + /** + * @cfg {Number} height + * The height of this component in pixels (defaults to auto). + * Note to express this dimension as a percentage or offset see {@link Ext.Component#anchor}. + */ + /** + * @cfg {Number} width + * The width of this component in pixels (defaults to auto). + * Note to express this dimension as a percentage or offset see {@link Ext.Component#anchor}. + */ + /** + * @cfg {Boolean} autoHeight + *

True to use height:'auto', false to use fixed height (or allow it to be managed by its parent + * Container's {@link Ext.Container#layout layout manager}. Defaults to false.

+ *

Note: Although many components inherit this config option, not all will + * function as expected with a height of 'auto'. Setting autoHeight:true means that the + * browser will manage height based on the element's contents, and that Ext will not manage it at all.

+ *

If the browser is managing the height, be aware that resizes performed by the browser in response + * to changes within the structure of the Component cannot be detected. Therefore changes to the height might + * result in elements needing to be synchronized with the new height. Example:


+var w = new Ext.Window({
+    title: 'Window',
+    width: 600,
+    autoHeight: true,
+    items: {
+        title: 'Collapse Me',
+        height: 400,
+        collapsible: true,
+        border: false,
+        listeners: {
+            beforecollapse: function() {
+                w.el.shadow.hide();
+            },
+            beforeexpand: function() {
+                w.el.shadow.hide();
+            },
+            collapse: function() {
+                w.syncShadow();
+            },
+            expand: function() {
+                w.syncShadow();
+            }
+        }
+    }
+}).show();
+
+ */ + /** + * @cfg {Boolean} autoWidth + *

True to use width:'auto', false to use fixed width (or allow it to be managed by its parent + * Container's {@link Ext.Container#layout layout manager}. Defaults to false.

+ *

Note: Although many components inherit this config option, not all will + * function as expected with a width of 'auto'. Setting autoWidth:true means that the + * browser will manage width based on the element's contents, and that Ext will not manage it at all.

+ *

If the browser is managing the width, be aware that resizes performed by the browser in response + * to changes within the structure of the Component cannot be detected. Therefore changes to the width might + * result in elements needing to be synchronized with the new width. For example, where the target element is:


+<div id='grid-container' style='margin-left:25%;width:50%'></div>
+
+ * A Panel rendered into that target element must listen for browser window resize in order to relay its + * child items when the browser changes its width:

+var myPanel = new Ext.Panel({
+    renderTo: 'grid-container',
+    monitorResize: true, // relay on browser resize
+    title: 'Panel',
+    height: 400,
+    autoWidth: true,
+    layout: 'hbox',
+    layoutConfig: {
+        align: 'stretch'
+    },
+    defaults: {
+        flex: 1
+    },
+    items: [{
+        title: 'Box 1',
+    }, {
+        title: 'Box 2'
+    }, {
+        title: 'Box 3'
+    }],
+});
+
+ */ + + /* // private internal config + * {Boolean} deferHeight + * True to defer height calculations to an external component, false to allow this component to set its own + * height (defaults to false). + */ + + // private + initComponent : function(){ + Ext.BoxComponent.superclass.initComponent.call(this); + this.addEvents( + /** + * @event resize + * Fires after the component is resized. + * @param {Ext.Component} this + * @param {Number} adjWidth The box-adjusted width that was set + * @param {Number} adjHeight The box-adjusted height that was set + * @param {Number} rawWidth The width that was originally specified + * @param {Number} rawHeight The height that was originally specified + */ + 'resize', + /** + * @event move + * Fires after the component is moved. + * @param {Ext.Component} this + * @param {Number} x The new x position + * @param {Number} y The new y position + */ + 'move' + ); + }, + + // private, set in afterRender to signify that the component has been rendered + boxReady : false, + // private, used to defer height settings to subclasses + deferHeight: false, + + /** + * Sets the width and height of this BoxComponent. This method fires the {@link #resize} event. This method can accept + * either width and height as separate arguments, or you can pass a size object like {width:10, height:20}. + * @param {Mixed} width The new width to set. This may be one of:
+ * @param {Mixed} height The new height to set (not required if a size object is passed as the first arg). + * This may be one of:
+ * @return {Ext.BoxComponent} this + */ + setSize : function(w, h){ + // support for standard size objects + if(typeof w == 'object'){ + h = w.height; + w = w.width; + } + // not rendered + if(!this.boxReady){ + this.width = w; + this.height = h; + return this; + } + + // prevent recalcs when not needed + if(this.cacheSizes !== false && this.lastSize && this.lastSize.width == w && this.lastSize.height == h){ + return this; + } + this.lastSize = {width: w, height: h}; + var adj = this.adjustSize(w, h); + var aw = adj.width, ah = adj.height; + if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters + var rz = this.getResizeEl(); + if(!this.deferHeight && aw !== undefined && ah !== undefined){ + rz.setSize(aw, ah); + }else if(!this.deferHeight && ah !== undefined){ + rz.setHeight(ah); + }else if(aw !== undefined){ + rz.setWidth(aw); + } + this.onResize(aw, ah, w, h); + this.fireEvent('resize', this, aw, ah, w, h); + } + return this; + }, + + /** + * Sets the width of the component. This method fires the {@link #resize} event. + * @param {Number} width The new width to setThis may be one of:
+ * @return {Ext.BoxComponent} this + */ + setWidth : function(width){ + return this.setSize(width); + }, + + /** + * Sets the height of the component. This method fires the {@link #resize} event. + * @param {Number} height The new height to set. This may be one of:
+ * @return {Ext.BoxComponent} this + */ + setHeight : function(height){ + return this.setSize(undefined, height); + }, + + /** + * Gets the current size of the component's underlying element. + * @return {Object} An object containing the element's size {width: (element width), height: (element height)} + */ + getSize : function(){ + return this.getResizeEl().getSize(); + }, + + /** + * Gets the current width of the component's underlying element. + * @return {Number} + */ + getWidth : function(){ + return this.getResizeEl().getWidth(); + }, + + /** + * Gets the current height of the component's underlying element. + * @return {Number} + */ + getHeight : function(){ + return this.getResizeEl().getHeight(); + }, + + /** + * Gets the current size of the component's underlying element, including space taken by its margins. + * @return {Object} An object containing the element's size {width: (element width + left/right margins), height: (element height + top/bottom margins)} + */ + getOuterSize : function(){ + var el = this.getResizeEl(); + return {width: el.getWidth() + el.getMargins('lr'), + height: el.getHeight() + el.getMargins('tb')}; + }, + + /** + * Gets the current XY position of the component's underlying element. + * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false) + * @return {Array} The XY position of the element (e.g., [100, 200]) + */ + getPosition : function(local){ + var el = this.getPositionEl(); + if(local === true){ + return [el.getLeft(true), el.getTop(true)]; + } + return this.xy || el.getXY(); + }, + + /** + * Gets the current box measurements of the component's underlying element. + * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false) + * @return {Object} box An object in the format {x, y, width, height} + */ + getBox : function(local){ + var pos = this.getPosition(local); + var s = this.getSize(); + s.x = pos[0]; + s.y = pos[1]; + return s; + }, + + /** + * Sets the current box measurements of the component's underlying element. + * @param {Object} box An object in the format {x, y, width, height} + * @return {Ext.BoxComponent} this + */ + updateBox : function(box){ + this.setSize(box.width, box.height); + this.setPagePosition(box.x, box.y); + return this; + }, + + /** + *

Returns the outermost Element of this Component which defines the Components overall size.

+ *

Usually this will return the same Element as {@link #getEl}, + * but in some cases, a Component may have some more wrapping Elements around its main + * active Element.

+ *

An example is a ComboBox. It is encased in a wrapping Element which + * contains both the <input> Element (which is what would be returned + * by its {@link #getEl} method, and the trigger button Element. + * This Element is returned as the resizeEl. + */ + getResizeEl : function(){ + return this.resizeEl || this.el; + }, + + // protected + getPositionEl : function(){ + return this.positionEl || this.el; + }, + + /** + * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}. + * This method fires the {@link #move} event. + * @param {Number} left The new left + * @param {Number} top The new top + * @return {Ext.BoxComponent} this + */ + setPosition : function(x, y){ + if(x && typeof x[1] == 'number'){ + y = x[1]; + x = x[0]; + } + this.x = x; + this.y = y; + if(!this.boxReady){ + return this; + } + var adj = this.adjustPosition(x, y); + var ax = adj.x, ay = adj.y; + + var el = this.getPositionEl(); + if(ax !== undefined || ay !== undefined){ + if(ax !== undefined && ay !== undefined){ + el.setLeftTop(ax, ay); + }else if(ax !== undefined){ + el.setLeft(ax); + }else if(ay !== undefined){ + el.setTop(ay); + } + this.onPosition(ax, ay); + this.fireEvent('move', this, ax, ay); + } + return this; + }, + + /** + * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}. + * This method fires the {@link #move} event. + * @param {Number} x The new x position + * @param {Number} y The new y position + * @return {Ext.BoxComponent} this + */ + setPagePosition : function(x, y){ + if(x && typeof x[1] == 'number'){ + y = x[1]; + x = x[0]; + } + this.pageX = x; + this.pageY = y; + if(!this.boxReady){ + return; + } + if(x === undefined || y === undefined){ // cannot translate undefined points + return; + } + var p = this.getPositionEl().translatePoints(x, y); + this.setPosition(p.left, p.top); + return this; + }, + + // private + onRender : function(ct, position){ + Ext.BoxComponent.superclass.onRender.call(this, ct, position); + if(this.resizeEl){ + this.resizeEl = Ext.get(this.resizeEl); + } + if(this.positionEl){ + this.positionEl = Ext.get(this.positionEl); + } + }, + + // private + afterRender : function(){ + Ext.BoxComponent.superclass.afterRender.call(this); + this.boxReady = true; + this.setSize(this.width, this.height); + if(this.x || this.y){ + this.setPosition(this.x, this.y); + }else if(this.pageX || this.pageY){ + this.setPagePosition(this.pageX, this.pageY); + } + }, + + /** + * Force the component's size to recalculate based on the underlying element's current height and width. + * @return {Ext.BoxComponent} this + */ + syncSize : function(){ + delete this.lastSize; + this.setSize(this.autoWidth ? undefined : this.getResizeEl().getWidth(), this.autoHeight ? undefined : this.getResizeEl().getHeight()); + return this; + }, + + /* // protected + * Called after the component is resized, this method is empty by default but can be implemented by any + * subclass that needs to perform custom logic after a resize occurs. + * @param {Number} adjWidth The box-adjusted width that was set + * @param {Number} adjHeight The box-adjusted height that was set + * @param {Number} rawWidth The width that was originally specified + * @param {Number} rawHeight The height that was originally specified + */ + onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){ + + }, + + /* // protected + * Called after the component is moved, this method is empty by default but can be implemented by any + * subclass that needs to perform custom logic after a move occurs. + * @param {Number} x The new x position + * @param {Number} y The new y position + */ + onPosition : function(x, y){ + + }, + + // private + adjustSize : function(w, h){ + if(this.autoWidth){ + w = 'auto'; + } + if(this.autoHeight){ + h = 'auto'; + } + return {width : w, height: h}; + }, + + // private + adjustPosition : function(x, y){ + return {x : x, y: y}; + } +}); +Ext.reg('box', Ext.BoxComponent); + + +/** + * @class Ext.Spacer + * @extends Ext.BoxComponent + *

Used to provide a sizable space in a layout.

+ * @constructor + * @param {Object} config + */ +Ext.Spacer = Ext.extend(Ext.BoxComponent, { + autoEl:'div' +}); +Ext.reg('spacer', Ext.Spacer);/** + * @class Ext.SplitBar + * @extends Ext.util.Observable + * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized). + *

+ * Usage: + *

+var split = new Ext.SplitBar("elementToDrag", "elementToSize",
+                   Ext.SplitBar.HORIZONTAL, Ext.SplitBar.LEFT);
+split.setAdapter(new Ext.SplitBar.AbsoluteLayoutAdapter("container"));
+split.minSize = 100;
+split.maxSize = 600;
+split.animate = true;
+split.on('moved', splitterMoved);
+
+ * @constructor + * Create a new SplitBar + * @param {Mixed} dragElement The element to be dragged and act as the SplitBar. + * @param {Mixed} resizingElement The element to be resized based on where the SplitBar element is dragged + * @param {Number} orientation (optional) Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL) + * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or + Ext.SplitBar.TOP or Ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial + position of the SplitBar). + */ +Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){ + + /** @private */ + this.el = Ext.get(dragElement, true); + this.el.dom.unselectable = "on"; + /** @private */ + this.resizingEl = Ext.get(resizingElement, true); + + /** + * @private + * The orientation of the split. Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL) + * Note: If this is changed after creating the SplitBar, the placement property must be manually updated + * @type Number + */ + this.orientation = orientation || Ext.SplitBar.HORIZONTAL; + + /** + * The increment, in pixels by which to move this SplitBar. When undefined, the SplitBar moves smoothly. + * @type Number + * @property tickSize + */ + /** + * The minimum size of the resizing element. (Defaults to 0) + * @type Number + */ + this.minSize = 0; + + /** + * The maximum size of the resizing element. (Defaults to 2000) + * @type Number + */ + this.maxSize = 2000; + + /** + * Whether to animate the transition to the new size + * @type Boolean + */ + this.animate = false; + + /** + * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes. + * @type Boolean + */ + this.useShim = false; + + /** @private */ + this.shim = null; + + if(!existingProxy){ + /** @private */ + this.proxy = Ext.SplitBar.createProxy(this.orientation); + }else{ + this.proxy = Ext.get(existingProxy).dom; + } + /** @private */ + this.dd = new Ext.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id}); + + /** @private */ + this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this); + + /** @private */ + this.dd.endDrag = this.onEndProxyDrag.createDelegate(this); + + /** @private */ + this.dragSpecs = {}; + + /** + * @private The adapter to use to positon and resize elements + */ + this.adapter = new Ext.SplitBar.BasicLayoutAdapter(); + this.adapter.init(this); + + if(this.orientation == Ext.SplitBar.HORIZONTAL){ + /** @private */ + this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Ext.SplitBar.LEFT : Ext.SplitBar.RIGHT); + this.el.addClass("x-splitbar-h"); + }else{ + /** @private */ + this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Ext.SplitBar.TOP : Ext.SplitBar.BOTTOM); + this.el.addClass("x-splitbar-v"); + } + + this.addEvents( + /** + * @event resize + * Fires when the splitter is moved (alias for {@link #moved}) + * @param {Ext.SplitBar} this + * @param {Number} newSize the new width or height + */ + "resize", + /** + * @event moved + * Fires when the splitter is moved + * @param {Ext.SplitBar} this + * @param {Number} newSize the new width or height + */ + "moved", + /** + * @event beforeresize + * Fires before the splitter is dragged + * @param {Ext.SplitBar} this + */ + "beforeresize", + + "beforeapply" + ); + + Ext.SplitBar.superclass.constructor.call(this); +}; + +Ext.extend(Ext.SplitBar, Ext.util.Observable, { + onStartProxyDrag : function(x, y){ + this.fireEvent("beforeresize", this); + this.overlay = Ext.DomHelper.append(document.body, {cls: "x-drag-overlay", html: " "}, true); + this.overlay.unselectable(); + this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true)); + this.overlay.show(); + Ext.get(this.proxy).setDisplayed("block"); + var size = this.adapter.getElementSize(this); + this.activeMinSize = this.getMinimumSize(); + this.activeMaxSize = this.getMaximumSize(); + var c1 = size - this.activeMinSize; + var c2 = Math.max(this.activeMaxSize - size, 0); + if(this.orientation == Ext.SplitBar.HORIZONTAL){ + this.dd.resetConstraints(); + this.dd.setXConstraint( + this.placement == Ext.SplitBar.LEFT ? c1 : c2, + this.placement == Ext.SplitBar.LEFT ? c2 : c1, + this.tickSize + ); + this.dd.setYConstraint(0, 0); + }else{ + this.dd.resetConstraints(); + this.dd.setXConstraint(0, 0); + this.dd.setYConstraint( + this.placement == Ext.SplitBar.TOP ? c1 : c2, + this.placement == Ext.SplitBar.TOP ? c2 : c1, + this.tickSize + ); + } + this.dragSpecs.startSize = size; + this.dragSpecs.startPoint = [x, y]; + Ext.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y); + }, + + /** + * @private Called after the drag operation by the DDProxy + */ + onEndProxyDrag : function(e){ + Ext.get(this.proxy).setDisplayed(false); + var endPoint = Ext.lib.Event.getXY(e); + if(this.overlay){ + Ext.destroy(this.overlay); + delete this.overlay; + } + var newSize; + if(this.orientation == Ext.SplitBar.HORIZONTAL){ + newSize = this.dragSpecs.startSize + + (this.placement == Ext.SplitBar.LEFT ? + endPoint[0] - this.dragSpecs.startPoint[0] : + this.dragSpecs.startPoint[0] - endPoint[0] + ); + }else{ + newSize = this.dragSpecs.startSize + + (this.placement == Ext.SplitBar.TOP ? + endPoint[1] - this.dragSpecs.startPoint[1] : + this.dragSpecs.startPoint[1] - endPoint[1] + ); + } + newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize); + if(newSize != this.dragSpecs.startSize){ + if(this.fireEvent('beforeapply', this, newSize) !== false){ + this.adapter.setElementSize(this, newSize); + this.fireEvent("moved", this, newSize); + this.fireEvent("resize", this, newSize); + } + } + }, + + /** + * Get the adapter this SplitBar uses + * @return The adapter object + */ + getAdapter : function(){ + return this.adapter; + }, + + /** + * Set the adapter this SplitBar uses + * @param {Object} adapter A SplitBar adapter object + */ + setAdapter : function(adapter){ + this.adapter = adapter; + this.adapter.init(this); + }, + + /** + * Gets the minimum size for the resizing element + * @return {Number} The minimum size + */ + getMinimumSize : function(){ + return this.minSize; + }, + + /** + * Sets the minimum size for the resizing element + * @param {Number} minSize The minimum size + */ + setMinimumSize : function(minSize){ + this.minSize = minSize; + }, + + /** + * Gets the maximum size for the resizing element + * @return {Number} The maximum size + */ + getMaximumSize : function(){ + return this.maxSize; + }, + + /** + * Sets the maximum size for the resizing element + * @param {Number} maxSize The maximum size + */ + setMaximumSize : function(maxSize){ + this.maxSize = maxSize; + }, + + /** + * Sets the initialize size for the resizing element + * @param {Number} size The initial size + */ + setCurrentSize : function(size){ + var oldAnimate = this.animate; + this.animate = false; + this.adapter.setElementSize(this, size); + this.animate = oldAnimate; + }, + + /** + * Destroy this splitbar. + * @param {Boolean} removeEl True to remove the element + */ + destroy : function(removeEl){ + Ext.destroy(this.shim, Ext.get(this.proxy)); + this.dd.unreg(); + if(removeEl){ + this.el.remove(); + } + this.purgeListeners(); + } +}); + +/** + * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color. + */ +Ext.SplitBar.createProxy = function(dir){ + var proxy = new Ext.Element(document.createElement("div")); + proxy.unselectable(); + var cls = 'x-splitbar-proxy'; + proxy.addClass(cls + ' ' + (dir == Ext.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v')); + document.body.appendChild(proxy.dom); + return proxy.dom; +}; + +/** + * @class Ext.SplitBar.BasicLayoutAdapter + * Default Adapter. It assumes the splitter and resizing element are not positioned + * elements and only gets/sets the width of the element. Generally used for table based layouts. + */ +Ext.SplitBar.BasicLayoutAdapter = function(){ +}; + +Ext.SplitBar.BasicLayoutAdapter.prototype = { + // do nothing for now + init : function(s){ + + }, + /** + * Called before drag operations to get the current size of the resizing element. + * @param {Ext.SplitBar} s The SplitBar using this adapter + */ + getElementSize : function(s){ + if(s.orientation == Ext.SplitBar.HORIZONTAL){ + return s.resizingEl.getWidth(); + }else{ + return s.resizingEl.getHeight(); + } + }, + + /** + * Called after drag operations to set the size of the resizing element. + * @param {Ext.SplitBar} s The SplitBar using this adapter + * @param {Number} newSize The new size to set + * @param {Function} onComplete A function to be invoked when resizing is complete + */ + setElementSize : function(s, newSize, onComplete){ + if(s.orientation == Ext.SplitBar.HORIZONTAL){ + if(!s.animate){ + s.resizingEl.setWidth(newSize); + if(onComplete){ + onComplete(s, newSize); + } + }else{ + s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut'); + } + }else{ + + if(!s.animate){ + s.resizingEl.setHeight(newSize); + if(onComplete){ + onComplete(s, newSize); + } + }else{ + s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut'); + } + } + } +}; + +/** + *@class Ext.SplitBar.AbsoluteLayoutAdapter + * @extends Ext.SplitBar.BasicLayoutAdapter + * Adapter that moves the splitter element to align with the resized sizing element. + * Used with an absolute positioned SplitBar. + * @param {Mixed} container The container that wraps around the absolute positioned content. If it's + * document.body, make sure you assign an id to the body element. + */ +Ext.SplitBar.AbsoluteLayoutAdapter = function(container){ + this.basic = new Ext.SplitBar.BasicLayoutAdapter(); + this.container = Ext.get(container); +}; + +Ext.SplitBar.AbsoluteLayoutAdapter.prototype = { + init : function(s){ + this.basic.init(s); + }, + + getElementSize : function(s){ + return this.basic.getElementSize(s); + }, + + setElementSize : function(s, newSize, onComplete){ + this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s])); + }, + + moveSplitter : function(s){ + var yes = Ext.SplitBar; + switch(s.placement){ + case yes.LEFT: + s.el.setX(s.resizingEl.getRight()); + break; + case yes.RIGHT: + s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px"); + break; + case yes.TOP: + s.el.setY(s.resizingEl.getBottom()); + break; + case yes.BOTTOM: + s.el.setY(s.resizingEl.getTop() - s.el.getHeight()); + break; + } + } +}; + +/** + * Orientation constant - Create a vertical SplitBar + * @static + * @type Number + */ +Ext.SplitBar.VERTICAL = 1; + +/** + * Orientation constant - Create a horizontal SplitBar + * @static + * @type Number + */ +Ext.SplitBar.HORIZONTAL = 2; + +/** + * Placement constant - The resizing element is to the left of the splitter element + * @static + * @type Number + */ +Ext.SplitBar.LEFT = 1; + +/** + * Placement constant - The resizing element is to the right of the splitter element + * @static + * @type Number + */ +Ext.SplitBar.RIGHT = 2; + +/** + * Placement constant - The resizing element is positioned above the splitter element + * @static + * @type Number + */ +Ext.SplitBar.TOP = 3; + +/** + * Placement constant - The resizing element is positioned under splitter element + * @static + * @type Number + */ +Ext.SplitBar.BOTTOM = 4; +/** + * @class Ext.Container + * @extends Ext.BoxComponent + *

Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the + * basic behavior of containing items, namely adding, inserting and removing items.

+ * + *

The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}. + * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight + * Container to be encapsulated by an HTML element to your specifications by using the + * {@link Ext.Component#autoEl autoEl} config option. This is a useful technique when creating + * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels} + * for example.

+ * + *

The code below illustrates both how to explicitly create a Container, and how to implicitly + * create one using the 'container' xtype:


+// explicitly create a Container
+var embeddedColumns = new Ext.Container({
+    autoEl: 'div',  // This is the default
+    layout: 'column',
+    defaults: {
+        // implicitly create Container by specifying xtype
+        xtype: 'container',
+        autoEl: 'div', // This is the default.
+        layout: 'form',
+        columnWidth: 0.5,
+        style: {
+            padding: '10px'
+        }
+    },
+//  The two items below will be Ext.Containers, each encapsulated by a <DIV> element.
+    items: [{
+        items: {
+            xtype: 'datefield',
+            name: 'startDate',
+            fieldLabel: 'Start date'
+        }
+    }, {
+        items: {
+            xtype: 'datefield',
+            name: 'endDate',
+            fieldLabel: 'End date'
+        }
+    }]
+});

+ * + *

Layout

+ *

Container classes delegate the rendering of child Components to a layout + * manager class which must be configured into the Container using the + * {@link #layout} configuration property.

+ *

When either specifying child {@link #items} of a Container, + * or dynamically {@link #add adding} Components to a Container, remember to + * consider how you wish the Container to arrange those child elements, and + * whether those child elements need to be sized using one of Ext's built-in + * {@link #layout} schemes. By default, Containers use the + * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only + * renders child components, appending them one after the other inside the + * Container, and does not apply any sizing at all.

+ *

A common mistake is when a developer neglects to specify a + * {@link #layout} (e.g. widgets like GridPanels or + * TreePanels are added to Containers for which no {@link #layout} + * has been specified). If a Container is left to use the default + * {@link Ext.layout.ContainerLayout ContainerLayout} scheme, none of its + * child components will be resized, or changed in any way when the Container + * is resized.

+ *

Certain layout managers allow dynamic addition of child components. + * Those that do include {@link Ext.layout.CardLayout}, + * {@link Ext.layout.AnchorLayout}, {@link Ext.layout.FormLayout}, and + * {@link Ext.layout.TableLayout}. For example:


+//  Create the GridPanel.
+var myNewGrid = new Ext.grid.GridPanel({
+    store: myStore,
+    columns: myColumnModel,
+    title: 'Results', // the title becomes the title of the tab
+});
+
+myTabPanel.add(myNewGrid); // {@link Ext.TabPanel} implicitly uses {@link Ext.layout.CardLayout CardLayout}
+myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid);
+ * 

+ *

The example above adds a newly created GridPanel to a TabPanel. Note that + * a TabPanel uses {@link Ext.layout.CardLayout} as its layout manager which + * means all its child items are sized to {@link Ext.layout.FitLayout fit} + * exactly into its client area. + *

Overnesting is a common problem. + * An example of overnesting occurs when a GridPanel is added to a TabPanel + * by wrapping the GridPanel inside a wrapping Panel (that has no + * {@link #layout} specified) and then add that wrapping Panel + * to the TabPanel. The point to realize is that a GridPanel is a + * Component which can be added directly to a Container. If the wrapping Panel + * has no {@link #layout} configuration, then the overnested + * GridPanel will not be sized as expected.

+ + * + *

Adding via remote configuration

+ * + *

A server side script can be used to add Components which are generated dynamically on the server. + * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server + * based on certain parameters: + *


+// execute an Ajax request to invoke server side script:
+Ext.Ajax.request({
+    url: 'gen-invoice-grid.php',
+    // send additional parameters to instruct server script
+    params: {
+        startDate: Ext.getCmp('start-date').getValue(),
+        endDate: Ext.getCmp('end-date').getValue()
+    },
+    // process the response object to add it to the TabPanel:
+    success: function(xhr) {
+        var newComponent = eval(xhr.responseText); // see discussion below
+        myTabPanel.add(newComponent); // add the component to the TabPanel
+        myTabPanel.setActiveTab(newComponent);
+    },
+    failure: function() {
+        Ext.Msg.alert("Grid create failed", "Server communication failure");
+    }
+});
+
+ *

The server script needs to return an executable Javascript statement which, when processed + * using eval(), will return either a config object with an {@link Ext.Component#xtype xtype}, + * or an instantiated Component. The server might return this for example:


+(function() {
+    function formatDate(value){
+        return value ? value.dateFormat('M d, Y') : '';
+    };
+
+    var store = new Ext.data.Store({
+        url: 'get-invoice-data.php',
+        baseParams: {
+            startDate: '01/01/2008',
+            endDate: '01/31/2008'
+        },
+        reader: new Ext.data.JsonReader({
+            record: 'transaction',
+            idProperty: 'id',
+            totalRecords: 'total'
+        }, [
+           'customer',
+           'invNo',
+           {name: 'date', type: 'date', dateFormat: 'm/d/Y'},
+           {name: 'value', type: 'float'}
+        ])
+    });
+
+    var grid = new Ext.grid.GridPanel({
+        title: 'Invoice Report',
+        bbar: new Ext.PagingToolbar(store),
+        store: store,
+        columns: [
+            {header: "Customer", width: 250, dataIndex: 'customer', sortable: true},
+            {header: "Invoice Number", width: 120, dataIndex: 'invNo', sortable: true},
+            {header: "Invoice Date", width: 100, dataIndex: 'date', renderer: formatDate, sortable: true},
+            {header: "Value", width: 120, dataIndex: 'value', renderer: 'usMoney', sortable: true}
+        ],
+    });
+    store.load();
+    return grid;  // return instantiated component
+})();
+
+ *

When the above code fragment is passed through the eval function in the success handler + * of the Ajax request, the code is executed by the Javascript processor, and the anonymous function + * runs, and returns the instantiated grid component.

+ *

Note: since the code above is generated by a server script, the baseParams for + * the Store, the metadata to allow generation of the Record layout, and the ColumnModel + * can all be generated into the code since these are all known on the server.

+ * + * @xtype container + */ +Ext.Container = Ext.extend(Ext.BoxComponent, { + /** + * @cfg {Boolean} monitorResize + * True to automatically monitor window resize events to handle anything that is sensitive to the current size + * of the viewport. This value is typically managed by the chosen {@link #layout} and should not need + * to be set manually. + */ + /** + * @cfg {String/Object} layout + * When creating complex UIs, it is important to remember that sizing and + * positioning of child items is the responsibility of the Container's + * layout manager. If you expect child items to be sized in response to + * user interactions, you must specify a layout manager which + * creates and manages the type of layout you have in mind. For example:

+new Ext.Window({
+    width:300, height: 300,
+    layout: 'fit', // explicitly set layout manager: override the default (layout:'auto')
+    items: [{
+        title: 'Panel inside a Window'
+    }]
+}).show();
+     * 
+ *

Omitting the {@link #layout} config means that the + * {@link Ext.layout.ContainerLayout default layout manager} will be used which does + * nothing but render child components sequentially into the Container (no sizing or + * positioning will be performed in this situation).

+ *

The layout manager class for this container may be specified as either as an + * Object or as a String:

+ *
+ */ + /** + * @cfg {Object} layoutConfig + * This is a config object containing properties specific to the chosen + * {@link #layout} if {@link #layout} + * has been specified as a string.

+ */ + /** + * @cfg {Boolean/Number} bufferResize + * When set to true (100 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer + * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers + * with a large quantity of sub-components for which frequent layout calls would be expensive. + */ + bufferResize: 100, + + /** + * @cfg {String/Number} activeItem + * A string component id or the numeric index of the component that should be initially activated within the + * container's layout on render. For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first + * item in the container's collection). activeItem only applies to layout styles that can display + * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and + * {@link Ext.layout.FitLayout}). Related to {@link Ext.layout.ContainerLayout#activeItem}. + */ + /** + * @cfg {Object/Array} items + *
** IMPORTANT: be sure to specify a {@link #layout} ! **
+ *

A single item, or an array of child Components to be added to this container, + * for example:

+ *

+// specifying a single item
+items: {...},
+layout: 'fit',    // specify a layout!
+
+// specifying multiple items
+items: [{...}, {...}],
+layout: 'anchor', // specify a layout!
+     * 
+ *

Each item may be:

+ *
+ *

Notes:

+ *
+ */ + /** + * @cfg {Object} defaults + *

A config object that will be applied to all components added to this container either via the {@link #items} + * config or via the {@link #add} or {@link #insert} methods. The defaults config can contain any + * number of name/value property pairs to be added to each item, and should be valid for the types of items + * being added to the container. For example, to automatically apply padding to the body of each of a set of + * contained {@link Ext.Panel} items, you could pass: defaults: {bodyStyle:'padding:15px'}.


+ *

Note: defaults will not be applied to config objects if the option is already specified. + * For example:


+defaults: {               // defaults are applied to items, not the container
+    autoScroll:true
+},
+items: [
+    {
+        xtype: 'panel',   // defaults do not have precedence over
+        id: 'panel1',     // options in config objects, so the defaults
+        autoScroll: false // will not be applied here, panel1 will be autoScroll:false
+    },
+    new Ext.Panel({       // defaults do have precedence over options
+        id: 'panel2',     // options in components, so the defaults
+        autoScroll: false // will be applied here, panel2 will be autoScroll:true.
+    })
+]
+     * 
+ */ + + + /** @cfg {Boolean} autoDestroy + * If true the container will automatically destroy any contained component that is removed from it, else + * destruction must be handled manually (defaults to true). + */ + autoDestroy : true, + + /** @cfg {Boolean} forceLayout + * If true the container will force a layout initially even if hidden or collapsed. This option + * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false). + */ + forceLayout: false, + + /** @cfg {Boolean} hideBorders + * True to hide the borders of each contained component, false to defer to the component's existing + * border settings (defaults to false). + */ + /** @cfg {String} defaultType + *

The default {@link Ext.Component xtype} of child Components to create in this Container when + * a child item is specified as a raw configuration object, rather than as an instantiated Component.

+ *

Defaults to 'panel', except {@link Ext.menu.Menu} which defaults to 'menuitem', + * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to 'button'.

+ */ + defaultType : 'panel', + + // private + initComponent : function(){ + Ext.Container.superclass.initComponent.call(this); + + this.addEvents( + /** + * @event afterlayout + * Fires when the components in this container are arranged by the associated layout manager. + * @param {Ext.Container} this + * @param {ContainerLayout} layout The ContainerLayout implementation for this container + */ + 'afterlayout', + /** + * @event beforeadd + * Fires before any {@link Ext.Component} is added or inserted into the container. + * A handler can return false to cancel the add. + * @param {Ext.Container} this + * @param {Ext.Component} component The component being added + * @param {Number} index The index at which the component will be added to the container's items collection + */ + 'beforeadd', + /** + * @event beforeremove + * Fires before any {@link Ext.Component} is removed from the container. A handler can return + * false to cancel the remove. + * @param {Ext.Container} this + * @param {Ext.Component} component The component being removed + */ + 'beforeremove', + /** + * @event add + * @bubbles + * Fires after any {@link Ext.Component} is added or inserted into the container. + * @param {Ext.Container} this + * @param {Ext.Component} component The component that was added + * @param {Number} index The index at which the component was added to the container's items collection + */ + 'add', + /** + * @event remove + * @bubbles + * Fires after any {@link Ext.Component} is removed from the container. + * @param {Ext.Container} this + * @param {Ext.Component} component The component that was removed + */ + 'remove' + ); + + this.enableBubble('add', 'remove'); + + /** + * The collection of components in this container as a {@link Ext.util.MixedCollection} + * @type MixedCollection + * @property items + */ + var items = this.items; + if(items){ + delete this.items; + if(Ext.isArray(items) && items.length > 0){ + this.add.apply(this, items); + }else{ + this.add(items); + } + } + }, + + // private + initItems : function(){ + if(!this.items){ + this.items = new Ext.util.MixedCollection(false, this.getComponentId); + this.getLayout(); // initialize the layout + } + }, + + // private + setLayout : function(layout){ + if(this.layout && this.layout != layout){ + this.layout.setContainer(null); + } + this.initItems(); + this.layout = layout; + layout.setContainer(this); + }, + + // private + render : function(){ + Ext.Container.superclass.render.apply(this, arguments); + if(this.layout){ + if(Ext.isObject(this.layout) && !this.layout.layout){ + this.layoutConfig = this.layout; + this.layout = this.layoutConfig.type; + } + if(typeof this.layout == 'string'){ + this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig); + } + this.setLayout(this.layout); + + if(this.activeItem !== undefined){ + var item = this.activeItem; + delete this.activeItem; + this.layout.setActiveItem(item); + } + } + if(!this.ownerCt){ + // force a layout if no ownerCt is set + this.doLayout(false, true); + } + if(this.monitorResize === true){ + Ext.EventManager.onWindowResize(this.doLayout, this, [false]); + } + }, + + /** + *

Returns the Element to be used to contain the child Components of this Container.

+ *

An implementation is provided which returns the Container's {@link #getEl Element}, but + * if there is a more complex structure to a Container, this may be overridden to return + * the element into which the {@link #layout layout} renders child Components.

+ * @return {Ext.Element} The Element to render child Components into. + */ + getLayoutTarget : function(){ + return this.el; + }, + + // private - used as the key lookup function for the items collection + getComponentId : function(comp){ + return comp.getItemId(); + }, + + /** + *

Adds {@link Ext.Component Component}(s) to this Container.

+ *

Description : + *

+ *

Notes : + *

+ * @param {Object/Array} component + *

Either a single component or an Array of components to add. See + * {@link #items} for additional information.

+ * @param {Object} (Optional) component_2 + * @param {Object} (Optional) component_n + * @return {Ext.Component} component The Component (or config object) that was added. + */ + add : function(comp){ + this.initItems(); + var args = arguments.length > 1; + if(args || Ext.isArray(comp)){ + Ext.each(args ? arguments : comp, function(c){ + this.add(c); + }, this); + return; + } + var c = this.lookupComponent(this.applyDefaults(comp)); + var pos = this.items.length; + if(this.fireEvent('beforeadd', this, c, pos) !== false && this.onBeforeAdd(c) !== false){ + this.items.add(c); + c.ownerCt = this; + this.fireEvent('add', this, c, pos); + } + return c; + }, + + /** + * Inserts a Component into this Container at a specified index. Fires the + * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the + * Component has been inserted. + * @param {Number} index The index at which the Component will be inserted + * into the Container's items collection + * @param {Ext.Component} component The child Component to insert.

+ * Ext uses lazy rendering, and will only render the inserted Component should + * it become necessary.

+ * A Component config object may be passed in order to avoid the overhead of + * constructing a real Component object if lazy rendering might mean that the + * inserted Component will not be rendered immediately. To take advantage of + * this 'lazy instantiation', set the {@link Ext.Component#xtype} config + * property to the registered type of the Component wanted.

+ * For a list of all available xtypes, see {@link Ext.Component}. + * @return {Ext.Component} component The Component (or config object) that was + * inserted with the Container's default config values applied. + */ + insert : function(index, comp){ + this.initItems(); + var a = arguments, len = a.length; + if(len > 2){ + for(var i = len-1; i >= 1; --i) { + this.insert(index, a[i]); + } + return; + } + var c = this.lookupComponent(this.applyDefaults(comp)); + + if(c.ownerCt == this && this.items.indexOf(c) < index){ + --index; + } + + if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){ + this.items.insert(index, c); + c.ownerCt = this; + this.fireEvent('add', this, c, index); + } + return c; + }, + + // private + applyDefaults : function(c){ + if(this.defaults){ + if(typeof c == 'string'){ + c = Ext.ComponentMgr.get(c); + Ext.apply(c, this.defaults); + }else if(!c.events){ + Ext.applyIf(c, this.defaults); + }else{ + Ext.apply(c, this.defaults); + } + } + return c; + }, + + // private + onBeforeAdd : function(item){ + if(item.ownerCt){ + item.ownerCt.remove(item, false); + } + if(this.hideBorders === true){ + item.border = (item.border === true); + } + }, + + /** + * Removes a component from this container. Fires the {@link #beforeremove} event before removing, then fires + * the {@link #remove} event after the component has been removed. + * @param {Component/String} component The component reference or id to remove. + * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. + * Defaults to the value of this Container's {@link #autoDestroy} config. + * @return {Ext.Component} component The Component that was removed. + */ + remove : function(comp, autoDestroy){ + this.initItems(); + var c = this.getComponent(comp); + if(c && this.fireEvent('beforeremove', this, c) !== false){ + this.items.remove(c); + delete c.ownerCt; + if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){ + c.destroy(); + } + if(this.layout && this.layout.activeItem == c){ + delete this.layout.activeItem; + } + this.fireEvent('remove', this, c); + } + return c; + }, + + /** + * Removes all components from this container. + * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function. + * Defaults to the value of this Container's {@link #autoDestroy} config. + * @return {Array} Array of the destroyed components + */ + removeAll: function(autoDestroy){ + this.initItems(); + var item, rem = [], items = []; + this.items.each(function(i){ + rem.push(i); + }); + for (var i = 0, len = rem.length; i < len; ++i){ + item = rem[i]; + this.remove(item, autoDestroy); + if(item.ownerCt !== this){ + items.push(item); + } + } + return items; + }, + + /** + * Examines this container's {@link #items} property + * and gets a direct child component of this container. + * @param {String/Number} comp This parameter may be any of the following: + *
+ *

For additional information see {@link Ext.util.MixedCollection#get}. + * @return Ext.Component The component (if found). + */ + getComponent : function(comp){ + if(Ext.isObject(comp)){ + return comp; + } + return this.items.get(comp); + }, + + // private + lookupComponent : function(comp){ + if(typeof comp == 'string'){ + return Ext.ComponentMgr.get(comp); + }else if(!comp.events){ + return this.createComponent(comp); + } + return comp; + }, + + // private + createComponent : function(config){ + return Ext.create(config, this.defaultType); + }, + + /** + * Force this container's layout to be recalculated. A call to this function is required after adding a new component + * to an already rendered container, or possibly after changing sizing/position properties of child components. + * @param {Boolean} shallow (optional) True to only calc the layout of this component, and let child components auto + * calc layouts as required (defaults to false, which calls doLayout recursively for each subcontainer) + * @param {Boolean} force (optional) True to force a layout to occur, even if the item is hidden. + * @return {Ext.Container} this + */ + doLayout: function(shallow, force){ + var rendered = this.rendered, + forceLayout = this.forceLayout; + + if(!this.isVisible() || this.collapsed){ + this.deferLayout = this.deferLayout || !shallow; + if(!(force || forceLayout)){ + return; + } + shallow = shallow && !this.deferLayout; + } else { + delete this.deferLayout; + } + if(rendered && this.layout){ + this.layout.layout(); + } + if(shallow !== true && this.items){ + var cs = this.items.items; + for(var i = 0, len = cs.length; i < len; i++){ + var c = cs[i]; + if(c.doLayout){ + c.forceLayout = forceLayout; + c.doLayout(); + } + } + } + if(rendered){ + this.onLayout(shallow, force); + } + delete this.forceLayout; + }, + + //private + onLayout : Ext.emptyFn, + + onShow : function(){ + Ext.Container.superclass.onShow.call(this); + if(this.deferLayout !== undefined){ + this.doLayout(true); + } + }, + + /** + * Returns the layout currently in use by the container. If the container does not currently have a layout + * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout. + * @return {ContainerLayout} layout The container's layout + */ + getLayout : function(){ + if(!this.layout){ + var layout = new Ext.layout.ContainerLayout(this.layoutConfig); + this.setLayout(layout); + } + return this.layout; + }, + + // private + beforeDestroy : function(){ + if(this.items){ + Ext.destroy.apply(Ext, this.items.items); + } + if(this.monitorResize){ + Ext.EventManager.removeResizeListener(this.doLayout, this); + } + Ext.destroy(this.layout); + Ext.Container.superclass.beforeDestroy.call(this); + }, + + /** + * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (this) of + * function call will be the scope provided or the current component. The arguments to the function + * will be the args provided or the current component. If the function returns false at any point, + * the bubble is stopped. + * @param {Function} fn The function to call + * @param {Object} scope (optional) The scope of the function (defaults to current node) + * @param {Array} args (optional) The args to call the function with (default to passing the current component) + * @return {Ext.Container} this + */ + bubble : function(fn, scope, args){ + var p = this; + while(p){ + if(fn.apply(scope || p, args || [p]) === false){ + break; + } + p = p.ownerCt; + } + return this; + }, + + /** + * Cascades down the component/container heirarchy from this component (called first), calling the specified function with + * each component. The scope (this) of + * function call will be the scope provided or the current component. The arguments to the function + * will be the args provided or the current component. If the function returns false at any point, + * the cascade is stopped on that branch. + * @param {Function} fn The function to call + * @param {Object} scope (optional) The scope of the function (defaults to current component) + * @param {Array} args (optional) The args to call the function with (defaults to passing the current component) + * @return {Ext.Container} this + */ + cascade : function(fn, scope, args){ + if(fn.apply(scope || this, args || [this]) !== false){ + if(this.items){ + var cs = this.items.items; + for(var i = 0, len = cs.length; i < len; i++){ + if(cs[i].cascade){ + cs[i].cascade(fn, scope, args); + }else{ + fn.apply(scope || cs[i], args || [cs[i]]); + } + } + } + } + return this; + }, + + /** + * Find a component under this container at any level by id + * @param {String} id + * @return Ext.Component + */ + findById : function(id){ + var m, ct = this; + this.cascade(function(c){ + if(ct != c && c.id === id){ + m = c; + return false; + } + }); + return m || null; + }, + + /** + * Find a component under this container at any level by xtype or class + * @param {String/Class} xtype The xtype string for a component, or the class of the component directly + * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is + * the default), or true to check whether this Component is directly of the specified xtype. + * @return {Array} Array of Ext.Components + */ + findByType : function(xtype, shallow){ + return this.findBy(function(c){ + return c.isXType(xtype, shallow); + }); + }, + + /** + * Find a component under this container at any level by property + * @param {String} prop + * @param {String} value + * @return {Array} Array of Ext.Components + */ + find : function(prop, value){ + return this.findBy(function(c){ + return c[prop] === value; + }); + }, + + /** + * Find a component under this container at any level by a custom function. If the passed function returns + * true, the component will be included in the results. The passed function is called with the arguments (component, this container). + * @param {Function} fn The function to call + * @param {Object} scope (optional) + * @return {Array} Array of Ext.Components + */ + findBy : function(fn, scope){ + var m = [], ct = this; + this.cascade(function(c){ + if(ct != c && fn.call(scope || c, c, ct) === true){ + m.push(c); + } + }); + return m; + }, + + /** + * Get a component contained by this container (alias for items.get(key)) + * @param {String/Number} key The index or id of the component + * @return {Ext.Component} Ext.Component + */ + get : function(key){ + return this.items.get(key); + } +}); + +Ext.Container.LAYOUTS = {}; +Ext.reg('container', Ext.Container); +/** + * @class Ext.layout.ContainerLayout + *

The ContainerLayout class is the default layout manager delegated by {@link Ext.Container} to + * render any child Components when no {@link Ext.Container#layout layout} is configured into + * a {@link Ext.Container Container}. ContainerLayout provides the basic foundation for all other layout + * classes in Ext. It simply renders all child Components into the Container, performing no sizing or + * positioning services. To utilize a layout that provides sizing and positioning of child Components, + * specify an appropriate {@link Ext.Container#layout layout}.

+ *

This class is intended to be extended or created via the {@link Ext.Container#layout layout} + * configuration property. See {@link Ext.Container#layout} for additional details.

+ */ +Ext.layout.ContainerLayout = function(config){ + Ext.apply(this, config); +}; + +Ext.layout.ContainerLayout.prototype = { + /** + * @cfg {String} extraCls + *

An optional extra CSS class that will be added to the container. This can be useful for adding + * customized styles to the container or any of its children using standard CSS rules. See + * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.

+ *

Note: extraCls defaults to '' except for the following classes + * which assign a value by default: + *

+ * To configure the above Classes with an extra CSS class append to the default. For example, + * for ColumnLayout:

+     * extraCls: 'x-column custom-class'
+     * 
+ *

+ */ + /** + * @cfg {Boolean} renderHidden + * True to hide each contained item on render (defaults to false). + */ + + /** + * A reference to the {@link Ext.Component} that is active. For example,

+     * if(myPanel.layout.activeItem.id == 'item-1') { ... }
+     * 
+ * activeItem only applies to layout styles that can display items one at a time + * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} + * and {@link Ext.layout.FitLayout}). Read-only. Related to {@link Ext.Container#activeItem}. + * @type {Ext.Component} + * @property activeItem + */ + + // private + monitorResize:false, + // private + activeItem : null, + + // private + layout : function(){ + var target = this.container.getLayoutTarget(); + this.onLayout(this.container, target); + this.container.fireEvent('afterlayout', this.container, this); + }, + + // private + onLayout : function(ct, target){ + this.renderAll(ct, target); + }, + + // private + isValidParent : function(c, target){ + return target && c.getDomPositionEl().dom.parentNode == (target.dom || target); + }, + + // private + renderAll : function(ct, target){ + var items = ct.items.items; + for(var i = 0, len = items.length; i < len; i++) { + var c = items[i]; + if(c && (!c.rendered || !this.isValidParent(c, target))){ + this.renderItem(c, i, target); + } + } + }, + + // private + renderItem : function(c, position, target){ + if(c && !c.rendered){ + c.render(target, position); + this.configureItem(c, position); + }else if(c && !this.isValidParent(c, target)){ + if(typeof position == 'number'){ + position = target.dom.childNodes[position]; + } + target.dom.insertBefore(c.getDomPositionEl().dom, position || null); + c.container = target; + this.configureItem(c, position); + } + }, + + // private + configureItem: function(c, position){ + if(this.extraCls){ + var t = c.getPositionEl ? c.getPositionEl() : c; + t.addClass(this.extraCls); + } + if (this.renderHidden && c != this.activeItem) { + c.hide(); + } + if(c.doLayout){ + c.doLayout(false, this.forceLayout); + } + }, + + // private + onResize: function(){ + if(this.container.collapsed){ + return; + } + var b = this.container.bufferResize; + if(b){ + if(!this.resizeTask){ + this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this); + this.resizeBuffer = typeof b == 'number' ? b : 100; + } + this.resizeTask.delay(this.resizeBuffer); + }else{ + this.runLayout(); + } + }, + + // private + runLayout: function(){ + this.layout(); + this.container.onLayout(); + }, + + // private + setContainer : function(ct){ + if(this.monitorResize && ct != this.container){ + if(this.container){ + this.container.un('resize', this.onResize, this); + this.container.un('bodyresize', this.onResize, this); + } + if(ct){ + ct.on({ + scope: this, + resize: this.onResize, + bodyresize: this.onResize + }); + } + } + this.container = ct; + }, + + // private + parseMargins : function(v){ + if(typeof v == 'number'){ + v = v.toString(); + } + var ms = v.split(' '); + var len = ms.length; + if(len == 1){ + ms[1] = ms[0]; + ms[2] = ms[0]; + ms[3] = ms[0]; + } + if(len == 2){ + ms[2] = ms[0]; + ms[3] = ms[1]; + } + if(len == 3){ + ms[3] = ms[1]; + } + return { + top:parseInt(ms[0], 10) || 0, + right:parseInt(ms[1], 10) || 0, + bottom:parseInt(ms[2], 10) || 0, + left:parseInt(ms[3], 10) || 0 + }; + }, + + /** + * The {@link Template Ext.Template} used by Field rendering layout classes (such as + * {@link Ext.layout.FormLayout}) to create the DOM structure of a fully wrapped, + * labeled and styled form Field. A default Template is supplied, but this may be + * overriden to create custom field structures. The template processes values returned from + * {@link Ext.layout.FormLayout#getTemplateArgs}. + * @property fieldTpl + * @type Ext.Template + */ + fieldTpl: (function() { + var t = new Ext.Template( + '
', + '', + '
', + '
', + '
' + ); + t.disableFormats = true; + return t.compile(); + })(), + + /* + * Destroys this layout. This is a template method that is empty by default, but should be implemented + * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes. + * @protected + */ + destroy : Ext.emptyFn +}; +Ext.Container.LAYOUTS['auto'] = Ext.layout.ContainerLayout;/** + * @class Ext.layout.FitLayout + * @extends Ext.layout.ContainerLayout + *

This is a base class for layouts that contain a single item that automatically expands to fill the layout's + * container. This class is intended to be extended or created via the layout:'fit' {@link Ext.Container#layout} + * config, and should generally not need to be created directly via the new keyword.

+ *

FitLayout does not have any direct config options (other than inherited ones). To fit a panel to a container + * using FitLayout, simply set layout:'fit' on the container and add a single panel to it. If the container has + * multiple panels, only the first one will be displayed. Example usage:

+ *

+var p = new Ext.Panel({
+    title: 'Fit Layout',
+    layout:'fit',
+    items: {
+        title: 'Inner Panel',
+        html: '<p>This is the inner panel content</p>',
+        border: false
+    }
+});
+
+ */ +Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, { + // private + monitorResize:true, + + // private + onLayout : function(ct, target){ + Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target); + if(!this.container.collapsed){ + var sz = (Ext.isIE6 && Ext.isStrict && target.dom == document.body) ? target.getViewSize() : target.getStyleSize(); + this.setItemSize(this.activeItem || ct.items.itemAt(0), sz); + } + }, + + // private + setItemSize : function(item, size){ + if(item && size.height > 0){ // display none? + item.setSize(size); + } + } +}); +Ext.Container.LAYOUTS['fit'] = Ext.layout.FitLayout;/** + * @class Ext.layout.CardLayout + * @extends Ext.layout.FitLayout + *

This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be + * visible at any given time. This layout style is most commonly used for wizards, tab implementations, etc. + * This class is intended to be extended or created via the layout:'card' {@link Ext.Container#layout} config, + * and should generally not need to be created directly via the new keyword.

+ *

The CardLayout's focal method is {@link #setActiveItem}. Since only one panel is displayed at a time, + * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of + * the next panel to display. The layout itself does not provide a user interface for handling this navigation, + * so that functionality must be provided by the developer.

+ *

In the following example, a simplistic wizard setup is demonstrated. A button bar is added + * to the footer of the containing panel to provide navigation buttons. The buttons will be handled by a + * common navigation routine -- for this example, the implementation of that routine has been ommitted since + * it can be any type of custom logic. Note that other uses of a CardLayout (like a tab control) would require a + * completely different implementation. For serious implementations, a better approach would be to extend + * CardLayout to provide the custom functionality needed. Example usage:

+ *

+var navHandler = function(direction){
+    // This routine could contain business logic required to manage the navigation steps.
+    // It would call setActiveItem as needed, manage navigation button state, handle any
+    // branching logic that might be required, handle alternate actions like cancellation
+    // or finalization, etc.  A complete wizard implementation could get pretty
+    // sophisticated depending on the complexity required, and should probably be
+    // done as a subclass of CardLayout in a real-world implementation.
+};
+
+var card = new Ext.Panel({
+    title: 'Example Wizard',
+    layout:'card',
+    activeItem: 0, // make sure the active item is set on the container config!
+    bodyStyle: 'padding:15px',
+    defaults: {
+        // applied to each contained panel
+        border:false
+    },
+    // just an example of one possible navigation scheme, using buttons
+    bbar: [
+        {
+            id: 'move-prev',
+            text: 'Back',
+            handler: navHandler.createDelegate(this, [-1]),
+            disabled: true
+        },
+        '->', // greedy spacer so that the buttons are aligned to each side
+        {
+            id: 'move-next',
+            text: 'Next',
+            handler: navHandler.createDelegate(this, [1])
+        }
+    ],
+    // the panels (or "cards") within the layout
+    items: [{
+        id: 'card-0',
+        html: '<h1>Welcome to the Wizard!</h1><p>Step 1 of 3</p>'
+    },{
+        id: 'card-1',
+        html: '<p>Step 2 of 3</p>'
+    },{
+        id: 'card-2',
+        html: '<h1>Congratulations!</h1><p>Step 3 of 3 - Complete</p>'
+    }]
+});
+
+ */ +Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, { + /** + * @cfg {Boolean} deferredRender + * True to render each contained item at the time it becomes active, false to render all contained items + * as soon as the layout is rendered (defaults to false). If there is a significant amount of content or + * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to + * true might improve performance. + */ + deferredRender : false, + + /** + * @cfg {Boolean} layoutOnCardChange + * True to force a layout of the active item when the active card is changed. Defaults to false. + */ + layoutOnCardChange : false, + + /** + * @cfg {Boolean} renderHidden @hide + */ + // private + renderHidden : true, + + constructor: function(config){ + Ext.layout.CardLayout.superclass.constructor.call(this, config); + this.forceLayout = (this.deferredRender === false); + }, + + /** + * Sets the active (visible) item in the layout. + * @param {String/Number} item The string component id or numeric index of the item to activate + */ + setActiveItem : function(item){ + item = this.container.getComponent(item); + if(this.activeItem != item){ + if(this.activeItem){ + this.activeItem.hide(); + } + this.activeItem = item; + item.show(); + this.container.doLayout(); + if(this.layoutOnCardChange && item.doLayout){ + item.doLayout(); + } + } + }, + + // private + renderAll : function(ct, target){ + if(this.deferredRender){ + this.renderItem(this.activeItem, undefined, target); + }else{ + Ext.layout.CardLayout.superclass.renderAll.call(this, ct, target); + } + } +}); +Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;/** + * @class Ext.layout.AnchorLayout + * @extends Ext.layout.ContainerLayout + *

This is a layout that enables anchoring of contained elements relative to the container's dimensions. + * If the container is resized, all anchored items are automatically rerendered according to their + * {@link #anchor} rules.

+ *

This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout} + * config, and should generally not need to be created directly via the new keyword.

+ *

AnchorLayout does not have any direct config options (other than inherited ones). By default, + * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the + * container using the AnchorLayout can supply an anchoring-specific config property of anchorSize. + * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating + * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring + * logic if necessary. For example:

+ *

+var viewport = new Ext.Viewport({
+    layout:'anchor',
+    anchorSize: {width:800, height:600},
+    items:[{
+        title:'Item 1',
+        html:'Content 1',
+        width:800,
+        anchor:'right 20%'
+    },{
+        title:'Item 2',
+        html:'Content 2',
+        width:300,
+        anchor:'50% 30%'
+    },{
+        title:'Item 3',
+        html:'Content 3',
+        width:600,
+        anchor:'-100 50%'
+    }]
+});
+ * 
+ */ +Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, { + /** + * @cfg {String} anchor + *

This configuation option is to be applied to child items of a container managed by + * this layout (ie. configured with layout:'anchor').


+ * + *

This value is what tells the layout how an item should be anchored to the container. items + * added to an AnchorLayout accept an anchoring-specific config property of anchor which is a string + * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%'). + * The following types of anchor values are supported:

+ */ + + // private + monitorResize:true, + + // private + getAnchorViewSize : function(ct, target){ + return target.dom == document.body ? + target.getViewSize() : target.getStyleSize(); + }, + + // private + onLayout : function(ct, target){ + Ext.layout.AnchorLayout.superclass.onLayout.call(this, ct, target); + + var size = this.getAnchorViewSize(ct, target); + + var w = size.width, h = size.height; + + if(w < 20 && h < 20){ + return; + } + + // find the container anchoring size + var aw, ah; + if(ct.anchorSize){ + if(typeof ct.anchorSize == 'number'){ + aw = ct.anchorSize; + }else{ + aw = ct.anchorSize.width; + ah = ct.anchorSize.height; + } + }else{ + aw = ct.initialConfig.width; + ah = ct.initialConfig.height; + } + + var cs = ct.items.items, len = cs.length, i, c, a, cw, ch; + for(i = 0; i < len; i++){ + c = cs[i]; + if(c.anchor){ + a = c.anchorSpec; + if(!a){ // cache all anchor values + var vs = c.anchor.split(' '); + c.anchorSpec = a = { + right: this.parseAnchor(vs[0], c.initialConfig.width, aw), + bottom: this.parseAnchor(vs[1], c.initialConfig.height, ah) + }; + } + cw = a.right ? this.adjustWidthAnchor(a.right(w), c) : undefined; + ch = a.bottom ? this.adjustHeightAnchor(a.bottom(h), c) : undefined; + + if(cw || ch){ + c.setSize(cw || undefined, ch || undefined); + } + } + } + }, + + // private + parseAnchor : function(a, start, cstart){ + if(a && a != 'none'){ + var last; + if(/^(r|right|b|bottom)$/i.test(a)){ // standard anchor + var diff = cstart - start; + return function(v){ + if(v !== last){ + last = v; + return v - diff; + } + } + }else if(a.indexOf('%') != -1){ + var ratio = parseFloat(a.replace('%', ''))*.01; // percentage + return function(v){ + if(v !== last){ + last = v; + return Math.floor(v*ratio); + } + } + }else{ + a = parseInt(a, 10); + if(!isNaN(a)){ // simple offset adjustment + return function(v){ + if(v !== last){ + last = v; + return v + a; + } + } + } + } + } + return false; + }, + + // private + adjustWidthAnchor : function(value, comp){ + return value; + }, + + // private + adjustHeightAnchor : function(value, comp){ + return value; + } + + /** + * @property activeItem + * @hide + */ +}); +Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout;/** + * @class Ext.layout.ColumnLayout + * @extends Ext.layout.ContainerLayout + *

This is the layout style of choice for creating structural layouts in a multi-column format where the width of + * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content. + * This class is intended to be extended or created via the layout:'column' {@link Ext.Container#layout} config, + * and should generally not need to be created directly via the new keyword.

+ *

ColumnLayout does not have any direct config options (other than inherited ones), but it does support a + * specific config property of columnWidth that can be included in the config of any panel added to it. The + * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel. + * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).

+ *

The width property is always evaluated as pixels, and must be a number greater than or equal to 1. + * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and + * less than 1 (e.g., .25).

+ *

The basic rules for specifying column widths are pretty simple. The logic makes two passes through the + * set of contained panels. During the first layout pass, all panels that either have a fixed width or none + * specified (auto) are skipped, but their widths are subtracted from the overall container width. During the second + * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on + * the total remaining container width. In other words, percentage width panels are designed to fill the space + * left over by all the fixed-width and/or auto-width panels. Because of this, while you can specify any number of columns + * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your + * layout may not render as expected. Example usage:

+ *

+// All columns are percentages -- they must add up to 1
+var p = new Ext.Panel({
+    title: 'Column Layout - Percentage Only',
+    layout:'column',
+    items: [{
+        title: 'Column 1',
+        columnWidth: .25 
+    },{
+        title: 'Column 2',
+        columnWidth: .6
+    },{
+        title: 'Column 3',
+        columnWidth: .15
+    }]
+});
+
+// Mix of width and columnWidth -- all columnWidth values must add up
+// to 1. The first column will take up exactly 120px, and the last two
+// columns will fill the remaining container width.
+var p = new Ext.Panel({
+    title: 'Column Layout - Mixed',
+    layout:'column',
+    items: [{
+        title: 'Column 1',
+        width: 120
+    },{
+        title: 'Column 2',
+        columnWidth: .8
+    },{
+        title: 'Column 3',
+        columnWidth: .2
+    }]
+});
+
+ */ +Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, { + // private + monitorResize:true, + + extraCls: 'x-column', + + scrollOffset : 0, + + // private + isValidParent : function(c, target){ + return (c.getPositionEl ? c.getPositionEl() : c.getEl()).dom.parentNode == this.innerCt.dom; + }, + + // private + onLayout : function(ct, target){ + var cs = ct.items.items, len = cs.length, c, i; + + if(!this.innerCt){ + target.addClass('x-column-layout-ct'); + + // the innerCt prevents wrapping and shuffling while + // the container is resizing + this.innerCt = target.createChild({cls:'x-column-inner'}); + this.innerCt.createChild({cls:'x-clear'}); + } + this.renderAll(ct, this.innerCt); + + var size = Ext.isIE && target.dom != Ext.getBody().dom ? target.getStyleSize() : target.getViewSize(); + + if(size.width < 1 && size.height < 1){ // display none? + return; + } + + var w = size.width - target.getPadding('lr') - this.scrollOffset, + h = size.height - target.getPadding('tb'), + pw = w; + + this.innerCt.setWidth(w); + + // some columns can be percentages while others are fixed + // so we need to make 2 passes + + for(i = 0; i < len; i++){ + c = cs[i]; + if(!c.columnWidth){ + pw -= (c.getSize().width + c.getEl().getMargins('lr')); + } + } + + pw = pw < 0 ? 0 : pw; + + for(i = 0; i < len; i++){ + c = cs[i]; + if(c.columnWidth){ + c.setSize(Math.floor(c.columnWidth*pw) - c.getEl().getMargins('lr')); + } + } + } + + /** + * @property activeItem + * @hide + */ +}); + +Ext.Container.LAYOUTS['column'] = Ext.layout.ColumnLayout;/** + * @class Ext.layout.BorderLayout + * @extends Ext.layout.ContainerLayout + *

This is a multi-pane, application-oriented UI layout style that supports multiple + * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between + * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in + * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.

+ *

This class is intended to be extended or created via the layout:'border' + * {@link Ext.Container#layout} config, and should generally not need to be created directly + * via the new keyword.

+ *

BorderLayout does not have any direct config options (other than inherited ones). + * All configuration options available for customizing the BorderLayout are at the + * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion} + * levels.

+ *

Example usage:

+ *

+var myBorderPanel = new Ext.Panel({
+    {@link Ext.Component#renderTo renderTo}: document.body,
+    {@link Ext.BoxComponent#width width}: 700,
+    {@link Ext.BoxComponent#height height}: 500,
+    {@link Ext.Panel#title title}: 'Border Layout',
+    {@link Ext.Container#layout layout}: 'border',
+    {@link Ext.Container#items items}: [{
+        {@link Ext.Panel#title title}: 'South Region is resizable',
+        {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south',     // position for region
+        {@link Ext.BoxComponent#height height}: 100,
+        {@link Ext.layout.BorderLayout.Region#split split}: true,         // enable resizing
+        {@link Ext.SplitBar#minSize minSize}: 75,         // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50} 
+        {@link Ext.SplitBar#maxSize maxSize}: 150,
+        {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5'
+    },{
+        // xtype: 'panel' implied by default
+        {@link Ext.Panel#title title}: 'West Region is collapsible',
+        {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west',
+        {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5',
+        {@link Ext.BoxComponent#width width}: 200,
+        {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true,   // make collapsible
+        {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed
+        {@link Ext.Component#id id}: 'west-region-container',
+        {@link Ext.Container#layout layout}: 'fit',
+        {@link Ext.Panel#unstyled unstyled}: true
+    },{
+        {@link Ext.Panel#title title}: 'Center Region',
+        {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center',     // center region is required, no width/height specified
+        {@link Ext.Component#xtype xtype}: 'container',
+        {@link Ext.Container#layout layout}: 'fit',
+        {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0'
+    }]
+});
+
+ *

Notes:

+ */ +Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, { + // private + monitorResize:true, + // private + rendered : false, + + // private + onLayout : function(ct, target){ + var collapsed; + if(!this.rendered){ + target.addClass('x-border-layout-ct'); + var items = ct.items.items; + collapsed = []; + for(var i = 0, len = items.length; i < len; i++) { + var c = items[i]; + var pos = c.region; + if(c.collapsed){ + collapsed.push(c); + } + c.collapsed = false; + if(!c.rendered){ + c.cls = c.cls ? c.cls +' x-border-panel' : 'x-border-panel'; + c.render(target, i); + } + this[pos] = pos != 'center' && c.split ? + new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) : + new Ext.layout.BorderLayout.Region(this, c.initialConfig, pos); + this[pos].render(target, c); + } + this.rendered = true; + } + + var size = target.getViewSize(); + if(size.width < 20 || size.height < 20){ // display none? + if(collapsed){ + this.restoreCollapsed = collapsed; + } + return; + }else if(this.restoreCollapsed){ + collapsed = this.restoreCollapsed; + delete this.restoreCollapsed; + } + + var w = size.width, h = size.height; + var centerW = w, centerH = h, centerY = 0, centerX = 0; + + var n = this.north, s = this.south, west = this.west, e = this.east, c = this.center; + if(!c && Ext.layout.BorderLayout.WARN !== false){ + throw 'No center region defined in BorderLayout ' + ct.id; + } + + if(n && n.isVisible()){ + var b = n.getSize(); + var m = n.getMargins(); + b.width = w - (m.left+m.right); + b.x = m.left; + b.y = m.top; + centerY = b.height + b.y + m.bottom; + centerH -= centerY; + n.applyLayout(b); + } + if(s && s.isVisible()){ + var b = s.getSize(); + var m = s.getMargins(); + b.width = w - (m.left+m.right); + b.x = m.left; + var totalHeight = (b.height + m.top + m.bottom); + b.y = h - totalHeight + m.top; + centerH -= totalHeight; + s.applyLayout(b); + } + if(west && west.isVisible()){ + var b = west.getSize(); + var m = west.getMargins(); + b.height = centerH - (m.top+m.bottom); + b.x = m.left; + b.y = centerY + m.top; + var totalWidth = (b.width + m.left + m.right); + centerX += totalWidth; + centerW -= totalWidth; + west.applyLayout(b); + } + if(e && e.isVisible()){ + var b = e.getSize(); + var m = e.getMargins(); + b.height = centerH - (m.top+m.bottom); + var totalWidth = (b.width + m.left + m.right); + b.x = w - totalWidth + m.left; + b.y = centerY + m.top; + centerW -= totalWidth; + e.applyLayout(b); + } + if(c){ + var m = c.getMargins(); + var centerBox = { + x: centerX + m.left, + y: centerY + m.top, + width: centerW - (m.left+m.right), + height: centerH - (m.top+m.bottom) + }; + c.applyLayout(centerBox); + } + if(collapsed){ + for(var i = 0, len = collapsed.length; i < len; i++){ + collapsed[i].collapse(false); + } + } + if(Ext.isIE && Ext.isStrict){ // workaround IE strict repainting issue + target.repaint(); + } + }, + + destroy: function() { + var r = ['north', 'south', 'east', 'west']; + for (var i = 0; i < r.length; i++) { + var region = this[r[i]]; + if(region){ + if(region.destroy){ + region.destroy(); + }else if (region.split){ + region.split.destroy(true); + } + } + } + Ext.layout.BorderLayout.superclass.destroy.call(this); + } + + /** + * @property activeItem + * @hide + */ +}); + +/** + * @class Ext.layout.BorderLayout.Region + *

This is a region of a {@link Ext.layout.BorderLayout BorderLayout} that acts as a subcontainer + * within the layout. Each region has its own {@link Ext.layout.ContainerLayout layout} that is + * independent of other regions and the containing BorderLayout, and can be any of the + * {@link Ext.layout.ContainerLayout valid Ext layout types}.

+ *

Region size is managed automatically and cannot be changed by the user -- for + * {@link #split resizable regions}, see {@link Ext.layout.BorderLayout.SplitRegion}.

+ * @constructor + * Create a new Region. + * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region. + * @param {Object} config The configuration options + * @param {String} position The region position. Valid values are: north, south, + * east, west and center. Every {@link Ext.layout.BorderLayout BorderLayout} + * must have a center region for the primary content -- all other regions are optional. + */ +Ext.layout.BorderLayout.Region = function(layout, config, pos){ + Ext.apply(this, config); + this.layout = layout; + this.position = pos; + this.state = {}; + if(typeof this.margins == 'string'){ + this.margins = this.layout.parseMargins(this.margins); + } + this.margins = Ext.applyIf(this.margins || {}, this.defaultMargins); + if(this.collapsible){ + if(typeof this.cmargins == 'string'){ + this.cmargins = this.layout.parseMargins(this.cmargins); + } + if(this.collapseMode == 'mini' && !this.cmargins){ + this.cmargins = {left:0,top:0,right:0,bottom:0}; + }else{ + this.cmargins = Ext.applyIf(this.cmargins || {}, + pos == 'north' || pos == 'south' ? this.defaultNSCMargins : this.defaultEWCMargins); + } + } +}; + +Ext.layout.BorderLayout.Region.prototype = { + /** + * @cfg {Boolean} animFloat + * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated + * panel that will close again once the user mouses out of that panel (or clicks out if + * {@link #autoHide} = false). Setting {@link #animFloat} = false will + * prevent the open and close of these floated panels from being animated (defaults to true). + */ + /** + * @cfg {Boolean} autoHide + * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated + * panel. If autoHide = true, the panel will automatically hide after the user mouses + * out of the panel. If autoHide = false, the panel will continue to display until the + * user clicks outside of the panel (defaults to true). + */ + /** + * @cfg {String} collapseMode + * collapseMode supports two configuration values:

+ *

Note: if a collapsible region does not have a title bar, then set collapseMode = + * 'mini' and {@link #split} = true in order for the region to be {@link #collapsible} + * by the user as the expand/collapse tool button (that would go in the title bar) will not be rendered.

+ *

See also {@link #cmargins}.

+ */ + /** + * @cfg {Object} margins + * An object containing margins to apply to the region when in the expanded state in the + * format:

+{
+    top: (top margin),
+    right: (right margin),
+    bottom: (bottom margin),
+    left: (left margin)
+}
+ *

May also be a string containing space-separated, numeric margin values. The order of the + * sides associated with each value matches the way CSS processes margin values:

+ *

+ *

Defaults to:


+     * {top:0, right:0, bottom:0, left:0}
+     * 
+ */ + /** + * @cfg {Object} cmargins + * An object containing margins to apply to the region when in the collapsed state in the + * format:

+{
+    top: (top margin),
+    right: (right margin),
+    bottom: (bottom margin),
+    left: (left margin)
+}
+ *

May also be a string containing space-separated, numeric margin values. The order of the + * sides associated with each value matches the way CSS processes margin values.

+ *

+ */ + /** + * @cfg {Boolean} collapsible + *

true to allow the user to collapse this region (defaults to false). If + * true, an expand/collapse tool button will automatically be rendered into the title + * bar of the region, otherwise the button will not be shown.

+ *

Note: that a title bar is required to display the collapse/expand toggle button -- if + * no title is specified for the region's panel, the region will only be collapsible if + * {@link #collapseMode} = 'mini' and {@link #split} = true. + */ + collapsible : false, + /** + * @cfg {Boolean} split + *

true to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and + * display a 5px wide {@link Ext.SplitBar} between this region and its neighbor, allowing the user to + * resize the regions dynamically. Defaults to false creating a + * {@link Ext.layout.BorderLayout.Region Region}.


+ *

Notes:

+ */ + split:false, + /** + * @cfg {Boolean} floatable + * true to allow clicking a collapsed region's bar to display the region's panel floated + * above the layout, false to force the user to fully expand a collapsed region by + * clicking the expand button to see it again (defaults to true). + */ + floatable: true, + /** + * @cfg {Number} minWidth + *

The minimum allowable width in pixels for this region (defaults to 50). + * maxWidth may also be specified.


+ *

Note: setting the {@link Ext.SplitBar#minSize minSize} / + * {@link Ext.SplitBar#maxSize maxSize} supersedes any specified + * minWidth / maxWidth.

+ */ + minWidth:50, + /** + * @cfg {Number} minHeight + * The minimum allowable height in pixels for this region (defaults to 50) + * maxHeight may also be specified.


+ *

Note: setting the {@link Ext.SplitBar#minSize minSize} / + * {@link Ext.SplitBar#maxSize maxSize} supersedes any specified + * minHeight / maxHeight.

+ */ + minHeight:50, + + // private + defaultMargins : {left:0,top:0,right:0,bottom:0}, + // private + defaultNSCMargins : {left:5,top:5,right:5,bottom:5}, + // private + defaultEWCMargins : {left:5,top:0,right:5,bottom:0}, + floatingZIndex: 100, + + /** + * True if this region is collapsed. Read-only. + * @type Boolean + * @property + */ + isCollapsed : false, + + /** + * This region's panel. Read-only. + * @type Ext.Panel + * @property panel + */ + /** + * This region's layout. Read-only. + * @type Layout + * @property layout + */ + /** + * This region's layout position (north, south, east, west or center). Read-only. + * @type String + * @property position + */ + + // private + render : function(ct, p){ + this.panel = p; + p.el.enableDisplayMode(); + this.targetEl = ct; + this.el = p.el; + + var gs = p.getState, ps = this.position; + p.getState = function(){ + return Ext.apply(gs.call(p) || {}, this.state); + }.createDelegate(this); + + if(ps != 'center'){ + p.allowQueuedExpand = false; + p.on({ + beforecollapse: this.beforeCollapse, + collapse: this.onCollapse, + beforeexpand: this.beforeExpand, + expand: this.onExpand, + hide: this.onHide, + show: this.onShow, + scope: this + }); + if(this.collapsible || this.floatable){ + p.collapseEl = 'el'; + p.slideAnchor = this.getSlideAnchor(); + } + if(p.tools && p.tools.toggle){ + p.tools.toggle.addClass('x-tool-collapse-'+ps); + p.tools.toggle.addClassOnOver('x-tool-collapse-'+ps+'-over'); + } + } + }, + + // private + getCollapsedEl : function(){ + if(!this.collapsedEl){ + if(!this.toolTemplate){ + var tt = new Ext.Template( + '
 
' + ); + tt.disableFormats = true; + tt.compile(); + Ext.layout.BorderLayout.Region.prototype.toolTemplate = tt; + } + this.collapsedEl = this.targetEl.createChild({ + cls: "x-layout-collapsed x-layout-collapsed-"+this.position, + id: this.panel.id + '-xcollapsed' + }); + this.collapsedEl.enableDisplayMode('block'); + + if(this.collapseMode == 'mini'){ + this.collapsedEl.addClass('x-layout-cmini-'+this.position); + this.miniCollapsedEl = this.collapsedEl.createChild({ + cls: "x-layout-mini x-layout-mini-"+this.position, html: " " + }); + this.miniCollapsedEl.addClassOnOver('x-layout-mini-over'); + this.collapsedEl.addClassOnOver("x-layout-collapsed-over"); + this.collapsedEl.on('click', this.onExpandClick, this, {stopEvent:true}); + }else { + if(this.collapsible !== false && !this.hideCollapseTool) { + var t = this.toolTemplate.append( + this.collapsedEl.dom, + {id:'expand-'+this.position}, true); + t.addClassOnOver('x-tool-expand-'+this.position+'-over'); + t.on('click', this.onExpandClick, this, {stopEvent:true}); + } + if(this.floatable !== false || this.titleCollapse){ + this.collapsedEl.addClassOnOver("x-layout-collapsed-over"); + this.collapsedEl.on("click", this[this.floatable ? 'collapseClick' : 'onExpandClick'], this); + } + } + } + return this.collapsedEl; + }, + + // private + onExpandClick : function(e){ + if(this.isSlid){ + this.afterSlideIn(); + this.panel.expand(false); + }else{ + this.panel.expand(); + } + }, + + // private + onCollapseClick : function(e){ + this.panel.collapse(); + }, + + // private + beforeCollapse : function(p, animate){ + this.lastAnim = animate; + if(this.splitEl){ + this.splitEl.hide(); + } + this.getCollapsedEl().show(); + this.panel.el.setStyle('z-index', 100); + this.isCollapsed = true; + this.layout.layout(); + }, + + // private + onCollapse : function(animate){ + this.panel.el.setStyle('z-index', 1); + if(this.lastAnim === false || this.panel.animCollapse === false){ + this.getCollapsedEl().dom.style.visibility = 'visible'; + }else{ + this.getCollapsedEl().slideIn(this.panel.slideAnchor, {duration:.2}); + } + this.state.collapsed = true; + this.panel.saveState(); + }, + + // private + beforeExpand : function(animate){ + var c = this.getCollapsedEl(); + this.el.show(); + if(this.position == 'east' || this.position == 'west'){ + this.panel.setSize(undefined, c.getHeight()); + }else{ + this.panel.setSize(c.getWidth(), undefined); + } + c.hide(); + c.dom.style.visibility = 'hidden'; + this.panel.el.setStyle('z-index', this.floatingZIndex); + }, + + // private + onExpand : function(){ + this.isCollapsed = false; + if(this.splitEl){ + this.splitEl.show(); + } + this.layout.layout(); + this.panel.el.setStyle('z-index', 1); + this.state.collapsed = false; + this.panel.saveState(); + }, + + // private + collapseClick : function(e){ + if(this.isSlid){ + e.stopPropagation(); + this.slideIn(); + }else{ + e.stopPropagation(); + this.slideOut(); + } + }, + + // private + onHide : function(){ + if(this.isCollapsed){ + this.getCollapsedEl().hide(); + }else if(this.splitEl){ + this.splitEl.hide(); + } + }, + + // private + onShow : function(){ + if(this.isCollapsed){ + this.getCollapsedEl().show(); + }else if(this.splitEl){ + this.splitEl.show(); + } + }, + + /** + * True if this region is currently visible, else false. + * @return {Boolean} + */ + isVisible : function(){ + return !this.panel.hidden; + }, + + /** + * Returns the current margins for this region. If the region is collapsed, the + * {@link #cmargins} (collapsed margins) value will be returned, otherwise the + * {@link #margins} value will be returned. + * @return {Object} An object containing the element's margins: {left: (left + * margin), top: (top margin), right: (right margin), bottom: (bottom margin)} + */ + getMargins : function(){ + return this.isCollapsed && this.cmargins ? this.cmargins : this.margins; + }, + + /** + * Returns the current size of this region. If the region is collapsed, the size of the + * collapsedEl will be returned, otherwise the size of the region's panel will be returned. + * @return {Object} An object containing the element's size: {width: (element width), + * height: (element height)} + */ + getSize : function(){ + return this.isCollapsed ? this.getCollapsedEl().getSize() : this.panel.getSize(); + }, + + /** + * Sets the specified panel as the container element for this region. + * @param {Ext.Panel} panel The new panel + */ + setPanel : function(panel){ + this.panel = panel; + }, + + /** + * Returns the minimum allowable width for this region. + * @return {Number} The minimum width + */ + getMinWidth: function(){ + return this.minWidth; + }, + + /** + * Returns the minimum allowable height for this region. + * @return {Number} The minimum height + */ + getMinHeight: function(){ + return this.minHeight; + }, + + // private + applyLayoutCollapsed : function(box){ + var ce = this.getCollapsedEl(); + ce.setLeftTop(box.x, box.y); + ce.setSize(box.width, box.height); + }, + + // private + applyLayout : function(box){ + if(this.isCollapsed){ + this.applyLayoutCollapsed(box); + }else{ + this.panel.setPosition(box.x, box.y); + this.panel.setSize(box.width, box.height); + } + }, + + // private + beforeSlide: function(){ + this.panel.beforeEffect(); + }, + + // private + afterSlide : function(){ + this.panel.afterEffect(); + }, + + // private + initAutoHide : function(){ + if(this.autoHide !== false){ + if(!this.autoHideHd){ + var st = new Ext.util.DelayedTask(this.slideIn, this); + this.autoHideHd = { + "mouseout": function(e){ + if(!e.within(this.el, true)){ + st.delay(500); + } + }, + "mouseover" : function(e){ + st.cancel(); + }, + scope : this + }; + } + this.el.on(this.autoHideHd); + } + }, + + // private + clearAutoHide : function(){ + if(this.autoHide !== false){ + this.el.un("mouseout", this.autoHideHd.mouseout); + this.el.un("mouseover", this.autoHideHd.mouseover); + } + }, + + // private + clearMonitor : function(){ + Ext.getDoc().un("click", this.slideInIf, this); + }, + + /** + * If this Region is {@link #floatable}, this method slides this Region into full visibility over the top + * of the center Region where it floats until either {@link #slideIn} is called, or other regions of the layout + * are clicked, or the mouse exits the Region. + */ + slideOut : function(){ + if(this.isSlid || this.el.hasActiveFx()){ + return; + } + this.isSlid = true; + var ts = this.panel.tools; + if(ts && ts.toggle){ + ts.toggle.hide(); + } + this.el.show(); + if(this.position == 'east' || this.position == 'west'){ + this.panel.setSize(undefined, this.collapsedEl.getHeight()); + }else{ + this.panel.setSize(this.collapsedEl.getWidth(), undefined); + } + this.restoreLT = [this.el.dom.style.left, this.el.dom.style.top]; + this.el.alignTo(this.collapsedEl, this.getCollapseAnchor()); + this.el.setStyle("z-index", this.floatingZIndex+2); + this.panel.el.replaceClass('x-panel-collapsed', 'x-panel-floating'); + if(this.animFloat !== false){ + this.beforeSlide(); + this.el.slideIn(this.getSlideAnchor(), { + callback: function(){ + this.afterSlide(); + this.initAutoHide(); + Ext.getDoc().on("click", this.slideInIf, this); + }, + scope: this, + block: true + }); + }else{ + this.initAutoHide(); + Ext.getDoc().on("click", this.slideInIf, this); + } + }, + + // private + afterSlideIn : function(){ + this.clearAutoHide(); + this.isSlid = false; + this.clearMonitor(); + this.el.setStyle("z-index", ""); + this.panel.el.replaceClass('x-panel-floating', 'x-panel-collapsed'); + this.el.dom.style.left = this.restoreLT[0]; + this.el.dom.style.top = this.restoreLT[1]; + + var ts = this.panel.tools; + if(ts && ts.toggle){ + ts.toggle.show(); + } + }, + + /** + * If this Region is {@link #floatable}, and this Region has been slid into floating visibility, then this method slides + * this region back into its collapsed state. + */ + slideIn : function(cb){ + if(!this.isSlid || this.el.hasActiveFx()){ + Ext.callback(cb); + return; + } + this.isSlid = false; + if(this.animFloat !== false){ + this.beforeSlide(); + this.el.slideOut(this.getSlideAnchor(), { + callback: function(){ + this.el.hide(); + this.afterSlide(); + this.afterSlideIn(); + Ext.callback(cb); + }, + scope: this, + block: true + }); + }else{ + this.el.hide(); + this.afterSlideIn(); + } + }, + + // private + slideInIf : function(e){ + if(!e.within(this.el)){ + this.slideIn(); + } + }, + + // private + anchors : { + "west" : "left", + "east" : "right", + "north" : "top", + "south" : "bottom" + }, + + // private + sanchors : { + "west" : "l", + "east" : "r", + "north" : "t", + "south" : "b" + }, + + // private + canchors : { + "west" : "tl-tr", + "east" : "tr-tl", + "north" : "tl-bl", + "south" : "bl-tl" + }, + + // private + getAnchor : function(){ + return this.anchors[this.position]; + }, + + // private + getCollapseAnchor : function(){ + return this.canchors[this.position]; + }, + + // private + getSlideAnchor : function(){ + return this.sanchors[this.position]; + }, + + // private + getAlignAdj : function(){ + var cm = this.cmargins; + switch(this.position){ + case "west": + return [0, 0]; + break; + case "east": + return [0, 0]; + break; + case "north": + return [0, 0]; + break; + case "south": + return [0, 0]; + break; + } + }, + + // private + getExpandAdj : function(){ + var c = this.collapsedEl, cm = this.cmargins; + switch(this.position){ + case "west": + return [-(cm.right+c.getWidth()+cm.left), 0]; + break; + case "east": + return [cm.right+c.getWidth()+cm.left, 0]; + break; + case "north": + return [0, -(cm.top+cm.bottom+c.getHeight())]; + break; + case "south": + return [0, cm.top+cm.bottom+c.getHeight()]; + break; + } + } +}; + +/** + * @class Ext.layout.BorderLayout.SplitRegion + * @extends Ext.layout.BorderLayout.Region + *

This is a specialized type of {@link Ext.layout.BorderLayout.Region BorderLayout region} that + * has a built-in {@link Ext.SplitBar} for user resizing of regions. The movement of the split bar + * is configurable to move either {@link #tickSize smooth or incrementally}.

+ * @constructor + * Create a new SplitRegion. + * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region. + * @param {Object} config The configuration options + * @param {String} position The region position. Valid values are: north, south, east, west and center. Every + * BorderLayout must have a center region for the primary content -- all other regions are optional. + */ +Ext.layout.BorderLayout.SplitRegion = function(layout, config, pos){ + Ext.layout.BorderLayout.SplitRegion.superclass.constructor.call(this, layout, config, pos); + // prevent switch + this.applyLayout = this.applyFns[pos]; +}; + +Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, { + /** + * @cfg {Number} tickSize + * The increment, in pixels by which to move this Region's {@link Ext.SplitBar SplitBar}. + * By default, the {@link Ext.SplitBar SplitBar} moves smoothly. + */ + /** + * @cfg {String} splitTip + * The tooltip to display when the user hovers over a + * {@link Ext.layout.BorderLayout.Region#collapsible non-collapsible} region's split bar + * (defaults to "Drag to resize."). Only applies if + * {@link #useSplitTips} = true. + */ + splitTip : "Drag to resize.", + /** + * @cfg {String} collapsibleSplitTip + * The tooltip to display when the user hovers over a + * {@link Ext.layout.BorderLayout.Region#collapsible collapsible} region's split bar + * (defaults to "Drag to resize. Double click to hide."). Only applies if + * {@link #useSplitTips} = true. + */ + collapsibleSplitTip : "Drag to resize. Double click to hide.", + /** + * @cfg {Boolean} useSplitTips + * true to display a tooltip when the user hovers over a region's split bar + * (defaults to false). The tooltip text will be the value of either + * {@link #splitTip} or {@link #collapsibleSplitTip} as appropriate. + */ + useSplitTips : false, + + // private + splitSettings : { + north : { + orientation: Ext.SplitBar.VERTICAL, + placement: Ext.SplitBar.TOP, + maxFn : 'getVMaxSize', + minProp: 'minHeight', + maxProp: 'maxHeight' + }, + south : { + orientation: Ext.SplitBar.VERTICAL, + placement: Ext.SplitBar.BOTTOM, + maxFn : 'getVMaxSize', + minProp: 'minHeight', + maxProp: 'maxHeight' + }, + east : { + orientation: Ext.SplitBar.HORIZONTAL, + placement: Ext.SplitBar.RIGHT, + maxFn : 'getHMaxSize', + minProp: 'minWidth', + maxProp: 'maxWidth' + }, + west : { + orientation: Ext.SplitBar.HORIZONTAL, + placement: Ext.SplitBar.LEFT, + maxFn : 'getHMaxSize', + minProp: 'minWidth', + maxProp: 'maxWidth' + } + }, + + // private + applyFns : { + west : function(box){ + if(this.isCollapsed){ + return this.applyLayoutCollapsed(box); + } + var sd = this.splitEl.dom, s = sd.style; + this.panel.setPosition(box.x, box.y); + var sw = sd.offsetWidth; + s.left = (box.x+box.width-sw)+'px'; + s.top = (box.y)+'px'; + s.height = Math.max(0, box.height)+'px'; + this.panel.setSize(box.width-sw, box.height); + }, + east : function(box){ + if(this.isCollapsed){ + return this.applyLayoutCollapsed(box); + } + var sd = this.splitEl.dom, s = sd.style; + var sw = sd.offsetWidth; + this.panel.setPosition(box.x+sw, box.y); + s.left = (box.x)+'px'; + s.top = (box.y)+'px'; + s.height = Math.max(0, box.height)+'px'; + this.panel.setSize(box.width-sw, box.height); + }, + north : function(box){ + if(this.isCollapsed){ + return this.applyLayoutCollapsed(box); + } + var sd = this.splitEl.dom, s = sd.style; + var sh = sd.offsetHeight; + this.panel.setPosition(box.x, box.y); + s.left = (box.x)+'px'; + s.top = (box.y+box.height-sh)+'px'; + s.width = Math.max(0, box.width)+'px'; + this.panel.setSize(box.width, box.height-sh); + }, + south : function(box){ + if(this.isCollapsed){ + return this.applyLayoutCollapsed(box); + } + var sd = this.splitEl.dom, s = sd.style; + var sh = sd.offsetHeight; + this.panel.setPosition(box.x, box.y+sh); + s.left = (box.x)+'px'; + s.top = (box.y)+'px'; + s.width = Math.max(0, box.width)+'px'; + this.panel.setSize(box.width, box.height-sh); + } + }, + + // private + render : function(ct, p){ + Ext.layout.BorderLayout.SplitRegion.superclass.render.call(this, ct, p); + + var ps = this.position; + + this.splitEl = ct.createChild({ + cls: "x-layout-split x-layout-split-"+ps, html: " ", + id: this.panel.id + '-xsplit' + }); + + if(this.collapseMode == 'mini'){ + this.miniSplitEl = this.splitEl.createChild({ + cls: "x-layout-mini x-layout-mini-"+ps, html: " " + }); + this.miniSplitEl.addClassOnOver('x-layout-mini-over'); + this.miniSplitEl.on('click', this.onCollapseClick, this, {stopEvent:true}); + } + + var s = this.splitSettings[ps]; + + this.split = new Ext.SplitBar(this.splitEl.dom, p.el, s.orientation); + this.split.tickSize = this.tickSize; + this.split.placement = s.placement; + this.split.getMaximumSize = this[s.maxFn].createDelegate(this); + this.split.minSize = this.minSize || this[s.minProp]; + this.split.on("beforeapply", this.onSplitMove, this); + this.split.useShim = this.useShim === true; + this.maxSize = this.maxSize || this[s.maxProp]; + + if(p.hidden){ + this.splitEl.hide(); + } + + if(this.useSplitTips){ + this.splitEl.dom.title = this.collapsible ? this.collapsibleSplitTip : this.splitTip; + } + if(this.collapsible){ + this.splitEl.on("dblclick", this.onCollapseClick, this); + } + }, + + //docs inherit from superclass + getSize : function(){ + if(this.isCollapsed){ + return this.collapsedEl.getSize(); + } + var s = this.panel.getSize(); + if(this.position == 'north' || this.position == 'south'){ + s.height += this.splitEl.dom.offsetHeight; + }else{ + s.width += this.splitEl.dom.offsetWidth; + } + return s; + }, + + // private + getHMaxSize : function(){ + var cmax = this.maxSize || 10000; + var center = this.layout.center; + return Math.min(cmax, (this.el.getWidth()+center.el.getWidth())-center.getMinWidth()); + }, + + // private + getVMaxSize : function(){ + var cmax = this.maxSize || 10000; + var center = this.layout.center; + return Math.min(cmax, (this.el.getHeight()+center.el.getHeight())-center.getMinHeight()); + }, + + // private + onSplitMove : function(split, newSize){ + var s = this.panel.getSize(); + this.lastSplitSize = newSize; + if(this.position == 'north' || this.position == 'south'){ + this.panel.setSize(s.width, newSize); + this.state.height = newSize; + }else{ + this.panel.setSize(newSize, s.height); + this.state.width = newSize; + } + this.layout.layout(); + this.panel.saveState(); + return false; + }, + + /** + * Returns a reference to the split bar in use by this region. + * @return {Ext.SplitBar} The split bar + */ + getSplitBar : function(){ + return this.split; + }, + + // inherit docs + destroy : function() { + Ext.destroy( + this.miniSplitEl, + this.split, + this.splitEl + ); + } +}); + +Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;/** + * @class Ext.layout.FormLayout + * @extends Ext.layout.AnchorLayout + *

This layout manager is specifically designed for rendering and managing child Components of + * {@link Ext.form.FormPanel forms}. It is responsible for rendering the labels of + * {@link Ext.form.Field Field}s.

+ * + *

This layout manager is used when a Container is configured with the layout:'form' + * {@link Ext.Container#layout layout} config option, and should generally not need to be created directly + * via the new keyword. See {@link Ext.Container#layout} for additional details.

+ * + *

In an application, it will usually be preferrable to use a {@link Ext.form.FormPanel FormPanel} + * (which is configured with FormLayout as its layout class by default) since it also provides built-in + * functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form.

+ * + *

A {@link Ext.Container Container} using the FormLayout layout manager (e.g. + * {@link Ext.form.FormPanel} or specifying layout:'form') can also accept the following + * layout-specific config properties:

+ * + *

Any Component (including Fields) managed by FormLayout accepts the following as a config option: + *

+ * + *

Any Component managed by FormLayout may be rendered as a form field (with an associated label) by + * configuring it with a non-null {@link Ext.Component#fieldLabel fieldLabel}. Components configured + * in this way may be configured with the following options which affect the way the FormLayout renders them: + *

+ * + *

Example usage:

+ *

+// Required if showing validation messages
+Ext.QuickTips.init();
+
+// While you can create a basic Panel with layout:'form', practically
+// you should usually use a FormPanel to also get its form functionality
+// since it already creates a FormLayout internally.
+var form = new Ext.form.FormPanel({
+    title: 'Form Layout',
+    bodyStyle: 'padding:15px',
+    width: 350,
+    defaultType: 'textfield',
+    defaults: {
+        // applied to each contained item
+        width: 230,
+        msgTarget: 'side'
+    },
+    items: [{
+            fieldLabel: 'First Name',
+            name: 'first',
+            allowBlank: false,
+            {@link Ext.Component#labelSeparator labelSeparator}: ':' // override labelSeparator layout config
+        },{
+            fieldLabel: 'Last Name',
+            name: 'last'
+        },{
+            fieldLabel: 'Email',
+            name: 'email',
+            vtype:'email'
+        }, {
+            xtype: 'textarea',
+            hideLabel: true,     // override hideLabels layout config
+            name: 'msg',
+            anchor: '100% -53'
+        }
+    ],
+    buttons: [
+        {text: 'Save'},
+        {text: 'Cancel'}
+    ],
+    layoutConfig: {
+        {@link #labelSeparator}: '~' // superseded by assignment below
+    },
+    // config options applicable to container when layout='form':
+    hideLabels: false,
+    labelAlign: 'left',   // or 'right' or 'top'
+    {@link Ext.form.FormPanel#labelSeparator labelSeparator}: '>>', // takes precedence over layoutConfig value
+    labelWidth: 65,       // defaults to 100
+    labelPad: 8           // defaults to 5, must specify labelWidth to be honored
+});
+
+ */ +Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, { + + /** + * @cfg {String} labelSeparator + * See {@link Ext.form.FormPanel}.{@link Ext.form.FormPanel#labelSeparator labelSeparator}. Configuration + * of this property at the container level takes precedence. + */ + labelSeparator : ':', + + /** + * Read only. The CSS style specification string added to field labels in this layout if not + * otherwise {@link Ext.Component#labelStyle specified by each contained field}. + * @type String + * @property labelStyle + */ + + // private + setContainer : function(ct){ + Ext.layout.FormLayout.superclass.setContainer.call(this, ct); + if(ct.labelAlign){ + ct.addClass('x-form-label-'+ct.labelAlign); + } + + if(ct.hideLabels){ + this.labelStyle = "display:none"; + this.elementStyle = "padding-left:0;"; + this.labelAdjust = 0; + }else{ + this.labelSeparator = ct.labelSeparator || this.labelSeparator; + ct.labelWidth = ct.labelWidth || 100; + if(typeof ct.labelWidth == 'number'){ + var pad = (typeof ct.labelPad == 'number' ? ct.labelPad : 5); + this.labelAdjust = ct.labelWidth+pad; + this.labelStyle = "width:"+ct.labelWidth+"px;"; + this.elementStyle = "padding-left:"+(ct.labelWidth+pad)+'px'; + } + if(ct.labelAlign == 'top'){ + this.labelStyle = "width:auto;"; + this.labelAdjust = 0; + this.elementStyle = "padding-left:0;"; + } + } + }, + + //private + getLabelStyle: function(s){ + var ls = '', items = [this.labelStyle, s]; + for (var i = 0, len = items.length; i < len; ++i){ + if (items[i]){ + ls += items[i]; + if (ls.substr(-1, 1) != ';'){ + ls += ';' + } + } + } + return ls; + }, + + /** + * @cfg {Ext.Template} fieldTpl + * A {@link Ext.Template#compile compile}d {@link Ext.Template} for rendering + * the fully wrapped, labeled and styled form Field. Defaults to:


+new Ext.Template(
+    '<div class="x-form-item {itemCls}" tabIndex="-1">',
+        '<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>',
+        '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
+        '</div><div class="{clearCls}"></div>',
+    '</div>'
+);
+
+ *

This may be specified to produce a different DOM structure when rendering form Fields.

+ *

A description of the properties within the template follows:

+ *

Also see {@link #getTemplateArgs}

+ */ + + // private + renderItem : function(c, position, target){ + if(c && !c.rendered && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){ + var args = this.getTemplateArgs(c); + if(typeof position == 'number'){ + position = target.dom.childNodes[position] || null; + } + if(position){ + this.fieldTpl.insertBefore(position, args); + }else{ + this.fieldTpl.append(target, args); + } + c.render('x-form-el-'+c.id); + }else { + Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments); + } + }, + + /** + *

Provides template arguments for rendering the fully wrapped, labeled and styled form Field.

+ *

This method returns an object hash containing properties used by the layout's {@link #fieldTpl} + * to create a correctly wrapped, labeled and styled form Field. This may be overriden to + * create custom layouts. The properties which must be returned are:

+ * @param field The {@link Field Ext.form.Field} being rendered. + * @return An object hash containing the properties required to render the Field. + */ + getTemplateArgs: function(field) { + var noLabelSep = !field.fieldLabel || field.hideLabel; + return { + id: field.id, + label: field.fieldLabel, + labelStyle: field.labelStyle||this.labelStyle||'', + elementStyle: this.elementStyle||'', + labelSeparator: noLabelSep ? '' : (typeof field.labelSeparator == 'undefined' ? this.labelSeparator : field.labelSeparator), + itemCls: (field.itemCls||this.container.itemCls||'') + (field.hideLabel ? ' x-hide-label' : ''), + clearCls: field.clearCls || 'x-form-clear-left' + }; + }, + + // private + adjustWidthAnchor : function(value, comp){ + return value - (comp.isFormField || comp.fieldLabel ? (comp.hideLabel ? 0 : this.labelAdjust) : 0); + }, + + // private + isValidParent : function(c, target){ + return true; + } + + /** + * @property activeItem + * @hide + */ +}); + +Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout;/** + * @class Ext.layout.AccordionLayout + * @extends Ext.layout.FitLayout + *

This is a layout that contains multiple panels in an expandable accordion style such that only + * one panel can be open at any given time. Each panel has built-in support for expanding and collapsing. + *

This class is intended to be extended or created via the {@link Ext.Container#layout layout} + * configuration property. See {@link Ext.Container#layout} for additional details.

+ *

Example usage:

+ *

+var accordion = new Ext.Panel({
+    title: 'Accordion Layout',
+    layout:'accordion',
+    defaults: {
+        // applied to each contained panel
+        bodyStyle: 'padding:15px'
+    },
+    layoutConfig: {
+        // layout-specific configs go here
+        titleCollapse: false,
+        animate: true,
+        activeOnTop: true
+    },
+    items: [{
+        title: 'Panel 1',
+        html: '<p>Panel content!</p>'
+    },{
+        title: 'Panel 2',
+        html: '<p>Panel content!</p>'
+    },{
+        title: 'Panel 3',
+        html: '<p>Panel content!</p>'
+    }]
+});
+
+ */ +Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, { + /** + * @cfg {Boolean} fill + * True to adjust the active item's height to fill the available space in the container, false to use the + * item's current height, or auto height if not explicitly set (defaults to true). + */ + fill : true, + /** + * @cfg {Boolean} autoWidth + * True to set each contained item's width to 'auto', false to use the item's current width (defaults to true). + * Note that some components, in particular the {@link Ext.grid.GridPanel grid}, will not function properly within + * layouts if they have auto width, so in such cases this config should be set to false. + */ + autoWidth : true, + /** + * @cfg {Boolean} titleCollapse + * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow + * expand/collapse only when the toggle tool button is clicked (defaults to true). When set to false, + * {@link #hideCollapseTool} should be false also. + */ + titleCollapse : true, + /** + * @cfg {Boolean} hideCollapseTool + * True to hide the contained panels' collapse/expand toggle buttons, false to display them (defaults to false). + * When set to true, {@link #titleCollapse} should be true also. + */ + hideCollapseTool : false, + /** + * @cfg {Boolean} collapseFirst + * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools + * in the contained panels' title bars, false to render it last (defaults to false). + */ + collapseFirst : false, + /** + * @cfg {Boolean} animate + * True to slide the contained panels open and closed during expand/collapse using animation, false to open and + * close directly with no animation (defaults to false). Note: to defer to the specific config setting of each + * contained panel for this property, set this to undefined at the layout level. + */ + animate : false, + /** + * @cfg {Boolean} sequence + * Experimental. If animate is set to true, this will result in each animation running in sequence. + */ + sequence : false, + /** + * @cfg {Boolean} activeOnTop + * True to swap the position of each panel as it is expanded so that it becomes the first item in the container, + * false to keep the panels in the rendered order. This is NOT compatible with "animate:true" (defaults to false). + */ + activeOnTop : false, + + renderItem : function(c){ + if(this.animate === false){ + c.animCollapse = false; + } + c.collapsible = true; + if(this.autoWidth){ + c.autoWidth = true; + } + if(this.titleCollapse){ + c.titleCollapse = true; + } + if(this.hideCollapseTool){ + c.hideCollapseTool = true; + } + if(this.collapseFirst !== undefined){ + c.collapseFirst = this.collapseFirst; + } + if(!this.activeItem && !c.collapsed){ + this.activeItem = c; + }else if(this.activeItem && this.activeItem != c){ + c.collapsed = true; + } + Ext.layout.AccordionLayout.superclass.renderItem.apply(this, arguments); + c.header.addClass('x-accordion-hd'); + c.on('beforeexpand', this.beforeExpand, this); + }, + + // private + beforeExpand : function(p, anim){ + var ai = this.activeItem; + if(ai){ + if(this.sequence){ + delete this.activeItem; + if (!ai.collapsed){ + ai.collapse({callback:function(){ + p.expand(anim || true); + }, scope: this}); + return false; + } + }else{ + ai.collapse(this.animate); + } + } + this.activeItem = p; + if(this.activeOnTop){ + p.el.dom.parentNode.insertBefore(p.el.dom, p.el.dom.parentNode.firstChild); + } + this.layout(); + }, + + // private + setItemSize : function(item, size){ + if(this.fill && item){ + var hh = 0; + this.container.items.each(function(p){ + if(p != item){ + hh += p.header.getHeight(); + } + }); + size.height -= hh; + item.setSize(size); + } + }, + + /** + * Sets the active (expanded) item in the layout. + * @param {String/Number} item The string component id or numeric index of the item to activate + */ + setActiveItem : function(item){ + item = this.container.getComponent(item); + if(this.activeItem != item){ + if(item.rendered && item.collapsed){ + item.expand(); + }else{ + this.activeItem = item; + } + } + + } +}); +Ext.Container.LAYOUTS.accordion = Ext.layout.AccordionLayout; + +//backwards compat +Ext.layout.Accordion = Ext.layout.AccordionLayout;/** + * @class Ext.layout.TableLayout + * @extends Ext.layout.ContainerLayout + *

This layout allows you to easily render content into an HTML table. The total number of columns can be + * specified, and rowspan and colspan can be used to create complex layouts within the table. + * This class is intended to be extended or created via the layout:'table' {@link Ext.Container#layout} config, + * and should generally not need to be created directly via the new keyword.

+ *

Note that when creating a layout via config, the layout-specific config properties must be passed in via + * the {@link Ext.Container#layoutConfig} object which will then be applied internally to the layout. In the + * case of TableLayout, the only valid layout config property is {@link #columns}. However, the items added to a + * TableLayout can supply the following table-specific config properties:

+ * + *

The basic concept of building up a TableLayout is conceptually very similar to building up a standard + * HTML table. You simply add each panel (or "cell") that you want to include along with any span attributes + * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts. + * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the + * total column count in the layoutConfig and start adding panels in their natural order from left to right, + * top to bottom. The layout will automatically figure out, based on the column count, rowspans and colspans, + * how to position each panel within the table. Just like with HTML tables, your rowspans and colspans must add + * up correctly in your overall layout or you'll end up with missing and/or extra cells! Example usage:

+ *

+// This code will generate a layout table that is 3 columns by 2 rows
+// with some spanning included.  The basic layout will be:
+// +--------+-----------------+
+// |   A    |   B             |
+// |        |--------+--------|
+// |        |   C    |   D    |
+// +--------+--------+--------+
+var table = new Ext.Panel({
+    title: 'Table Layout',
+    layout:'table',
+    defaults: {
+        // applied to each contained panel
+        bodyStyle:'padding:20px'
+    },
+    layoutConfig: {
+        // The total column count must be specified here
+        columns: 3
+    },
+    items: [{
+        html: '<p>Cell A content</p>',
+        rowspan: 2
+    },{
+        html: '<p>Cell B content</p>',
+        colspan: 2
+    },{
+        html: '<p>Cell C content</p>',
+        cellCls: 'highlight'
+    },{
+        html: '<p>Cell D content</p>'
+    }]
+});
+
+ */ +Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, { + /** + * @cfg {Number} columns + * The total number of columns to create in the table for this layout. If not specified, all Components added to + * this layout will be rendered into a single row using one column per Component. + */ + + // private + monitorResize:false, + + /** + * @cfg {Object} tableAttrs + *

An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification + * used to create the layout's <table> element. Example:


+{
+    xtype: 'panel',
+    layout: 'table',
+    layoutConfig: {
+        tableAttrs: {
+        	style: {
+        		width: '100%'
+        	}
+        },
+        columns: 3
+    }
+}
+ */ + tableAttrs:null, + + // private + setContainer : function(ct){ + Ext.layout.TableLayout.superclass.setContainer.call(this, ct); + + this.currentRow = 0; + this.currentColumn = 0; + this.cells = []; + }, + + // private + onLayout : function(ct, target){ + var cs = ct.items.items, len = cs.length, c, i; + + if(!this.table){ + target.addClass('x-table-layout-ct'); + + this.table = target.createChild( + Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true); + } + this.renderAll(ct, target); + }, + + // private + getRow : function(index){ + var row = this.table.tBodies[0].childNodes[index]; + if(!row){ + row = document.createElement('tr'); + this.table.tBodies[0].appendChild(row); + } + return row; + }, + + // private + getNextCell : function(c){ + var cell = this.getNextNonSpan(this.currentColumn, this.currentRow); + var curCol = this.currentColumn = cell[0], curRow = this.currentRow = cell[1]; + for(var rowIndex = curRow; rowIndex < curRow + (c.rowspan || 1); rowIndex++){ + if(!this.cells[rowIndex]){ + this.cells[rowIndex] = []; + } + for(var colIndex = curCol; colIndex < curCol + (c.colspan || 1); colIndex++){ + this.cells[rowIndex][colIndex] = true; + } + } + var td = document.createElement('td'); + if(c.cellId){ + td.id = c.cellId; + } + var cls = 'x-table-layout-cell'; + if(c.cellCls){ + cls += ' ' + c.cellCls; + } + td.className = cls; + if(c.colspan){ + td.colSpan = c.colspan; + } + if(c.rowspan){ + td.rowSpan = c.rowspan; + } + this.getRow(curRow).appendChild(td); + return td; + }, + + // private + getNextNonSpan: function(colIndex, rowIndex){ + var cols = this.columns; + while((cols && colIndex >= cols) || (this.cells[rowIndex] && this.cells[rowIndex][colIndex])) { + if(cols && colIndex >= cols){ + rowIndex++; + colIndex = 0; + }else{ + colIndex++; + } + } + return [colIndex, rowIndex]; + }, + + // private + renderItem : function(c, position, target){ + if(c && !c.rendered){ + c.render(this.getNextCell(c)); + if(this.extraCls){ + var t = c.getPositionEl ? c.getPositionEl() : c; + t.addClass(this.extraCls); + } + } + }, + + // private + isValidParent : function(c, target){ + return true; + } + + /** + * @property activeItem + * @hide + */ +}); + +Ext.Container.LAYOUTS['table'] = Ext.layout.TableLayout;/** + * @class Ext.layout.AbsoluteLayout + * @extends Ext.layout.AnchorLayout + *

This is a layout that inherits the anchoring of {@link Ext.layout.AnchorLayout} and adds the + * ability for x/y positioning using the standard x and y component config options.

+ *

This class is intended to be extended or created via the {@link Ext.Container#layout layout} + * configuration property. See {@link Ext.Container#layout} for additional details.

+ *

Example usage:

+ *

+var form = new Ext.form.FormPanel({
+    title: 'Absolute Layout',
+    layout:'absolute',
+    layoutConfig: {
+        // layout-specific configs go here
+        extraCls: 'x-abs-layout-item',
+    },
+    baseCls: 'x-plain',
+    url:'save-form.php',
+    defaultType: 'textfield',
+    items: [{
+        x: 0,
+        y: 5,
+        xtype:'label',
+        text: 'Send To:'
+    },{
+        x: 60,
+        y: 0,
+        name: 'to',
+        anchor:'100%'  // anchor width by percentage
+    },{
+        x: 0,
+        y: 35,
+        xtype:'label',
+        text: 'Subject:'
+    },{
+        x: 60,
+        y: 30,
+        name: 'subject',
+        anchor: '100%'  // anchor width by percentage
+    },{
+        x:0,
+        y: 60,
+        xtype: 'textarea',
+        name: 'msg',
+        anchor: '100% 100%'  // anchor width and height
+    }]
+});
+
+ */ +Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, { + + extraCls: 'x-abs-layout-item', + + onLayout : function(ct, target){ + target.position(); + this.paddingLeft = target.getPadding('l'); + this.paddingTop = target.getPadding('t'); + + Ext.layout.AbsoluteLayout.superclass.onLayout.call(this, ct, target); + }, + + // private + adjustWidthAnchor : function(value, comp){ + return value ? value - comp.getPosition(true)[0] + this.paddingLeft : value; + }, + + // private + adjustHeightAnchor : function(value, comp){ + return value ? value - comp.getPosition(true)[1] + this.paddingTop : value; + } + /** + * @property activeItem + * @hide + */ +}); +Ext.Container.LAYOUTS['absolute'] = Ext.layout.AbsoluteLayout; +/** + * @class Ext.layout.BoxLayout + * @extends Ext.layout.ContainerLayout + *

Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.

+ */ +Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, { + /** + * @cfg {Object} defaultMargins + *

If the individual contained items do not have a margins + * property specified, the default margins from this property will be + * applied to each item.

+ *

This property may be specified as an object containing margins + * to apply in the format:


+{
+    top: (top margin),
+    right: (right margin),
+    bottom: (bottom margin),
+    left: (left margin)
+}
+ *

This property may also be specified as a string containing + * space-separated, numeric margin values. The order of the sides associated + * with each value matches the way CSS processes margin values:

+ *
+ *

Defaults to:


+     * {top:0, right:0, bottom:0, left:0}
+     * 
+ */ + defaultMargins : {left:0,top:0,right:0,bottom:0}, + /** + * @cfg {String} padding + * Defaults to '0'. Sets the padding to be applied to all child items managed by this + * container's layout. + */ + padding : '0', + // documented in subclasses + pack : 'start', + + // private + monitorResize : true, + scrollOffset : 0, + extraCls : 'x-box-item', + ctCls : 'x-box-layout-ct', + innerCls : 'x-box-inner', + + // private + isValidParent : function(c, target){ + return c.getEl().dom.parentNode == this.innerCt.dom; + }, + + // private + onLayout : function(ct, target){ + var cs = ct.items.items, len = cs.length, c, i, last = len-1, cm; + + if(!this.innerCt){ + target.addClass(this.ctCls); + + // the innerCt prevents wrapping and shuffling while + // the container is resizing + this.innerCt = target.createChild({cls:this.innerCls}); + this.padding = this.parseMargins(this.padding); + } + this.renderAll(ct, this.innerCt); + }, + + // private + renderItem : function(c){ + if(typeof c.margins == 'string'){ + c.margins = this.parseMargins(c.margins); + }else if(!c.margins){ + c.margins = this.defaultMargins; + } + Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments); + }, + + getTargetSize : function(target){ + return (Ext.isIE6 && Ext.isStrict && target.dom == document.body) ? target.getStyleSize() : target.getViewSize(); + }, + + getItems: function(ct){ + var items = []; + ct.items.each(function(c){ + if(c.isVisible()){ + items.push(c); + } + }); + return items; + } + + /** + * @property activeItem + * @hide + */ +}); + +/** + * @class Ext.layout.VBoxLayout + * @extends Ext.layout.BoxLayout + * A layout that arranges items vertically + */ +Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, { + /** + * @cfg {String} align + * Controls how the child items of the container are aligned. Acceptable configuration values for this + * property are: + *
+ */ + align : 'left', // left, center, stretch, strechmax + /** + * @cfg {String} pack + * Controls how the child items of the container are packed together. Acceptable configuration values + * for this property are: + *
+ */ + /** + * @cfg {Number} flex + * This configuation option is to be applied to child items of the container managed + * by this layout. Each child item with a flex property will be flexed vertically + * according to each item's relative flex value compared to the sum of all items with + * a flex value specified. Any child items that have either a flex = 0 or + * flex = undefined will not be 'flexed' (the initial size will not be changed). + */ + + // private + onLayout : function(ct, target){ + Ext.layout.VBoxLayout.superclass.onLayout.call(this, ct, target); + + + var cs = this.getItems(ct), cm, ch, margin, + size = this.getTargetSize(target), + w = size.width - target.getPadding('lr') - this.scrollOffset, + h = size.height - target.getPadding('tb'), + l = this.padding.left, t = this.padding.top, + isStart = this.pack == 'start', + isRestore = ['stretch', 'stretchmax'].indexOf(this.align) == -1, + stretchWidth = w - (this.padding.left + this.padding.right), + extraHeight = 0, + maxWidth = 0, + totalFlex = 0, + flexHeight = 0, + usedHeight = 0; + + Ext.each(cs, function(c){ + cm = c.margins; + totalFlex += c.flex || 0; + ch = c.getHeight(); + margin = cm.top + cm.bottom; + extraHeight += ch + margin; + flexHeight += margin + (c.flex ? 0 : ch); + maxWidth = Math.max(maxWidth, c.getWidth() + cm.left + cm.right); + }); + extraHeight = h - extraHeight - this.padding.top - this.padding.bottom; + + var innerCtWidth = maxWidth + this.padding.left + this.padding.right; + switch(this.align){ + case 'stretch': + this.innerCt.setSize(w, h); + break; + case 'stretchmax': + case 'left': + this.innerCt.setSize(innerCtWidth, h); + break; + case 'center': + this.innerCt.setSize(w = Math.max(w, innerCtWidth), h); + break; + } + + var availHeight = Math.max(0, h - this.padding.top - this.padding.bottom - flexHeight), + leftOver = availHeight, + heights = [], + restore = [], + idx = 0, + availableWidth = Math.max(0, w - this.padding.left - this.padding.right); + + + Ext.each(cs, function(c){ + if(isStart && c.flex){ + ch = Math.floor(availHeight * (c.flex / totalFlex)); + leftOver -= ch; + heights.push(ch); + } + }); + + if(this.pack == 'center'){ + t += extraHeight ? extraHeight / 2 : 0; + }else if(this.pack == 'end'){ + t += extraHeight; + } + Ext.each(cs, function(c){ + cm = c.margins; + t += cm.top; + c.setPosition(l + cm.left, t); + if(isStart && c.flex){ + ch = Math.max(0, heights[idx++] + (leftOver-- > 0 ? 1 : 0)); + if(isRestore){ + restore.push(c.getWidth()); + } + c.setSize(availableWidth, ch); + }else{ + ch = c.getHeight(); + } + t += ch + cm.bottom; + }); + + idx = 0; + Ext.each(cs, function(c){ + cm = c.margins; + if(this.align == 'stretch'){ + c.setWidth((stretchWidth - (cm.left + cm.right)).constrain( + c.minWidth || 0, c.maxWidth || 1000000)); + }else if(this.align == 'stretchmax'){ + c.setWidth((maxWidth - (cm.left + cm.right)).constrain( + c.minWidth || 0, c.maxWidth || 1000000)); + }else{ + if(this.align == 'center'){ + var diff = availableWidth - (c.getWidth() + cm.left + cm.right); + if(diff > 0){ + c.setPosition(l + cm.left + (diff/2), c.y); + } + } + if(isStart && c.flex){ + c.setWidth(restore[idx++]); + } + } + }, this); + } + /** + * @property activeItem + * @hide + */ +}); + +Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout; + +/** + * @class Ext.layout.HBoxLayout + * @extends Ext.layout.BoxLayout + * A layout that arranges items horizontally + */ +Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, { + /** + * @cfg {String} align + * Controls how the child items of the container are aligned. Acceptable configuration values for this + * property are: + *