commit extjs-2.2.1
[extjs.git] / source / widgets / Container.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\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
22     autoEl: {},\r
23     layout: 'column',\r
24     defaults: {\r
25         xtype: 'container',\r
26         autoEl: {},\r
27         layout: 'form',\r
28         columnWidth: 0.5,\r
29         style: {\r
30             padding: '10px'\r
31         }\r
32     },\r
33     items: [{\r
34         items: {\r
35             xtype: 'datefield',\r
36             name: 'startDate',\r
37             fieldLabel: 'Start date'\r
38         }\r
39     }, {\r
40         items: {\r
41             xtype: 'datefield',\r
42             name: 'endDate',\r
43             fieldLabel: 'End date'\r
44         }\r
45     }]\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
69     store: myStore,\r
70     columns: myColumnModel,\r
71     title: 'Results',\r
72 });\r
73 \r
74 myTabPanel.add(myGrid);\r
75 myTabPanel.setActiveTab(myGrid);\r
76 </code></pre>\r
77  */\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
82      */\r
83     /**\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
88      * <li>absolute</li>\r
89      * <li>accordion</li>\r
90      * <li>anchor</li>\r
91      * <li>border</li>\r
92      * <li>card</li>\r
93      * <li>column</li>\r
94      * <li>fit</li>\r
95      * <li>form</li>\r
96      * <li>table</li></ul>\r
97      */\r
98     /**\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
112      */\r
113     /**\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
118      */\r
119     /**\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
126      */\r
127     /**\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
140      */\r
141     /**\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
148      */\r
149 \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
153      */\r
154     autoDestroy: 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
158      */\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
163      */\r
164     defaultType: 'panel',\r
165 \r
166     // private\r
167     initComponent : function(){\r
168         Ext.Container.superclass.initComponent.call(this);\r
169 \r
170         this.addEvents(\r
171             /**\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
176              */\r
177             'afterlayout',\r
178             /**\r
179              * @event beforeadd\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
185              */\r
186             'beforeadd',\r
187             /**\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
193              */\r
194             'beforeremove',\r
195             /**\r
196              * @event add\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
201              */\r
202             'add',\r
203             /**\r
204              * @event remove\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
208              */\r
209             'remove'\r
210         );\r
211 \r
212         /**\r
213          * The collection of components in this container as a {@link Ext.util.MixedCollection}\r
214          * @type MixedCollection\r
215          * @property items\r
216          */\r
217         var items = this.items;\r
218         if(items){\r
219             delete this.items;\r
220             if(Ext.isArray(items) && items.length > 0){\r
221                 this.add.apply(this, items);\r
222             }else{\r
223                 this.add(items);\r
224             }\r
225         }\r
226     },\r
227 \r
228     // private\r
229     initItems : function(){\r
230         if(!this.items){\r
231             this.items = new Ext.util.MixedCollection(false, this.getComponentId);\r
232             this.getLayout(); // initialize the layout\r
233         }\r
234     },\r
235 \r
236     // private\r
237     setLayout : function(layout){\r
238         if(this.layout && this.layout != layout){\r
239             this.layout.setContainer(null);\r
240         }\r
241         this.initItems();\r
242         this.layout = layout;\r
243         layout.setContainer(this);\r
244     },\r
245 \r
246     // private\r
247     render : function(){\r
248         Ext.Container.superclass.render.apply(this, arguments);\r
249         if(this.layout){\r
250             if(typeof this.layout == 'string'){\r
251                 this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig);\r
252             }\r
253             this.setLayout(this.layout);\r
254 \r
255             if(this.activeItem !== undefined){\r
256                 var item = this.activeItem;\r
257                 delete this.activeItem;\r
258                 this.layout.setActiveItem(item);\r
259                 return;\r
260             }\r
261         }\r
262         if(!this.ownerCt){\r
263             this.doLayout();\r
264         }\r
265         if(this.monitorResize === true){\r
266             Ext.EventManager.onWindowResize(this.doLayout, this, [false]);\r
267         }\r
268     },\r
269 \r
270     /**\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
276      */\r
277     getLayoutTarget : function(){\r
278         return this.el;\r
279     },\r
280 \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
284     },\r
285 \r
286     /**\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
313      * is used.<br><br>\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
319     store: myStore,\r
320     colModel: myColModel\r
321 });\r
322 myTabPanel.add(myNewGrid);\r
323 myTabPanel.setActiveTab(myNewGrid);\r
324 </code></pre>\r
325      */\r
326     add : function(comp){\r
327         if(!this.items){\r
328             this.initItems();\r
329         }\r
330         var a = arguments, len = a.length;\r
331         if(len > 1){\r
332             for(var i = 0; i < len; i++) {\r
333                 this.add(a[i]);\r
334             }\r
335             return;\r
336         }\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
340             this.items.add(c);\r
341             c.ownerCt = this;\r
342             this.fireEvent('add', this, c, pos);\r
343         }\r
344         return c;\r
345     },\r
346 \r
347     /**\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
364      */\r
365     insert : function(index, comp){\r
366         if(!this.items){\r
367             this.initItems();\r
368         }\r
369         var a = arguments, len = a.length;\r
370         if(len > 2){\r
371             for(var i = len-1; i >= 1; --i) {\r
372                 this.insert(index, a[i]);\r
373             }\r
374             return;\r
375         }\r
376         var c = this.lookupComponent(this.applyDefaults(comp));\r
377 \r
378         if(c.ownerCt == this && this.items.indexOf(c) < index){\r
379             --index;\r
380         }\r
381 \r
382         if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){\r
383             this.items.insert(index, c);\r
384             c.ownerCt = this;\r
385             this.fireEvent('add', this, c, index);\r
386         }\r
387         return c;\r
388     },\r
389 \r
390     // private\r
391     applyDefaults : function(c){\r
392         if(this.defaults){\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
398             }else{\r
399                 Ext.apply(c, this.defaults);\r
400             }\r
401         }\r
402         return c;\r
403     },\r
404 \r
405     // private\r
406     onBeforeAdd : function(item){\r
407         if(item.ownerCt){\r
408             item.ownerCt.remove(item, false);\r
409         }\r
410         if(this.hideBorders === true){\r
411             item.border = (item.border === true);\r
412         }\r
413     },\r
414 \r
415     /**\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
422      */\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
427             delete c.ownerCt;\r
428             if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){\r
429                 c.destroy();\r
430             }\r
431             if(this.layout && this.layout.activeItem == c){\r
432                 delete this.layout.activeItem;\r
433             }\r
434             this.fireEvent('remove', this, c);\r
435         }\r
436         return c;\r
437     },\r
438     \r
439     /**\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
444      */\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
449         }\r
450         return items;\r
451     },\r
452 \r
453     /**\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
457      */\r
458     getComponent : function(comp){\r
459         if(typeof comp == 'object'){\r
460             return comp;\r
461         }\r
462         return this.items.get(comp);\r
463     },\r
464 \r
465     // private\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
471         }\r
472         return comp;\r
473     },\r
474 \r
475     // private\r
476     createComponent : function(config){\r
477         return Ext.ComponentMgr.create(config, this.defaultType);\r
478     },\r
479 \r
480     /**\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
485      */\r
486     doLayout : function(shallow){\r
487         if(this.rendered && this.layout){\r
488             this.layout.layout();\r
489         }\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
493                 var c  = cs[i];\r
494                 if(c.doLayout){\r
495                     c.doLayout();\r
496                 }\r
497             }\r
498         }\r
499     },\r
500 \r
501     /**\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
505      */\r
506     getLayout : function(){\r
507         if(!this.layout){\r
508             var layout = new Ext.layout.ContainerLayout(this.layoutConfig);\r
509             this.setLayout(layout);\r
510         }\r
511         return this.layout;\r
512     },\r
513 \r
514     // private\r
515     beforeDestroy : function(){\r
516         if(this.items){\r
517             Ext.destroy.apply(Ext, this.items.items);\r
518         }\r
519         if(this.monitorResize){\r
520             Ext.EventManager.removeResizeListener(this.doLayout, this);\r
521         }\r
522         if (this.layout && this.layout.destroy) {\r
523             this.layout.destroy();\r
524         }\r
525         Ext.Container.superclass.beforeDestroy.call(this);\r
526     },\r
527 \r
528     /**\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
536      */\r
537     bubble : function(fn, scope, args){\r
538         var p = this;\r
539         while(p){\r
540             if(fn.apply(scope || p, args || [p]) === false){\r
541                 break;\r
542             }\r
543             p = p.ownerCt;\r
544         }\r
545     },\r
546 \r
547     /**\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
556      */\r
557     cascade : function(fn, scope, args){\r
558         if(fn.apply(scope || this, args || [this]) !== false){\r
559             if(this.items){\r
560                 var cs = this.items.items;\r
561                 for(var i = 0, len = cs.length; i < len; i++){\r
562                     if(cs[i].cascade){\r
563                         cs[i].cascade(fn, scope, args);\r
564                     }else{\r
565                         fn.apply(scope || cs[i], args || [cs[i]]);\r
566                     }\r
567                 }\r
568             }\r
569         }\r
570     },\r
571 \r
572     /**\r
573      * Find a component under this container at any level by id\r
574      * @param {String} id\r
575      * @return Ext.Component\r
576      */\r
577     findById : function(id){\r
578         var m, ct = this;\r
579         this.cascade(function(c){\r
580             if(ct != c && c.id === id){\r
581                 m = c;\r
582                 return false;\r
583             }\r
584         });\r
585         return m || null;\r
586     },\r
587 \r
588     /**\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
594      */\r
595     findByType : function(xtype, shallow){\r
596         return this.findBy(function(c){\r
597             return c.isXType(xtype, shallow);\r
598         });\r
599     },\r
600 \r
601     /**\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
606      */\r
607     find : function(prop, value){\r
608         return this.findBy(function(c){\r
609             return c[prop] === value;\r
610         });\r
611     },\r
612 \r
613     /**\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
619      */\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
624                 m.push(c);\r
625             }\r
626         });\r
627         return m;\r
628     }\r
629 });\r
630 \r
631 Ext.Container.LAYOUTS = {};\r
632 Ext.reg('container', Ext.Container);