2 * Ext JS Library 2.2.1
\r
3 * Copyright(c) 2006-2009, Ext JS, LLC.
\r
4 * licensing@extjs.com
\r
6 * http://extjs.com/license
\r
10 * @class Ext.Container
\r
11 * @extends Ext.BoxComponent
\r
12 * <p>Base class for any {@link Ext.BoxComponent} that can contain other components. The most commonly
\r
13 * used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}, but you can
\r
14 * create a lightweight Container to encapsulate an HTML element that is created to your
\r
15 * specifications at render time by using the {@link Ext.Component#autoEl autoEl} config option
\r
16 * which takes the form of a {@link Ext.DomHelper DomHelper} specification. If you do not need
\r
17 * the capabilities offered by the above mentioned classes, for instance embedded
\r
18 * {@link Ext.layout.ColumnLayout column} layouts inside FormPanels, then this is a useful technique.</p>
\r
19 * <p>The code below illustrates both how to explicitly <i>create</i> a Container, and how to implicitly
\r
20 * create one using the <b><tt>'container'</tt></b> xtype:<pre><code>
\r
21 var embeddedColumns = new Ext.Container({
\r
37 fieldLabel: 'Start date'
\r
43 fieldLabel: 'End date'
\r
46 });</code></pre></p>
\r
47 * Containers handle the basic behavior of containing items, namely adding, inserting and removing them.
\r
48 * The specific layout logic required to visually render contained items is delegated to any one of the different
\r
49 * {@link #layout} classes available.</p>
\r
50 * <p>When either specifying child {@link #items} of a Container, or dynamically adding components to a Container,
\r
51 * remember to consider how you wish the Container to arrange those child elements, and whether those child elements
\r
52 * need to be sized using one of Ext's built-in layout schemes.</p>
\r
53 * <p>By default, Containers use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
\r
54 * child components, appending them one after the other inside the Container, and does not apply any sizing at all.
\r
55 * This is a common source of confusion when widgets like GridPanels or TreePanels are added to Containers for
\r
56 * which no layout has been specified. If a Container is left to use the ContainerLayout scheme, none of its child
\r
57 * components will be resized, or changed in any way when the Container is resized.</p>
\r
58 * <p>A very common example of this is where a developer will attempt to add a GridPanel to a TabPanel by wrapping
\r
59 * the GridPanel <i>inside</i> a wrapping Panel and add that wrapping Panel to the TabPanel. This misses the point that
\r
60 * Ext's inheritance means that a GridPanel <b>is</b> a Component which can be added unadorned into a Container. If
\r
61 * that wrapping Panel has no layout configuration, then the GridPanel will not be sized as expected.<p>
\r
62 * <p>Below is an example of adding a newly created GridPanel to a TabPanel. A TabPanel uses {@link Ext.layout.CardLayout}
\r
63 * as its layout manager which means all its child items are sized to fit exactly into its client area. The following
\r
64 * code requires prior knowledge of how to create GridPanels. See {@link Ext.grid.GridPanel}, {@link Ext.data.Store}
\r
65 * and {@link Ext.data.JsonReader} as well as the grid examples in the Ext installation's <tt>examples/grid</tt>
\r
66 * directory.</p><pre><code>
\r
67 // Create the GridPanel.
\r
68 myGrid = new Ext.grid.GridPanel({
\r
70 columns: myColumnModel,
\r
74 myTabPanel.add(myGrid);
\r
75 myTabPanel.setActiveTab(myGrid);
\r
78 Ext.Container = Ext.extend(Ext.BoxComponent, {
\r
79 /** @cfg {Boolean} monitorResize
\r
80 * True to automatically monitor window resize events to handle anything that is sensitive to the current size
\r
81 * of the viewport. This value is typically managed by the chosen {@link #layout} and should not need to be set manually.
\r
84 * @cfg {String} layout
\r
85 * The layout type to be used in this container. If not specified, a default {@link Ext.layout.ContainerLayout}
\r
86 * will be created and used. Specific config values for the chosen layout type can be specified using
\r
87 * {@link #layoutConfig}. Valid values are:<ul class="mdetail-params">
\r
89 * <li>accordion</li>
\r
96 * <li>table</li></ul>
\r
99 * @cfg {Object} layoutConfig
\r
100 * This is a config object containing properties specific to the chosen layout (to be used in conjunction with
\r
101 * the {@link #layout} config value). For complete details regarding the valid config options for each layout
\r
102 * type, see the layout class corresponding to the type specified:<ul class="mdetail-params">
\r
103 * <li>{@link Ext.layout.Absolute}</li>
\r
104 * <li>{@link Ext.layout.Accordion}</li>
\r
105 * <li>{@link Ext.layout.AnchorLayout}</li>
\r
106 * <li>{@link Ext.layout.BorderLayout}</li>
\r
107 * <li>{@link Ext.layout.CardLayout}</li>
\r
108 * <li>{@link Ext.layout.ColumnLayout}</li>
\r
109 * <li>{@link Ext.layout.FitLayout}</li>
\r
110 * <li>{@link Ext.layout.FormLayout}</li>
\r
111 * <li>{@link Ext.layout.TableLayout}</li></ul>
\r
114 * @cfg {Boolean/Number} bufferResize
\r
115 * When set to true (100 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer
\r
116 * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers
\r
117 * with a large quantity of sub-components for which frequent layout calls would be expensive.
\r
120 * @cfg {String/Number} activeItem
\r
121 * A string component id or the numeric index of the component that should be initially activated within the
\r
122 * container's layout on render. For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first
\r
123 * item in the container's collection). activeItem only applies to layout styles that can display
\r
124 * items one at a time (like {@link Ext.layout.Accordion}, {@link Ext.layout.CardLayout} and
\r
125 * {@link Ext.layout.FitLayout}). Related to {@link Ext.layout.ContainerLayout#activeItem}.
\r
128 * @cfg {Mixed} items
\r
129 * A single item, or an array of child Components to be added to this container.
\r
130 * Each item can be any type of object based on {@link Ext.Component}.<br><br>
\r
131 * Component config objects may also be specified in order to avoid the overhead
\r
132 * of constructing a real Component object if lazy rendering might mean that the
\r
133 * added Component will not be rendered immediately. To take advantage of this
\r
134 * "lazy instantiation", set the {@link Ext.Component#xtype} config property to
\r
135 * the registered type of the Component wanted.<br><br>
\r
136 * For a list of all available xtypes, see {@link Ext.Component}.
\r
137 * If a single item is being passed, it should be passed directly as an object
\r
138 * reference (e.g., items: {...}). Multiple items should be passed as an array
\r
139 * of objects (e.g., items: [{...}, {...}]).
\r
142 * @cfg {Object} defaults
\r
143 * A config object that will be applied to all components added to this container either via the {@link #items}
\r
144 * config or via the {@link #add} or {@link #insert} methods. The defaults config can contain any number of
\r
145 * name/value property pairs to be added to each item, and should be valid for the types of items
\r
146 * being added to the container. For example, to automatically apply padding to the body of each of a set of
\r
147 * contained {@link Ext.Panel} items, you could pass: defaults: {bodyStyle:'padding:15px'}.
\r
150 /** @cfg {Boolean} autoDestroy
\r
151 * If true the container will automatically destroy any contained component that is removed from it, else
\r
152 * destruction must be handled manually (defaults to true).
\r
155 /** @cfg {Boolean} hideBorders
\r
156 * True to hide the borders of each contained component, false to defer to the component's existing
\r
157 * border settings (defaults to false).
\r
159 /** @cfg {String} defaultType
\r
160 * <p>The default {@link Ext.Component xtype} of child Components to create in this Container when
\r
161 * a child item is specified as a raw configuration object, rather than as an instantiated Component.</p>
\r
162 * <p>Defaults to 'panel'.</p>
\r
164 defaultType: 'panel',
\r
167 initComponent : function(){
\r
168 Ext.Container.superclass.initComponent.call(this);
\r
172 * @event afterlayout
\r
173 * Fires when the components in this container are arranged by the associated layout manager.
\r
174 * @param {Ext.Container} this
\r
175 * @param {ContainerLayout} layout The ContainerLayout implementation for this container
\r
180 * Fires before any {@link Ext.Component} is added or inserted into the container.
\r
181 * A handler can return false to cancel the add.
\r
182 * @param {Ext.Container} this
\r
183 * @param {Ext.Component} component The component being added
\r
184 * @param {Number} index The index at which the component will be added to the container's items collection
\r
188 * @event beforeremove
\r
189 * Fires before any {@link Ext.Component} is removed from the container. A handler can return
\r
190 * false to cancel the remove.
\r
191 * @param {Ext.Container} this
\r
192 * @param {Ext.Component} component The component being removed
\r
197 * Fires after any {@link Ext.Component} is added or inserted into the container.
\r
198 * @param {Ext.Container} this
\r
199 * @param {Ext.Component} component The component that was added
\r
200 * @param {Number} index The index at which the component was added to the container's items collection
\r
205 * Fires after any {@link Ext.Component} is removed from the container.
\r
206 * @param {Ext.Container} this
\r
207 * @param {Ext.Component} component The component that was removed
\r
213 * The collection of components in this container as a {@link Ext.util.MixedCollection}
\r
214 * @type MixedCollection
\r
217 var items = this.items;
\r
220 if(Ext.isArray(items) && items.length > 0){
\r
221 this.add.apply(this, items);
\r
229 initItems : function(){
\r
231 this.items = new Ext.util.MixedCollection(false, this.getComponentId);
\r
232 this.getLayout(); // initialize the layout
\r
237 setLayout : function(layout){
\r
238 if(this.layout && this.layout != layout){
\r
239 this.layout.setContainer(null);
\r
242 this.layout = layout;
\r
243 layout.setContainer(this);
\r
247 render : function(){
\r
248 Ext.Container.superclass.render.apply(this, arguments);
\r
250 if(typeof this.layout == 'string'){
\r
251 this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig);
\r
253 this.setLayout(this.layout);
\r
255 if(this.activeItem !== undefined){
\r
256 var item = this.activeItem;
\r
257 delete this.activeItem;
\r
258 this.layout.setActiveItem(item);
\r
265 if(this.monitorResize === true){
\r
266 Ext.EventManager.onWindowResize(this.doLayout, this, [false]);
\r
271 * <p>Returns the Element to be used to contain the child Components of this Container.</p>
\r
272 * <p>An implementation is provided which returns the Container's {@link #getEl Element}, but
\r
273 * if there is a more complex structure to a Container, this may be overridden to return
\r
274 * the element into which the {@link #layout layout} renders child Components.</p>
\r
275 * @return {Ext.Element} The Element to render child Components into.
\r
277 getLayoutTarget : function(){
\r
281 // private - used as the key lookup function for the items collection
\r
282 getComponentId : function(comp){
\r
283 return comp.itemId || comp.id;
\r
287 * <p>Adds a {@link Ext.Component Component} to this Container. Fires the {@link #beforeadd} event before
\r
288 * adding, then fires the {@link #add} event after the component has been added.</p>
\r
289 * <p>You will never call the render method of a child Component when using a Container.
\r
290 * Child Components are rendered by this Container's {@link #layout} manager when
\r
291 * this Container is first rendered.</p>
\r
292 * <p>Certain layout managers allow dynamic addition of child components. Those that do
\r
293 * include {@link Ext.layout.CardLayout}, {@link Ext.layout.AnchorLayout},
\r
294 * {@link Ext.layout.FormLayout}, {@link Ext.layout.TableLayout}.</p>
\r
295 * <p>If the Container is already rendered when add is called, you may need to call
\r
296 * {@link #doLayout} to refresh the view which causes any unrendered child Components
\r
297 * to be rendered. This is required so that you can add multiple child components if needed
\r
298 * while only refreshing the layout once.</p>
\r
299 * <p>When creating complex UIs, it is important to remember that sizing and positioning
\r
300 * of child items is the responsibility of the Container's {@link #layout} manager. If
\r
301 * you expect child items to be sized in response to user interactions, you must
\r
302 * specify a layout manager which creates and manages the type of layout you have in mind.</p>
\r
303 * <p><b>Omitting the {@link #layout} config means that a basic layout manager is
\r
304 * used which does nothnig but render child components sequentially into the Container.
\r
305 * No sizing or positioning will be performed in this situation.</b></p>
\r
306 * @param {Ext.Component/Object} component The Component to add.<br><br>
\r
307 * Ext uses lazy rendering, and will only render the added Component should
\r
308 * it become necessary, that is: when the Container is layed out either on first render
\r
309 * or in response to a {@link #doLayout} call.<br><br>
\r
310 * A Component config object may be passed instead of an instantiated Component object.
\r
311 * The type of Component created from a config object is determined by the {@link Ext.Component#xtype xtype}
\r
312 * config property. If no xtype is configured, the Container's {@link #defaultType}
\r
314 * For a list of all available xtypes, see {@link Ext.Component}.
\r
315 * @return {Ext.Component} component The Component (or config object) that was
\r
316 * added with the Container's default config values applied.
\r
317 * <p>example:</p><pre><code>
\r
318 var myNewGrid = new Ext.grid.GridPanel({
\r
320 colModel: myColModel
\r
322 myTabPanel.add(myNewGrid);
\r
323 myTabPanel.setActiveTab(myNewGrid);
\r
326 add : function(comp){
\r
330 var a = arguments, len = a.length;
\r
332 for(var i = 0; i < len; i++) {
\r
337 var c = this.lookupComponent(this.applyDefaults(comp));
\r
338 var pos = this.items.length;
\r
339 if(this.fireEvent('beforeadd', this, c, pos) !== false && this.onBeforeAdd(c) !== false){
\r
342 this.fireEvent('add', this, c, pos);
\r
348 * Inserts a Component into this Container at a specified index. Fires the
\r
349 * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the
\r
350 * Component has been inserted.
\r
351 * @param {Number} index The index at which the Component will be inserted
\r
352 * into the Container's items collection
\r
353 * @param {Ext.Component} component The child Component to insert.<br><br>
\r
354 * Ext uses lazy rendering, and will only render the inserted Component should
\r
355 * it become necessary.<br><br>
\r
356 * A Component config object may be passed in order to avoid the overhead of
\r
357 * constructing a real Component object if lazy rendering might mean that the
\r
358 * inserted Component will not be rendered immediately. To take advantage of
\r
359 * this "lazy instantiation", set the {@link Ext.Component#xtype} config
\r
360 * property to the registered type of the Component wanted.<br><br>
\r
361 * For a list of all available xtypes, see {@link Ext.Component}.
\r
362 * @return {Ext.Component} component The Component (or config object) that was
\r
363 * inserted with the Container's default config values applied.
\r
365 insert : function(index, comp){
\r
369 var a = arguments, len = a.length;
\r
371 for(var i = len-1; i >= 1; --i) {
\r
372 this.insert(index, a[i]);
\r
376 var c = this.lookupComponent(this.applyDefaults(comp));
\r
378 if(c.ownerCt == this && this.items.indexOf(c) < index){
\r
382 if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
\r
383 this.items.insert(index, c);
\r
385 this.fireEvent('add', this, c, index);
\r
391 applyDefaults : function(c){
\r
393 if(typeof c == 'string'){
\r
394 c = Ext.ComponentMgr.get(c);
\r
395 Ext.apply(c, this.defaults);
\r
396 }else if(!c.events){
\r
397 Ext.applyIf(c, this.defaults);
\r
399 Ext.apply(c, this.defaults);
\r
406 onBeforeAdd : function(item){
\r
408 item.ownerCt.remove(item, false);
\r
410 if(this.hideBorders === true){
\r
411 item.border = (item.border === true);
\r
416 * Removes a component from this container. Fires the {@link #beforeremove} event before removing, then fires
\r
417 * the {@link #remove} event after the component has been removed.
\r
418 * @param {Component/String} component The component reference or id to remove.
\r
419 * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
\r
420 * Defaults to the value of this Container's {@link #autoDestroy} config.
\r
421 * @return {Ext.Component} component The Component that was removed.
\r
423 remove : function(comp, autoDestroy){
\r
424 var c = this.getComponent(comp);
\r
425 if(c && this.fireEvent('beforeremove', this, c) !== false){
\r
426 this.items.remove(c);
\r
428 if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){
\r
431 if(this.layout && this.layout.activeItem == c){
\r
432 delete this.layout.activeItem;
\r
434 this.fireEvent('remove', this, c);
\r
440 * Removes all components from this container.
\r
441 * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
\r
442 * Defaults to the value of this Container's {@link #autoDestroy} config.
\r
443 * @return {Array} Array of the destroyed components
\r
445 removeAll: function(autoDestroy){
\r
446 var item, items = [];
\r
447 while((item = this.items.last())){
\r
448 items.unshift(this.remove(item, autoDestroy));
\r
454 * Gets a direct child Component by id, or by index.
\r
455 * @param {String/Number} id or index of child Component to return.
\r
456 * @return Ext.Component
\r
458 getComponent : function(comp){
\r
459 if(typeof comp == 'object'){
\r
462 return this.items.get(comp);
\r
466 lookupComponent : function(comp){
\r
467 if(typeof comp == 'string'){
\r
468 return Ext.ComponentMgr.get(comp);
\r
469 }else if(!comp.events){
\r
470 return this.createComponent(comp);
\r
476 createComponent : function(config){
\r
477 return Ext.ComponentMgr.create(config, this.defaultType);
\r
481 * Force this container's layout to be recalculated. A call to this function is required after adding a new component
\r
482 * to an already rendered container, or possibly after changing sizing/position properties of child components.
\r
483 * @param {Boolean} shallow (optional) True to only calc the layout of this component, and let child components auto
\r
484 * calc layouts as required (defaults to false, which calls doLayout recursively for each subcontainer)
\r
486 doLayout : function(shallow){
\r
487 if(this.rendered && this.layout){
\r
488 this.layout.layout();
\r
490 if(shallow !== false && this.items){
\r
491 var cs = this.items.items;
\r
492 for(var i = 0, len = cs.length; i < len; i++) {
\r
502 * Returns the layout currently in use by the container. If the container does not currently have a layout
\r
503 * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout.
\r
504 * @return {ContainerLayout} layout The container's layout
\r
506 getLayout : function(){
\r
508 var layout = new Ext.layout.ContainerLayout(this.layoutConfig);
\r
509 this.setLayout(layout);
\r
511 return this.layout;
\r
515 beforeDestroy : function(){
\r
517 Ext.destroy.apply(Ext, this.items.items);
\r
519 if(this.monitorResize){
\r
520 Ext.EventManager.removeResizeListener(this.doLayout, this);
\r
522 if (this.layout && this.layout.destroy) {
\r
523 this.layout.destroy();
\r
525 Ext.Container.superclass.beforeDestroy.call(this);
\r
529 * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (<i>this</i>) of
\r
530 * function call will be the scope provided or the current component. The arguments to the function
\r
531 * will be the args provided or the current component. If the function returns false at any point,
\r
532 * the bubble is stopped.
\r
533 * @param {Function} fn The function to call
\r
534 * @param {Object} scope (optional) The scope of the function (defaults to current node)
\r
535 * @param {Array} args (optional) The args to call the function with (default to passing the current component)
\r
537 bubble : function(fn, scope, args){
\r
540 if(fn.apply(scope || p, args || [p]) === false){
\r
548 * Cascades down the component/container heirarchy from this component (called first), calling the specified function with
\r
549 * each component. The scope (<i>this</i>) of
\r
550 * function call will be the scope provided or the current component. The arguments to the function
\r
551 * will be the args provided or the current component. If the function returns false at any point,
\r
552 * the cascade is stopped on that branch.
\r
553 * @param {Function} fn The function to call
\r
554 * @param {Object} scope (optional) The scope of the function (defaults to current component)
\r
555 * @param {Array} args (optional) The args to call the function with (defaults to passing the current component)
\r
557 cascade : function(fn, scope, args){
\r
558 if(fn.apply(scope || this, args || [this]) !== false){
\r
560 var cs = this.items.items;
\r
561 for(var i = 0, len = cs.length; i < len; i++){
\r
563 cs[i].cascade(fn, scope, args);
\r
565 fn.apply(scope || cs[i], args || [cs[i]]);
\r
573 * Find a component under this container at any level by id
\r
574 * @param {String} id
\r
575 * @return Ext.Component
\r
577 findById : function(id){
\r
579 this.cascade(function(c){
\r
580 if(ct != c && c.id === id){
\r
589 * Find a component under this container at any level by xtype or class
\r
590 * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
\r
591 * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
\r
592 * the default), or true to check whether this Component is directly of the specified xtype.
\r
593 * @return {Array} Array of Ext.Components
\r
595 findByType : function(xtype, shallow){
\r
596 return this.findBy(function(c){
\r
597 return c.isXType(xtype, shallow);
\r
602 * Find a component under this container at any level by property
\r
603 * @param {String} prop
\r
604 * @param {String} value
\r
605 * @return {Array} Array of Ext.Components
\r
607 find : function(prop, value){
\r
608 return this.findBy(function(c){
\r
609 return c[prop] === value;
\r
614 * Find a component under this container at any level by a custom function. If the passed function returns
\r
615 * true, the component will be included in the results. The passed function is called with the arguments (component, this container).
\r
616 * @param {Function} fcn
\r
617 * @param {Object} scope (optional)
\r
618 * @return {Array} Array of Ext.Components
\r
620 findBy : function(fn, scope){
\r
621 var m = [], ct = this;
\r
622 this.cascade(function(c){
\r
623 if(ct != c && fn.call(scope || c, c, ct) === true){
\r
631 Ext.Container.LAYOUTS = {};
\r
632 Ext.reg('container', Ext.Container);