Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / container / AbstractContainer.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.container.AbstractContainer
17  * @extends Ext.Component
18  * An abstract base class which provides shared methods for Containers across the Sencha product line.
19  * @private
20  */
21 Ext.define('Ext.container.AbstractContainer', {
22
23     /* Begin Definitions */
24
25     extend: 'Ext.Component',
26
27     requires: [
28         'Ext.util.MixedCollection',
29         'Ext.layout.container.Auto',
30         'Ext.ZIndexManager'
31     ],
32
33     /* End Definitions */
34     /**
35      * @cfg {String/Object} layout
36      * <p><b>Important</b>: In order for child items to be correctly sized and
37      * positioned, typically a layout manager <b>must</b> be specified through
38      * the <code>layout</code> configuration option.</p>
39      * <p>The sizing and positioning of child {@link #items} is the responsibility of
40      * the Container's layout manager which creates and manages the type of layout
41      * you have in mind.  For example:</p>
42      * <p>If the {@link #layout} configuration is not explicitly specified for
43      * a general purpose container (e.g. Container or Panel) the
44      * {@link Ext.layout.container.Auto default layout manager} will be used
45      * which does nothing but render child components sequentially into the
46      * Container (no sizing or positioning will be performed in this situation).</p>
47      * <p><b><code>layout</code></b> may be specified as either as an Object or as a String:</p>
48      * <div><ul class="mdetail-params">
49      * <li><u>Specify as an Object</u></li>
50      * <div><ul class="mdetail-params">
51      * <li>Example usage:</li>
52      * <pre><code>
53 layout: {
54     type: 'vbox',
55     align: 'left'
56 }
57        </code></pre>
58      *
59      * <li><code><b>type</b></code></li>
60      * <br/><p>The layout type to be used for this container.  If not specified,
61      * a default {@link Ext.layout.container.Auto} will be created and used.</p>
62      * <p>Valid layout <code>type</code> values are:</p>
63      * <div class="sub-desc"><ul class="mdetail-params">
64      * <li><code><b>{@link Ext.layout.container.Auto Auto}</b></code> &nbsp;&nbsp;&nbsp; <b>Default</b></li>
65      * <li><code><b>{@link Ext.layout.container.Card card}</b></code></li>
66      * <li><code><b>{@link Ext.layout.container.Fit fit}</b></code></li>
67      * <li><code><b>{@link Ext.layout.container.HBox hbox}</b></code></li>
68      * <li><code><b>{@link Ext.layout.container.VBox vbox}</b></code></li>
69      * <li><code><b>{@link Ext.layout.container.Anchor anchor}</b></code></li>
70      * <li><code><b>{@link Ext.layout.container.Table table}</b></code></li>
71      * </ul></div>
72      *
73      * <li>Layout specific configuration properties</li>
74      * <p>Additional layout specific configuration properties may also be
75      * specified. For complete details regarding the valid config options for
76      * each layout type, see the layout class corresponding to the <code>type</code>
77      * specified.</p>
78      *
79      * </ul></div>
80      *
81      * <li><u>Specify as a String</u></li>
82      * <div><ul class="mdetail-params">
83      * <li>Example usage:</li>
84      * <pre><code>
85 layout: 'vbox'
86        </code></pre>
87      * <li><code><b>layout</b></code></li>
88      * <p>The layout <code>type</code> to be used for this container (see list
89      * of valid layout type values above).</p>
90      * <p>Additional layout specific configuration properties. For complete
91      * details regarding the valid config options for each layout type, see the
92      * layout class corresponding to the <code>layout</code> specified.</p>
93      * </ul></div></ul></div>
94      */
95
96     /**
97      * @cfg {String/Number} activeItem
98      * A string component id or the numeric index of the component that should be initially activated within the
99      * container's layout on render.  For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first
100      * item in the container's collection).  activeItem only applies to layout styles that can display
101      * items one at a time (like {@link Ext.layout.container.Card} and {@link Ext.layout.container.Fit}).
102      */
103     /**
104      * @cfg {Object/Object[]} items
105      * <p>A single item, or an array of child Components to be added to this container</p>
106      * <p><b>Unless configured with a {@link #layout}, a Container simply renders child Components serially into
107      * its encapsulating element and performs no sizing or positioning upon them.</b><p>
108      * <p>Example:</p>
109      * <pre><code>
110 // specifying a single item
111 items: {...},
112 layout: 'fit',    // The single items is sized to fit
113
114 // specifying multiple items
115 items: [{...}, {...}],
116 layout: 'hbox', // The items are arranged horizontally
117        </code></pre>
118      * <p>Each item may be:</p>
119      * <ul>
120      * <li>A {@link Ext.Component Component}</li>
121      * <li>A Component configuration object</li>
122      * </ul>
123      * <p>If a configuration object is specified, the actual type of Component to be
124      * instantiated my be indicated by using the {@link Ext.Component#xtype xtype} option.</p>
125      * <p>Every Component class has its own {@link Ext.Component#xtype xtype}.</p>
126      * <p>If an {@link Ext.Component#xtype xtype} is not explicitly
127      * specified, the {@link #defaultType} for the Container is used, which by default is usually <code>panel</code>.</p>
128      * <p><b>Notes</b>:</p>
129      * <p>Ext uses lazy rendering. Child Components will only be rendered
130      * should it become necessary. Items are automatically laid out when they are first
131      * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.</p>
132      * <p>Do not specify <code>{@link Ext.panel.Panel#contentEl contentEl}</code> or
133      * <code>{@link Ext.panel.Panel#html html}</code> with <code>items</code>.</p>
134      */
135     /**
136      * @cfg {Object/Function} defaults
137      * This option is a means of applying default settings to all added items whether added through the {@link #items}
138      * config or via the {@link #add} or {@link #insert} methods.
139      *
140      * Defaults are applied to both config objects and instantiated components conditionally so as not to override
141      * existing properties in the item (see {@link Ext#applyIf}).
142      *
143      * If the defaults option is specified as a function, then the function will be called using this Container as the
144      * scope (`this` reference) and passing the added item as the first parameter. Any resulting object
145      * from that call is then applied to the item as default properties.
146      *
147      * For example, to automatically apply padding to the body of each of a set of
148      * contained {@link Ext.panel.Panel} items, you could pass: `defaults: {bodyStyle:'padding:15px'}`.
149      *
150      * Usage:
151      *
152      *     defaults: { // defaults are applied to items, not the container
153      *         autoScroll: true
154      *     },
155      *     items: [
156      *         // default will not be applied here, panel1 will be autoScroll: false
157      *         {
158      *             xtype: 'panel',
159      *             id: 'panel1',
160      *             autoScroll: false
161      *         },
162      *         // this component will have autoScroll: true
163      *         new Ext.panel.Panel({
164      *             id: 'panel2'
165      *         })
166      *     ]
167      */
168
169     /** @cfg {Boolean} suspendLayout
170      * If true, suspend calls to doLayout.  Useful when batching multiple adds to a container and not passing them
171      * as multiple arguments or an array.
172      */
173     suspendLayout : false,
174
175     /** @cfg {Boolean} autoDestroy
176      * If true the container will automatically destroy any contained component that is removed from it, else
177      * destruction must be handled manually.
178      * Defaults to true.
179      */
180     autoDestroy : true,
181
182      /** @cfg {String} defaultType
183       * <p>The default {@link Ext.Component xtype} of child Components to create in this Container when
184       * a child item is specified as a raw configuration object, rather than as an instantiated Component.</p>
185       * <p>Defaults to <code>'panel'</code>.</p>
186       */
187     defaultType: 'panel',
188
189     isContainer : true,
190
191     /**
192      * The number of container layout calls made on this object.
193      * @property layoutCounter
194      * @type {Number}
195      * @private
196      */
197     layoutCounter : 0,
198
199     baseCls: Ext.baseCSSPrefix + 'container',
200
201     /**
202      * @cfg {String[]} bubbleEvents
203      * <p>An array of events that, when fired, should be bubbled to any parent container.
204      * See {@link Ext.util.Observable#enableBubble}.
205      * Defaults to <code>['add', 'remove']</code>.
206      */
207     bubbleEvents: ['add', 'remove'],
208
209     // @private
210     initComponent : function(){
211         var me = this;
212         me.addEvents(
213             /**
214              * @event afterlayout
215              * Fires when the components in this container are arranged by the associated layout manager.
216              * @param {Ext.container.Container} this
217              * @param {Ext.layout.container.Container} layout The ContainerLayout implementation for this container
218              */
219             'afterlayout',
220             /**
221              * @event beforeadd
222              * Fires before any {@link Ext.Component} is added or inserted into the container.
223              * A handler can return false to cancel the add.
224              * @param {Ext.container.Container} this
225              * @param {Ext.Component} component The component being added
226              * @param {Number} index The index at which the component will be added to the container's items collection
227              */
228             'beforeadd',
229             /**
230              * @event beforeremove
231              * Fires before any {@link Ext.Component} is removed from the container.  A handler can return
232              * false to cancel the remove.
233              * @param {Ext.container.Container} this
234              * @param {Ext.Component} component The component being removed
235              */
236             'beforeremove',
237             /**
238              * @event add
239              * @bubbles
240              * Fires after any {@link Ext.Component} is added or inserted into the container.
241              * @param {Ext.container.Container} this
242              * @param {Ext.Component} component The component that was added
243              * @param {Number} index The index at which the component was added to the container's items collection
244              */
245             'add',
246             /**
247              * @event remove
248              * @bubbles
249              * Fires after any {@link Ext.Component} is removed from the container.
250              * @param {Ext.container.Container} this
251              * @param {Ext.Component} component The component that was removed
252              */
253             'remove'
254         );
255
256         // layoutOnShow stack
257         me.layoutOnShow = Ext.create('Ext.util.MixedCollection');
258         me.callParent();
259         me.initItems();
260     },
261
262     // @private
263     initItems : function() {
264         var me = this,
265             items = me.items;
266
267         /**
268          * The MixedCollection containing all the child items of this container.
269          * @property items
270          * @type Ext.util.MixedCollection
271          */
272         me.items = Ext.create('Ext.util.MixedCollection', false, me.getComponentId);
273
274         if (items) {
275             if (!Ext.isArray(items)) {
276                 items = [items];
277             }
278
279             me.add(items);
280         }
281     },
282
283     // @private
284     afterRender : function() {
285         this.getLayout();
286         this.callParent();
287     },
288
289     renderChildren: function () {
290         var me = this,
291             layout = me.getLayout();
292
293         me.callParent();
294         // this component's elements exist, so now create the child components' elements
295
296         if (layout) {
297             me.suspendLayout = true;
298             layout.renderChildren();
299             delete me.suspendLayout;
300         }
301     },
302
303     // @private
304     setLayout : function(layout) {
305         var currentLayout = this.layout;
306
307         if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
308             currentLayout.setOwner(null);
309         }
310
311         this.layout = layout;
312         layout.setOwner(this);
313     },
314
315     /**
316      * Returns the {@link Ext.layout.container.AbstractContainer layout} instance currently associated with this Container.
317      * If a layout has not been instantiated yet, that is done first
318      * @return {Ext.layout.container.AbstractContainer} The layout
319      */
320     getLayout : function() {
321         var me = this;
322         if (!me.layout || !me.layout.isLayout) {
323             me.setLayout(Ext.layout.Layout.create(me.layout, 'autocontainer'));
324         }
325
326         return me.layout;
327     },
328
329     /**
330      * Manually force this container's layout to be recalculated. The framework uses this internally to refresh layouts
331      * form most cases.
332      * @return {Ext.container.Container} this
333      */
334     doLayout : function() {
335         var me = this,
336             layout = me.getLayout();
337
338         if (me.rendered && layout && !me.suspendLayout) {
339             // If either dimension is being auto-set, then it requires a ComponentLayout to be run.
340             if (!me.isFixedWidth() || !me.isFixedHeight()) {
341                 // Only run the ComponentLayout if it is not already in progress
342                 if (me.componentLayout.layoutBusy !== true) {
343                     me.doComponentLayout();
344                     if (me.componentLayout.layoutCancelled === true) {
345                         layout.layout();
346                     }
347                 }
348             }
349             // Both dimensions set, either by configuration, or by an owning layout, run a ContainerLayout
350             else {
351                 // Only run the ContainerLayout if it is not already in progress
352                 if (layout.layoutBusy !== true) {
353                     layout.layout();
354                 }
355             }
356         }
357
358         return me;
359     },
360
361     // @private
362     afterLayout : function(layout) {
363         ++this.layoutCounter;
364         this.fireEvent('afterlayout', this, layout);
365     },
366
367     // @private
368     prepareItems : function(items, applyDefaults) {
369         if (!Ext.isArray(items)) {
370             items = [items];
371         }
372
373         // Make sure defaults are applied and item is initialized
374         var i = 0,
375             len = items.length,
376             item;
377
378         for (; i < len; i++) {
379             item = items[i];
380             if (applyDefaults) {
381                 item = this.applyDefaults(item);
382             }
383             items[i] = this.lookupComponent(item);
384         }
385         return items;
386     },
387
388     // @private
389     applyDefaults : function(config) {
390         var defaults = this.defaults;
391
392         if (defaults) {
393             if (Ext.isFunction(defaults)) {
394                 defaults = defaults.call(this, config);
395             }
396
397             if (Ext.isString(config)) {
398                 config = Ext.ComponentManager.get(config);
399             }
400             Ext.applyIf(config, defaults);
401         }
402
403         return config;
404     },
405
406     // @private
407     lookupComponent : function(comp) {
408         return Ext.isString(comp) ? Ext.ComponentManager.get(comp) : this.createComponent(comp);
409     },
410
411     // @private
412     createComponent : function(config, defaultType) {
413         // // add in ownerCt at creation time but then immediately
414         // // remove so that onBeforeAdd can handle it
415         // var component = Ext.create(Ext.apply({ownerCt: this}, config), defaultType || this.defaultType);
416         //
417         // delete component.initialConfig.ownerCt;
418         // delete component.ownerCt;
419
420         return Ext.ComponentManager.create(config, defaultType || this.defaultType);
421     },
422
423     // @private - used as the key lookup function for the items collection
424     getComponentId : function(comp) {
425         return comp.getItemId();
426     },
427
428     /**
429
430 Adds {@link Ext.Component Component}(s) to this Container.
431
432 ##Description:##
433
434 - Fires the {@link #beforeadd} event before adding.
435 - The Container's {@link #defaults default config values} will be applied
436   accordingly (see `{@link #defaults}` for details).
437 - Fires the `{@link #add}` event after the component has been added.
438
439 ##Notes:##
440
441 If the Container is __already rendered__ when `add`
442 is called, it will render the newly added Component into its content area.
443
444 __**If**__ the Container was configured with a size-managing {@link #layout} manager, the Container
445 will recalculate its internal layout at this time too.
446
447 Note that the default layout manager simply renders child Components sequentially into the content area and thereafter performs no sizing.
448
449 If adding multiple new child Components, pass them as an array to the `add` method, so that only one layout recalculation is performed.
450
451     tb = new {@link Ext.toolbar.Toolbar}({
452         renderTo: document.body
453     });  // toolbar is rendered
454     tb.add([{text:'Button 1'}, {text:'Button 2'}]); // add multiple items. ({@link #defaultType} for {@link Ext.toolbar.Toolbar Toolbar} is 'button')
455
456 ##Warning:##
457
458 Components directly managed by the BorderLayout layout manager
459 may not be removed or added.  See the Notes for {@link Ext.layout.container.Border BorderLayout}
460 for more details.
461
462      * @param {Ext.Component[]/Ext.Component...} component
463      * Either one or more Components to add or an Array of Components to add.
464      * See `{@link #items}` for additional information.
465      *
466      * @return {Ext.Component[]/Ext.Component} The Components that were added.
467      * @markdown
468      */
469     add : function() {
470         var me = this,
471             args = Array.prototype.slice.call(arguments),
472             hasMultipleArgs,
473             items,
474             results = [],
475             i,
476             ln,
477             item,
478             index = -1,
479             cmp;
480
481         if (typeof args[0] == 'number') {
482             index = args.shift();
483         }
484
485         hasMultipleArgs = args.length > 1;
486         if (hasMultipleArgs || Ext.isArray(args[0])) {
487
488             items = hasMultipleArgs ? args : args[0];
489             // Suspend Layouts while we add multiple items to the container
490             me.suspendLayout = true;
491             for (i = 0, ln = items.length; i < ln; i++) {
492                 item = items[i];
493
494                 //<debug>
495                 if (!item) {
496                     Ext.Error.raise("Trying to add a null item as a child of Container with itemId/id: " + me.getItemId());
497                 }
498                 //</debug>
499
500                 if (index != -1) {
501                     item = me.add(index + i, item);
502                 } else {
503                     item = me.add(item);
504                 }
505                 results.push(item);
506             }
507             // Resume Layouts now that all items have been added and do a single layout for all the items just added
508             me.suspendLayout = false;
509             me.doLayout();
510             return results;
511         }
512
513         cmp = me.prepareItems(args[0], true)[0];
514
515         // Floating Components are not added into the items collection
516         // But they do get an upward ownerCt link so that they can traverse
517         // up to their z-index parent.
518         if (cmp.floating) {
519             cmp.onAdded(me, index);
520         } else {
521             index = (index !== -1) ? index : me.items.length;
522             if (me.fireEvent('beforeadd', me, cmp, index) !== false && me.onBeforeAdd(cmp) !== false) {
523                 me.items.insert(index, cmp);
524                 cmp.onAdded(me, index);
525                 me.onAdd(cmp, index);
526                 me.fireEvent('add', me, cmp, index);
527             }
528             me.doLayout();
529         }
530         return cmp;
531     },
532
533     onAdd : Ext.emptyFn,
534     onRemove : Ext.emptyFn,
535
536     /**
537      * Inserts a Component into this Container at a specified index. Fires the
538      * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the
539      * Component has been inserted.
540      * @param {Number} index The index at which the Component will be inserted
541      * into the Container's items collection
542      * @param {Ext.Component} component The child Component to insert.<br><br>
543      * Ext uses lazy rendering, and will only render the inserted Component should
544      * it become necessary.<br><br>
545      * A Component config object may be passed in order to avoid the overhead of
546      * constructing a real Component object if lazy rendering might mean that the
547      * inserted Component will not be rendered immediately. To take advantage of
548      * this 'lazy instantiation', set the {@link Ext.Component#xtype} config
549      * property to the registered type of the Component wanted.<br><br>
550      * For a list of all available xtypes, see {@link Ext.Component}.
551      * @return {Ext.Component} component The Component (or config object) that was
552      * inserted with the Container's default config values applied.
553      */
554     insert : function(index, comp) {
555         return this.add(index, comp);
556     },
557
558     /**
559      * Moves a Component within the Container
560      * @param {Number} fromIdx The index the Component you wish to move is currently at.
561      * @param {Number} toIdx The new index for the Component.
562      * @return {Ext.Component} component The Component (or config object) that was moved.
563      */
564     move : function(fromIdx, toIdx) {
565         var items = this.items,
566             item;
567         item = items.removeAt(fromIdx);
568         if (item === false) {
569             return false;
570         }
571         items.insert(toIdx, item);
572         this.doLayout();
573         return item;
574     },
575
576     // @private
577     onBeforeAdd : function(item) {
578         var me = this;
579
580         if (item.ownerCt) {
581             item.ownerCt.remove(item, false);
582         }
583
584         if (me.border === false || me.border === 0) {
585             item.border = (item.border === true);
586         }
587     },
588
589     /**
590      * Removes a component from this container.  Fires the {@link #beforeremove} event before removing, then fires
591      * the {@link #remove} event after the component has been removed.
592      * @param {Ext.Component/String} component The component reference or id to remove.
593      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
594      * Defaults to the value of this Container's {@link #autoDestroy} config.
595      * @return {Ext.Component} component The Component that was removed.
596      */
597     remove : function(comp, autoDestroy) {
598         var me = this,
599             c = me.getComponent(comp);
600         //<debug>
601             if (Ext.isDefined(Ext.global.console) && !c) {
602                 console.warn("Attempted to remove a component that does not exist. Ext.container.Container: remove takes an argument of the component to remove. cmp.remove() is incorrect usage.");
603             }
604         //</debug>
605
606         if (c && me.fireEvent('beforeremove', me, c) !== false) {
607             me.doRemove(c, autoDestroy);
608             me.fireEvent('remove', me, c);
609         }
610
611         return c;
612     },
613
614     // @private
615     doRemove : function(component, autoDestroy) {
616         var me = this,
617             layout = me.layout,
618             hasLayout = layout && me.rendered;
619
620         me.items.remove(component);
621         component.onRemoved();
622
623         if (hasLayout) {
624             layout.onRemove(component);
625         }
626
627         me.onRemove(component, autoDestroy);
628
629         if (autoDestroy === true || (autoDestroy !== false && me.autoDestroy)) {
630             component.destroy();
631         }
632
633         if (hasLayout && !autoDestroy) {
634             layout.afterRemove(component);
635         }
636
637         if (!me.destroying) {
638             me.doLayout();
639         }
640     },
641
642     /**
643      * Removes all components from this container.
644      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
645      * Defaults to the value of this Container's {@link #autoDestroy} config.
646      * @return {Ext.Component[]} Array of the destroyed components
647      */
648     removeAll : function(autoDestroy) {
649         var me = this,
650             removeItems = me.items.items.slice(),
651             items = [],
652             i = 0,
653             len = removeItems.length,
654             item;
655
656         // Suspend Layouts while we remove multiple items from the container
657         me.suspendLayout = true;
658         for (; i < len; i++) {
659             item = removeItems[i];
660             me.remove(item, autoDestroy);
661
662             if (item.ownerCt !== me) {
663                 items.push(item);
664             }
665         }
666
667         // Resume Layouts now that all items have been removed and do a single layout (if we removed anything!)
668         me.suspendLayout = false;
669         if (len) {
670             me.doLayout();
671         }
672         return items;
673     },
674
675     // Used by ComponentQuery to retrieve all of the items
676     // which can potentially be considered a child of this Container.
677     // This should be overriden by components which have child items
678     // that are not contained in items. For example dockedItems, menu, etc
679     // IMPORTANT note for maintainers:
680     //  Items are returned in tree traversal order. Each item is appended to the result array
681     //  followed by the results of that child's getRefItems call.
682     //  Floating child items are appended after internal child items.
683     getRefItems : function(deep) {
684         var me = this,
685             items = me.items.items,
686             len = items.length,
687             i = 0,
688             item,
689             result = [];
690
691         for (; i < len; i++) {
692             item = items[i];
693             result.push(item);
694             if (deep && item.getRefItems) {
695                 result.push.apply(result, item.getRefItems(true));
696             }
697         }
698
699         // Append floating items to the list.
700         // These will only be present after they are rendered.
701         if (me.floatingItems && me.floatingItems.accessList) {
702             result.push.apply(result, me.floatingItems.accessList);
703         }
704
705         return result;
706     },
707
708     /**
709      * Cascades down the component/container heirarchy from this component (passed in the first call), calling the specified function with
710      * each component. The scope (<code>this</code> reference) of the
711      * function call will be the scope provided or the current component. The arguments to the function
712      * will be the args provided or the current component. If the function returns false at any point,
713      * the cascade is stopped on that branch.
714      * @param {Function} fn The function to call
715      * @param {Object} [scope] The scope of the function (defaults to current component)
716      * @param {Array} [args] The args to call the function with. The current component always passed as the last argument.
717      * @return {Ext.Container} this
718      */
719     cascade : function(fn, scope, origArgs){
720         var me = this,
721             cs = me.items ? me.items.items : [],
722             len = cs.length,
723             i = 0,
724             c,
725             args = origArgs ? origArgs.concat(me) : [me],
726             componentIndex = args.length - 1;
727
728         if (fn.apply(scope || me, args) !== false) {
729             for(; i < len; i++){
730                 c = cs[i];
731                 if (c.cascade) {
732                     c.cascade(fn, scope, origArgs);
733                 } else {
734                     args[componentIndex] = c;
735                     fn.apply(scope || cs, args);
736                 }
737             }
738         }
739         return this;
740     },
741
742     /**
743      * Examines this container's <code>{@link #items}</code> <b>property</b>
744      * and gets a direct child component of this container.
745      * @param {String/Number} comp This parameter may be any of the following:
746      * <div><ul class="mdetail-params">
747      * <li>a <b><code>String</code></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
748      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
749      * <li>a <b><code>Number</code></b> : representing the position of the child component
750      * within the <code>{@link #items}</code> <b>property</b></li>
751      * </ul></div>
752      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
753      * @return Ext.Component The component (if found).
754      */
755     getComponent : function(comp) {
756         if (Ext.isObject(comp)) {
757             comp = comp.getItemId();
758         }
759
760         return this.items.get(comp);
761     },
762
763     /**
764      * Retrieves all descendant components which match the passed selector.
765      * Executes an Ext.ComponentQuery.query using this container as its root.
766      * @param {String} selector (optional) Selector complying to an Ext.ComponentQuery selector.
767      * If no selector is specified all items will be returned.
768      * @return {Ext.Component[]} Components which matched the selector
769      */
770     query : function(selector) {
771         selector = selector || '*';
772         return Ext.ComponentQuery.query(selector, this);
773     },
774
775     /**
776      * Retrieves the first direct child of this container which matches the passed selector.
777      * The passed in selector must comply with an Ext.ComponentQuery selector.
778      * @param {String} selector (optional) An Ext.ComponentQuery selector. If no selector is
779      * specified, the first child will be returned.
780      * @return Ext.Component
781      */
782     child : function(selector) {
783         selector = selector || '';
784         return this.query('> ' + selector)[0] || null;
785     },
786
787     /**
788      * Retrieves the first descendant of this container which matches the passed selector.
789      * The passed in selector must comply with an Ext.ComponentQuery selector.
790      * @param {String} selector (optional) An Ext.ComponentQuery selector. If no selector is
791      * specified, the first child will be returned.
792      * @return Ext.Component
793      */
794     down : function(selector) {
795         return this.query(selector)[0] || null;
796     },
797
798     // inherit docs
799     show : function() {
800         this.callParent(arguments);
801         this.performDeferredLayouts();
802         return this;
803     },
804
805     // Lay out any descendant containers who queued a layout operation during the time this was hidden
806     // This is also called by Panel after it expands because descendants of a collapsed Panel allso queue any layout ops.
807     performDeferredLayouts: function() {
808         var layoutCollection = this.layoutOnShow,
809             ln = layoutCollection.getCount(),
810             i = 0,
811             needsLayout,
812             item;
813
814         for (; i < ln; i++) {
815             item = layoutCollection.get(i);
816             needsLayout = item.needsLayout;
817
818             if (Ext.isObject(needsLayout)) {
819                 item.doComponentLayout(needsLayout.width, needsLayout.height, needsLayout.isSetSize, needsLayout.ownerCt);
820             }
821         }
822         layoutCollection.clear();
823     },
824
825     //@private
826     // Enable all immediate children that was previously disabled
827     onEnable: function() {
828         Ext.Array.each(this.query('[isFormField]'), function(item) {
829             if (item.resetDisable) {
830                 item.enable();
831                 delete item.resetDisable;
832             }
833         });
834         this.callParent();
835     },
836
837     // @private
838     // Disable all immediate children that was previously disabled
839     onDisable: function() {
840         Ext.Array.each(this.query('[isFormField]'), function(item) {
841             if (item.resetDisable !== false && !item.disabled) {
842                 item.disable();
843                 item.resetDisable = true;
844             }
845         });
846         this.callParent();
847     },
848
849     /**
850      * Occurs before componentLayout is run. Returning false from this method will prevent the containerLayout
851      * from being executed.
852      */
853     beforeLayout: function() {
854         return true;
855     },
856
857     // @private
858     beforeDestroy : function() {
859         var me = this,
860             items = me.items,
861             c;
862
863         if (items) {
864             while ((c = items.first())) {
865                 me.doRemove(c, true);
866             }
867         }
868
869         Ext.destroy(
870             me.layout
871         );
872         me.callParent();
873     }
874 });
875