/**
 * A Container has all of the abilities of {@link Ext.Component Component}, but lets you nest other
 * Components inside it. Applications are made up of lots of components, usually nested inside one
 * another. Containers allow you to render and arrange child Components inside them. Most apps have
 * a single top-level Container called a Viewport, which takes up the entire screen. Inside of this
 * are child components, for example in a mail app the Viewport Container's two children might be a
 * message List and an email preview pane.
 *
 * Containers give the following extra functionality:
 *
 * - Adding child Components at instantiation and run time
 * - Removing child Components
 * - Specifying a Layout
 *
 * Layouts determine how the child Components should be laid out on the screen. In our mail app
 * example we'd use an HBox layout so that we can pin the email list to the left hand edge of the
 * screen and allow the preview pane to occupy the rest. There are several layouts, each of which
 * help you achieve your desired application structure.
 *
 * ## Adding Components to Containers
 *
 * As we mentioned above, Containers are special Components that can have child Components arranged
 * by a Layout. One of the code samples above showed how to create a Panel with 2 child Panels
 * already defined inside it but it's easy to do this at run time too:
 *
 *     @example
 *     var mainPanel = Ext.create({
 *         xtype: 'panel',
 *         fullscreen: true,
 *         layout: 'hbox',
 *         defaults: {
 *             flex: 1
 *         },
 *         items: [{
 *             html: 'First Panel',
 *             style: 'background-color: #5E99CC'
 *         }]
 *     });
 *
 *     mainPanel.add({
 *         xtype: 'panel',
 *         html: 'About this App'
 *     });
 *
 * Here we created three Panels in total. First we create mainPanel, which
 * already contains another Panel in its {@link Ext.Container#cfg-items items}
 * configuration, with some dummy text ("First Panel"). Finally, we add the third
 * panel to the second by calling the {@link Ext.Container#method-add add} method on `mainPanel`.
 *
 * In this case we gave our mainPanel another hbox layout, but we also introduced some
 * {@link Ext.Container#defaults defaults}. These are applied to every item in the Panel, so in
 * this case every child inside `mainPanel` will be given a `flex: 1` configuration. The effect of
 * this is that when we first render the screen only a single child is present inside `mainPanel`,
 * so that child takes up the full width available to it. Once the `mainPanel.add` line is called
 * though, the `aboutPanel` is rendered inside of it and also given a `flex` of 1, which will
 * cause it and the first panel to both receive half the full width of the `mainPanel`.
 *
 * Likewise, it's easy to remove items from a Container:
 *
 *     mainPanel.remove(aboutPanel);
 *
 * After this line is run everything is back to how it was, with the first child panel once again
 * taking up the full width inside `mainPanel`.
 */
Ext.define('Ext.Container', {
    extend: 'Ext.Component',
 
    alternateClassName: ['Ext.lib.Container', 'Ext.container.Container'],
 
    requires: [
        'Ext.util.ItemCollection'
    ],
 
    xtype: 'container',
    isContainer: true,
 
    mixins: [
        'Ext.mixin.Queryable',
        'Ext.mixin.Container',
        'Ext.mixin.FocusableContainer'
    ],
 
    uses: [
        // This is a uses here because layout.Auto requires container, to ensure
        // component styling rules come in earlier than layout rules and we don't
        // want to create a circular dependency
        'Ext.layout.Auto'
    ],
 
    /**
     * @event add
     * Fires whenever item added to the Container.
     * @param {Ext.Container} this The Container instance.
     * @param {Object} item The item added to the Container.
     * @param {Number} index The index of the item within the Container.
     */
 
    /**
     * @event remove
     * Fires whenever item removed from the Container.
     * @param {Ext.Container} this The Container instance.
     * @param {Object} item The item removed from the Container.
     * @param {Number} index The index of the item that was removed.
     */
 
    /**
     * @event move
     * Fires whenever item moved within the Container.
     * @param {Ext.Container} this The Container instance.
     * @param {Object} item The item moved within the Container.
     * @param {Number} toIndex The new index of the item.
     * @param {Number} fromIndex The old index of the item.
     */
 
    /**
     * @private
     * @event renderedchange
     * Fires whenever an item is rendered into a container or derendered
     * from a Container.
     * @param {Ext.Container} this The Container instance.
     * @param {Object} item The item in the Container.
     * @param {Boolean} rendered The current rendered status of the item.
     */
 
    /**
     * @event activate
     * Fires whenever item within the Container is activated.
     * @param {Object} newActiveItem The new active item within the container.
     * @param {Ext.Container} this The Container instance.
     * @param {Object} oldActiveItem The old active item within the container.
     */
 
    /**
     * @event deactivate
     * Fires whenever item within the Container is deactivated.
     * @param {Object} oldActiveItem The old active item within the container.
     * @param {Ext.Container} this The Container instance.
     * @param {Object} newActiveItem The new active item within the container.
     */
 
    eventedConfig: {
        /**
         * @cfg {Ext.Component/Object/String/Number} activeItem The item from the {@link #cfg-items}
         * collection that will be active first. This is usually only meaningful in a
         * {@link Ext.layout.Card card layout}, where only one item can be active at a time. If
         * passed a string, it will be assumed to be a {@link Ext.ComponentQuery} selector. A number
         * will reference an index or a {@link Ext.Component Component} instance may be passed as
         * well. An object config will be created as a new component.
         * @accessor
         * @evented
         */
        activeItem: 0
    },
 
    config: {
        activeItemIndex: null,
 
        /**
         * @cfg {Boolean} [autoSize=true]
         * May be set to `false` for improved layout performance if auto-sizing is not required.
         *
         * Some versions of Safari, both desktop and mobile, have very slow performance
         * if the application has deeply nested containers due to the following WebKit
         * bug: https://bugs.webkit.org/show_bug.cgi?id=150445
         *
         * Applications that experience performance issues in the affected versions of
         * Safari may need to turn off autoSizing globally for all `Ext.Container` instances
         * by placing the following override in the application's "overrides" directory:
         *
         *     Ext.define('MyApp.overrides.Container', {
         *         override: 'Ext.Container',
         *         config: {
         *             autoSize: false
         *         }
         *     });
         *
         * Once auto-sizing support has turned off by default, it can be selectively
         * turned back on only on those container instances that explicitly need auto-sizing
         * behavior by setting `autoSize` to `true`.
         *
         * This option can also be used to allow items to be sized in percentage
         * units as a workaround for the following browser bug:
         * https://bugs.webkit.org/show_bug.cgi?id=137730
         *
         * To illustrate, the following example should render a 200px by 200px green box
         * (the container) with a yellow box inside of it (the child item).  The child
         * item's height and width are both set to `'50%'` so the child should render
         * exactly 100px by 100px in size.
         *
         *     @example
         *     Ext.create({
         *         xtype: 'container',
         *         renderTo: Ext.getBody(),
         *         height: 200,
         *         width: 200,
         *         style: 'background: green',
         *         items: [{
         *             xtype: 'component',
         *             style: 'background: yellow',
         *             height: '50%',
         *             width: '50%'
         *         }]
         *     });
         *
         * All browsers except for Safari render the previous example correctly, but
         * Safari does not assign a height to the component.  To make percentage-sized
         * items work in Safari, simply set `autoSize` to `false` on the container.
         *
         * Since the underlying implementation works by absolutely positioning the container's
         * body element, this option can only be used when the container is not
         * "shrink wrapping" the content in either direction.  When `autoSize` is
         * set to `false`, shrink wrapped dimension(s) will collapse to 0.
         */
        autoSize: null,
 
        /**
         * @cfg {String/Object/Boolean} cardSwitchAnimation
         * Animation to be used during transitions of cards.
         * @removed 2.0.0 Please use {@link Ext.layout.Card#animation} instead
         */
 
        // @cmd-auto-dependency { aliasPrefix : "layout."}
        /**
         * @cfg {Object/String} layout Configuration for this Container's layout. Example:
         *
         *     @example
         *     Ext.create({
         *         xtype: 'container',
         *         layout: {
         *             type: 'hbox',
         *             align: 'middle'
         *         },
         *         items: [{
         *             xtype: 'panel',
         *             flex: 1,
         *             bodyStyle: {
         *                 background: "#000",
         *                 color:"#fff"
         *             }
         *         }, {
         *            xtype: 'panel',
         *            flex: 2,
         *            bodyStyle: {
         *                background: "#f00",
         *                color:"#fff"
         *            }
         *         }]
         *     });
         *
         * @accessor
         */
        layout: 'auto',
 
        /**
         * @cfg {Object} control Enables you to easily control Components inside this Container by
         * listening to their events and taking some action. For example, if we had a container with
         * a nested Disable button, and we wanted to hide the Container when the Disable button is
         * tapped, we could do this:
         *
         *     @example
         *     Ext.create({
         *         xtype: 'container',
         *         control: {
         *            'button[text=Disable]': {
         *                tap: 'hideMe'
         *            }
         *         },
         *
         *         hideMe: function() {
         *             this.hide();
         *         }
         *     });
         *
         * We used a {@link Ext.ComponentQuery} selector to listen to the {@link Ext.Button#tap tap}
         * event on any {@link Ext.Button button} anywhere inside the Container that has the
         * {@link Ext.Button#text text} 'Disable'. Whenever a Component matching that selector
         * fires the `tap` event our `hideMe` function is called. `hideMe` is called with scope:
         * `this` (e.g. `this` is the Container instance).
         *
         */
        control: null,
 
        /**
         * @cfg {Object} defaults A set of default configurations to apply to all child Components
         * in this Container. It's often useful to specify defaults when creating more than one
         * items with similar configurations. For example here we can specify that each child is a
         * panel and avoid repeating the xtype declaration for each one:
         *
         *     @example
         *     Ext.create({
         *         xtype: 'container',
         *         defaults: {
         *             xtype: 'panel'
         *         },
         *         items: [
         *             {
         *                 html: 'Panel 1'
         *             },
         *             {
         *                 html: 'Panel 2'
         *             }
         *         ]
         *     });
         *
         * @accessor
         */
        defaults: null,
 
        // eslint-disable-next-line max-len
        // @cmd-auto-dependency { aliasPrefix: "widget.", typeProperty: "xtype", defaultTypeProperty: "defaultType", defaultsProperty: "defaults" }
        /**
         * @cfg {Array/Object} items The child items to add to this Container. This is usually an
         * array of Component configurations or instances, for example:
         *
         *     @example
         *     Ext.create({
         *         xtype: 'container',
         *         items: [{
         *             xtype: 'panel',
         *             html: 'This is an item'
         *         }]
         *     });
         *
         * This may also be specified as an object, the property names of which are `itemId`s, and
         * the property values are child Component config objects, for example:
         *
         *     @example
         *     Ext.create({
         *         xtype: 'tabpanel',
         *         items: {
         *             panel1: {
         *                 xtype: 'panel',
         *                 title: 'First panel'
         *             },
         *             panel2: {
         *                 xtype: 'panel',
         *                 title: 'Second panel'
         *             }
         *         }
         *     });
         *
         * @accessor
         */
        items: null,
 
        /**
         * @cfg {Boolean} autoDestroy
         * If `true`, child items will be destroyed as soon as they are
         * {@link #method-remove removed} from this container.
         * @accessor
         */
        autoDestroy: true,
 
        /**
         * @cfg {String} [defaultType=container]
         * 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.
         * @accessor
         */
        defaultType: null,
 
        /**
         * @cfg {String} defaultFocus
         *
         * Specifies a child Component to receive focus when this Container's {@link #method-focus}
         * method is called. Should be a valid {@link Ext.ComponentQuery query} selector.
         */
        defaultFocus: {
            $value: null,
            lazy: true
        },
 
        /**
         * @cfg {String} innerCls
         * A string to add to the immediate parent element of the inner items of this
         * container. That is, items that are not `docked`, `positioned` or `floated`. In
         * some containers, `positioned` items may be in this same element.
         * @since 6.5.0
         */
        innerCls: null,
 
        // @cmd-auto-dependency {defaultType: "Ext.Mask"}
        /**
         * @cfg {Boolean/String/Object/Ext.Mask/Ext.LoadMask} masked
         * A configuration to allow you to mask this container.
         * 
         * If the value is a string, it will be used as the message config for an
         * {@link Ext.LoadMask}.
         * 
         * For more precise control over the mask, you can optionally pass an object block with
         * and xtype of `loadmask`, and an optional `message` value to display a loading mask.
         * Please refer to the {@link Ext.LoadMask} component to see other configurations.
         *
         *     @example
         *     Ext.create({
         *         xtype: 'container',
         *         fullscreen: true,
         *         html: 'Hello World',
         *         masked: {
         *             xtype: 'loadmask',
         *             message: 'My Message'
         *         }
         *     });
         *
         * Alternatively, you can just call the setter at any time with `true`/`false` to show/hide
         * the mask:
         *
         *     setMasked(true); //show the mask
         *     setMasked(false); //hides the mask
         *
         * There are also two convenient methods, {@link #method-mask} and {@link #unmask}, to allow
         * you to mask and unmask this container at any time.
         *
         * Remember, the {@link Ext.Viewport} is always a container, so if you want to mask your
         * whole application at anytime, can call:
         *
         *     Ext.Viewport.setMasked({
         *         xtype: 'loadmask',
         *         message: 'Hello'
         *     });
         *
         * @accessor
         */
        masked: null
    },
 
    /**
     * @cfg {Boolean} [weighted=false]
     * If set to `true`, then child {@link #cfg!items} may be specified as a object,
     * with each property name specifying an {@link #cfg!itemId}, and the property
     * value being the child item configuration object.
     *
     * When using this scheme, each child item may contain a {@link #cfg!weight}
     * configuration value which affects its order in this container. Lower weights
     * are towards the start, higher weights towards the end.
     */
    weighted: false,
 
    /**
     * @cfg {Boolean} 
     * @protected
     * `true` to enable border management of docked items.  When enabled, borders of docked
     * items will collapse where they meet to avoid duplicated borders.
     */
    manageBorders: false,
 
    classCls: Ext.baseCSSPrefix + 'container',
 
    managedBordersCls: Ext.baseCSSPrefix + 'managed-borders',
 
    template: [{
        reference: 'bodyElement',
        cls: Ext.baseCSSPrefix + 'body-el',
        uiCls: 'body-el'
    }],
 
    constructor: function(config) {
        var me = this;
 
        me._items = me.items = new Ext.util.ItemCollection();
        me.innerItems = [];
 
        me.getReferences = me.getFirstReferences;
        me.onItemAdd = me.onFirstItemAdd;
 
        me.callParent(arguments);
 
        delete me.getReferences;
    },
 
    initialize: function() {
        var me = this;
 
        me.reference = me.setupReference(me.reference);
        me.callParent();
 
        if (me.manageBorders) {
            me.addCls(me.managedBordersCls);
        }
 
        // Ensure the container's layout instance is created, even if the container
        // has no items.  This ensures border management is handled correctly on empty
        // panels.
        me.getLayout();
    },
 
    /**
     * Changes the {@link #masked} configuration when its setter is called, which will convert the
     * value into a proper object/instance of {@link Ext.Mask}/{@link Ext.LoadMask}. If a mask
     * already exists, it will use that instead.
     * @param {Boolean/Object/String/Ext.Mask/Ext.LoadMask} masked
     * @return {Object} 
     */
    applyMasked: function(masked) {
        var isVisible = true,
            currentMask;
 
        if (masked === false) {
            masked = true;
            isVisible = false;
        }
        
        if (Ext.isString(masked)) {
            masked = {
                xtype: 'loadmask',
                message: masked
            };
        }
 
        // Subscript notation is used to reference Ext.Mask to prevent creation of an
        // auto-dependency
        // eslint-disable-next-line dot-notation
        currentMask = Ext.factory(masked, Ext['Mask'], this.getMasked());
 
        if (currentMask) {
            currentMask.setHidden(!isVisible);
 
            // TODO: Reliable render pathway and rendered transition.
            // was: this.el.append(currentMask.el);
            currentMask.render(this.el);
        }
 
        return currentMask;
    },
 
    /**
     * Convenience method which calls {@link #setMasked} with a value of `true` (to show the mask).
     * For additional functionality, call the {@link #setMasked} function direction (See the
     * {@link #masked} configuration documentation for more information).
     */
    mask: function(mask) {
        this.setMasked(mask || true);
    },
 
    /**
     * Convenience method which calls {@link #setMasked} with a value of false (to hide the mask).
     * For additional functionality, call the {@link #setMasked} function direction (See the
     * {@link #masked} configuration documentation for more information).
     */
    unmask: function() {
        this.setMasked(false);
    },
 
    initInheritedState: function(inheritedState, inheritedStateInner) {
        this.callParent([inheritedState, inheritedStateInner]);
        this.initContainerInheritedState(inheritedState, inheritedStateInner);
    },
 
    onAdded: function(parent, instanced) {
        this.callParent([parent, instanced]);
 
        this.containerOnAdded(parent, instanced);
    },
 
    onRemoved: function(destroying) {
        this.containerOnRemoved(destroying);
        this.callParent([destroying]);
    },
 
    afterItemShow: function(item) {
        var layout;
 
        if (item.getDocked()) {
            layout = this.getLayout();
            this.items.generation++;
            layout.handleDockedItemBorders();
        }
    },
 
    afterItemHide: function(item) {
        var layout;
 
        if (item.getDocked()) {
            layout = this.getLayout();
            this.items.generation++;
            layout.handleDockedItemBorders();
        }
    },
 
    applyItems: function(items, collection) {
        var me = this,
            activeItem;
 
        if (items) {
            me.getDefaultType();
            me.getDefaults();
 
            if (me.initialized && collection.length > 0) {
                me.removeAll();
            }
 
            // Read items from object properties back into the newItems array
            // unless the item is a Widget or is a config object with an xtype.
            if (me.weighted && !items.isWidget && !items.xtype) {
                items = Ext.convertKeyedItems(items);
            }
 
            me.add(items);
 
            // Don't need to call setActiveItem when Container is first initialized
            if (me.initialized) {
                activeItem = me.initialConfig.activeItem || me.config.activeItem || 0;
 
                me.setActiveItem(activeItem);
            }
        }
    },
 
    /**
     * @private
     */
    applyControl: function(selectors) {
        var selector, key, listener, listeners;
 
        for (selector in selectors) {
            listeners = selectors[selector];
 
            for (key in listeners) {
                listener = listeners[key];
 
                if (Ext.isObject(listener)) {
                    listener.delegate = selector;
                }
            }
 
            listeners.delegate = selector;
 
            this.addListener(listeners);
        }
 
        return selectors;
    },
 
    updateDisabled: function(disabled) {
        var me = this;
 
        me.callParent([disabled]);
 
        if (me.focusableContainer) {
            me.getItems();
 
            if (disabled) {
                me.element.saveTabbableState();
            }
            else {
                me.element.restoreTabbableState();
            }
 
            me.activateFocusableContainer(!disabled);
 
            if (!disabled) {
                me.initDefaultFocusable();
            }
        }
    },
 
    /**
     * Initialize layout and event listeners the very first time an item is added
     * @private
     */
    onFirstItemAdd: function(item) {
        var me = this;
 
        delete me.onItemAdd;
 
        if (item.isInner && me.innerHtmlElement && !me.getHtml() && !me.getTpl()) {
            me.innerHtmlElement.destroy();
            delete me.innerHtmlElement;
        }
 
        return me.onItemAdd.apply(me, arguments);
    },
 
    applyLayout: function(layout, oldLayout) {
        if (typeof layout === 'string') {
            layout = {
                type: layout
            };
        }
 
        if (oldLayout) {
            if (layout) {
                if (!layout.isLayout) {
                    oldLayout.setConfig(layout);
                }
                //<debug>
                else {
                    Ext.raise('Cannot change layout instances on ' + this.$className);
                }
                //</debug>
            }
 
            return oldLayout;
        }
 
        // Container has to be stamped into the layout as soon as its created.
        if (!(layout && layout.isLayout)) {
            layout = Ext.Factory.layout(Ext.apply({
                container: this
            }, layout), Ext.layout.Auto);
        }
 
        this.link('layout', layout);
 
        return layout;
    },
 
    updateDefaultType: function(defaultType) {
        // Cache the direct reference to the default item class here for performance
        this.defaultItemClass = Ext.ClassManager.getByAlias('widget.' + defaultType);
 
        //<debug>
        if (!this.defaultItemClass) {
            Ext.Logger.error(
                "Invalid defaultType of: '" + defaultType + "', must be a valid component xtype");
        }
        //</debug>
    },
 
    /**
     * Called when an item is added to this container either during initialization of the
     * {@link #cfg-items} config, or when new items are {@link #method-add added), or
     * {@link #method-insert inserted}.
     *
     * If the passed object is *not* an instanced component, it converts the passed object into an
     * instanced child component.
     *
     * It applies {@link #cfg-defaults} applied for contained child items - that is items
     * which are not positiond using {@link Ext.Component#cfg-left left},
     * {@link Ext.Component#cfg-top top}{@link Ext.Component#cfg-bottom bottom},
     * {@link Ext.Component#cfg-right right}{@link Ext.Component#cfg-centered centered} or
     * {@link Ext.Component#cfg-docked docked}.
     *
     * Derived classes can override this method to process context appropriate short-hands
     * such as {@link Ext.Toolbar} and "->" to insert a spacer.
     *
     * @param {Mixed} item The item being added. May be a raw config object or an instanced
     * Component or some other short-hand understood by the container.
     * @return {Ext.Component} The component to be added.
     * @protected
     */
    factoryItem: function(item) {
        var me = this;
 
        //<debug>
        if (!item) {
            Ext.Logger.error(
                "Invalid item given: " + item + ", must be either the config object to factory a " +
                "new item, or an existing component instance");
        }
        //</debug>
 
        item = me.applyItemDefaults(item);
 
        if (!item.isComponent) {
            // This forces default type to be resolved prior to any other configs that
            // may be using it to create children
            if (!me.$hasCachedDefaultItemClass) {
                me.getDefaultType();
                me.$hasCachedDefaultItemClass = true;
            }
 
            item = Ext.factory(item, me.defaultItemClass);
        }
 
        return item;
    },
 
    /**
     * Adds one or more Components to this Container. Example:
     *
     *     var myPanel = Ext.create({
     *         xtype: 'panel',
     *         html : 'This will be added to a Container'
     *     });
     *
     *     var items = myContainer.add([myPanel]); // Array returned
     *     var item  = myContainer.add(myPanel);   // One item is returned
     *
     * @param {Object/Object[]/Ext.Component/Ext.Component[]} newItems The new item(s) to add
     * to the Container. Note that if an array of items to add was passed in, an array of added
     * items will be returned as well even if there was only one item.
     *
     * @return {Ext.Component/Ext.Component[]} The Component(s) that were added.
     */
    add: function(newItems) {
        var me = this,
            items = me.getItems(),
            weighted = me.weighted,
            addingArray = true,
            addedItems = [],
            doWeightedInsert, i, ln, item, instanced;
 
        if (!Ext.isArray(newItems)) {
            newItems = [newItems];
            addingArray = false;
        }
 
        // If we are maintaining child items in weight order, then we only
        // have to do a calculated insert if there are existing items.
        // If no existing items, we can just sort the incoming items
        // and add them in that order.
        if (weighted) {
            if (items.length) {
                doWeightedInsert = true;
            }
            else {
                Ext.Array.sort(newItems, Ext.weightSortFn);
            }
        }
 
        for (= 0, ln = newItems.length; i < ln; i++) {
            item = newItems[i];
 
            if (item) {
                instanced = item.isWidget;
 
                if (!instanced) {
                    item.$initParent = me;
                }
 
                item = me.factoryItem(item);
 
                // If we are a weighted container, and we're not empty, and we're adding multiple
                // items, then insert items according to weighting.
                if (doWeightedInsert) {
                    me.doInsert(items.findInsertionIndex(item, Ext.weightSortFn), item, instanced);
                }
                else {
                    me.doAdd(item, instanced);
                }
 
                delete item.$initParent;
 
                if (me.focusableContainer) {
                    me.onFocusableChildAdd(item);
                }
 
                addedItems.push(item);
            }
            //<debug>
            else if (item !== null) {
                Ext.raise('Invalid item passed to add');
            }
            //</debug>
        }
 
        if ((me.isConfiguring || !me.getActiveItem()) && me.innerItems.length > 0) {
            me.setActiveItem(me.initialConfig.activeItem || 0);
        }
 
        if (me.rendered && ln && me.focusableContainer) {
            me.initFocusableContainer();
        }
 
        return addingArray ? addedItems : addedItems[0];
    },
 
    onItemWeightChange: function(item) {
        var items = this.getItems(),
            oldIndex = items.indexOf(item),
            index;
 
        items.remove(item);
        index = items.findInsertionIndex(item, Ext.weightSortFn);
        items.insert(index, item);
 
        this.insertInner(item, index);
        this.onItemMove(item, index, oldIndex);
    },
 
    /**
     * @private
     * @param {Ext.Component} item 
     * @param {Boolean} instanced 
     * when received.
     */
    doAdd: function(item, instanced) {
        var me = this,
            items = me.getItems(),
            index;
 
        if (!items.has(item)) {
            index = items.length;
            items.add(item);
 
            if (item.isInnerItem()) {
                me.insertInner(item, index);
            }
 
            item.onAdded(me, !!instanced);
 
            if (me.focusableContainer) {
                me.onFocusableChildAdd(item);
            }
 
            me.onItemAdd(item, index);
        }
    },
 
    /**
     * Removes an item from this Container, optionally destroying it.
     * @param {Ext.Component/String/Number/Array} which The component instance, id or
     * index to remove or an array of these.
     * @param {Boolean} [destroy] `true` to automatically call Component's
     * {@link Ext.Component#method-destroy destroy} method.
     *
     * @return {Ext.Component} The Component that was removed.
     */
    remove: function(which, destroy) {
        var me = this,
            component = me.getComponent(which), // fails for []'s
            activeItem, index, innerItems, item, wasActive;
 
        if (destroy === undefined) {
            destroy = me.getAutoDestroy();
        }
 
        if (!component) {
            //<debug>
            if (!Ext.isArray(which)) {
                Ext.raise(
                    'Invalid first argument to Ext.Container#remove() - ',
                    Ext.typeOf(which));
            }
            //</debug>
 
            activeItem = me.getActiveItem();
 
            for (index = 0; index < which.length; ++index) {
                item = me.getComponent(which[index]);
 
                if (item === activeItem) {
                    wasActive = true;
                }
                else if (item) {
                    me.remove(item, destroy);
                }
            }
 
            // If we are removing the activeItem, save it for last
            if (wasActive) {
                me.remove(activeItem, destroy);
            }
 
            return which; // the same array we were given
        }
 
        index = me.indexOf(component);
        innerItems = me.getInnerItems();
 
        if (index !== -1) {
            if (!me.removingAll && innerItems.length > 1 && component === me.getActiveItem()) {
                me.on({
                    activeitemchange: 'doRemove',
                    scope: me,
                    single: true,
                    order: 'after',
                    args: [component, index, destroy]
                });
 
                me.doResetActiveItem(innerItems.indexOf(component));
            }
            else {
                me.doRemove(component, index, destroy);
 
                if (innerItems.length === 0) {
                    me.setActiveItem(null);
                }
            }
        }
 
        return component;
    },
 
    doResetActiveItem: function(innerIndex) {
        if (innerIndex === 0) {
            this.setActiveItem(1);
        }
        else {
            this.setActiveItem(0);
        }
    },
 
    doRemove: function(item, index, destroy) {
        var me = this;
 
        // Don't bother removing from these collections during destroy, since
        // they will just be nulled out
        if (!me.destroying) {
            me.items.remove(item);
 
            if (item.isInnerItem()) {
                me.removeInner(item);
            }
 
            me.onItemRemove(item, index, destroy);
        }
 
        if (!item.destroyed) {
            item.onRemoved(item.destroying || destroy);
        }
 
        if (me.focusableContainer && !me.destroying && !me.destroyed) {
            me.onFocusableChildRemove(item, destroy);
        }
 
        if (destroy && !item.destroyed) {
            item.destroy();
        }
    },
 
    /**
     * Removes all items currently in the Container, optionally destroying them all.
     *
     * @param {Boolean} [destroy] Pass `true` to {@link Ext.Component#method!destroy destroy}
     * each removed Component. Defaults to `autoDestroy`.
     * @param {Boolean} [everything] Pass `true` to completely remove all items including
     * docked, floated and positioned items.
     *
     * @return {Ext.Component[]} The removed components
     */
    removeAll: function(destroy, everything) {
        var me = this,
            destroying = me.destroying,
            items = me.items,
            removed = destroying ? null : [],
            ln = items.length,
            i, item;
 
        if (typeof destroy !== 'boolean') {
            destroy = this.getAutoDestroy();
        }
 
        // removingAll flag is used so we don't unnecessarily change activeItem while
        // removing all items.
        me.removingAll = true;
 
        for (= 0; i < ln; i++) {
            item = items.getAt(i);
 
            if (item && (everything || item.isInnerItem())) {
                me.doRemove(item, i, destroy);
 
                // When we are destroying, the items will not be removed from the collection
                // so the count won't be modified
                if (!destroying) {
                    i--;
                    ln--;
                }
            }
 
            if (removed) {
                removed.push(item);
            }
        }
 
        if (!destroying) {
            me.setActiveItem(null);
        }
 
        me.removingAll = false;
 
        return removed;
    },
 
    /**
     * Returns the Component for a given index in the Container's {@link #property-items}.
     * @param {Number} index The index of the Component to return.
     * @return {Ext.Component} The item at the specified `index`, if found.
     */
    getAt: function(index) {
        return this.items.getAt(index);
    },
 
    getInnerAt: function(index) {
        return this.innerItems[index];
    },
 
    /**
     * Removes the Component at the specified index:
     *
     *     myContainer.removeAt(0); // removes the first item
     *
     * @param {Number} index The index of the Component to remove.
     *
     * @param {Boolean} [destroy] `true` to automatically call Component's
     * {@link Ext.Component#method-destroy destroy} method.
     *
     * @return {Ext.Component} The removed Component
     */
    removeAt: function(index, destroy) {
        var item = this.getAt(index);
 
        if (item) {
            this.remove(item, destroy);
        }
 
        return item;
    },
 
    /**
     * Removes an inner Component at the specified index:
     *
     *     myContainer.removeInnerAt(0); // removes the first item of the innerItems property
     *
     * @param {Number} index The index of the Component to remove.
     * @return {Ext.Component} The removed Component
     */
    removeInnerAt: function(index) {
        var item = this.getInnerItems()[index];
 
        if (item) {
            this.remove(item);
        }
 
        return item;
    },
 
    /**
     * @private
     */
    has: function(item) {
        return this.getItems().indexOf(item) !== -1;
    },
 
    /**
     * @private
     */
    hasInnerItem: function(item) {
        return this.innerItems.indexOf(item) !== -1;
    },
 
    /**
     * @private
     */
    indexOf: function(item) {
        return this.getItems().indexOf(item);
    },
 
    innerIndexOf: function(item) {
        return this.innerItems.indexOf(item);
    },
 
    /**
     * @private
     * @param {Ext.Component} item 
     * @param {Number} index 
     */
    insertInner: function(item, index) {
        var items = this.getItems().items,
            innerItems = this.innerItems,
            currentInnerIndex = innerItems.indexOf(item),
            newInnerIndex = -1,
            nextSibling;
 
        if (currentInnerIndex !== -1) {
            innerItems.splice(currentInnerIndex, 1);
        }
 
        if (typeof index === 'number') {
            do {
                nextSibling = items[++index];
            } while (nextSibling && !nextSibling.isInnerItem());
 
            if (nextSibling) {
                newInnerIndex = innerItems.indexOf(nextSibling);
                innerItems.splice(newInnerIndex, 0, item);
            }
        }
 
        if (newInnerIndex === -1) {
            innerItems.push(item);
            newInnerIndex = innerItems.length - 1;
        }
 
        if (currentInnerIndex !== -1) {
            this.onInnerItemMove(item, newInnerIndex, currentInnerIndex);
        }
 
        return this;
    },
 
    onInnerItemMove: Ext.emptyFn,
 
    /**
     * @private
     * @param {Ext.Component} item 
     */
    removeInner: function(item) {
        Ext.Array.remove(this.innerItems, item);
 
        return this;
    },
 
    /**
     * Adds a child Component at the given index. For example, here's how we can add a new item,
     * making it the first child Component of this Container:
     *
     *     myContainer.insert(0, {xtype: 'panel', html: 'new item'});
     *
     * @param {Number} index The index to insert the Component at.
     * @param {Object} item The Component to insert.
     */
    insert: function(index, item) {
        var me = this,
            instanced,
            i;
 
        //<debug>
        if (typeof index !== 'number') {
            Ext.Logger.error("Invalid index of '" + index + "', must be a valid number");
        }
        //</debug>
 
        if (Ext.isArray(item)) {
            for (= item.length - 1; i >= 0; i--) {
                me.insert(index, item[i]);
            }
 
            return me;
        }
 
        instanced = item.isWidget;
 
        if (!instanced) {
            item.$initParent = me;
        }
 
        item = me.factoryItem(item);
        me.doInsert(index, item, instanced);
        delete item.$initParent;
 
        return item;
    },
 
    /**
     * @private
     * @param {Number} index 
     * @param {Ext.Component} item 
     * @param {Boolean} instanced 
     */
    doInsert: function(index, item, instanced) {
        var me = this,
            items = me.items,
            itemsLength = items.length,
            currentIndex, isInnerItem;
 
        isInnerItem = item.isInnerItem();
 
        if (index > itemsLength) {
            index = itemsLength;
        }
 
        if (items[index - 1] === item) {
            return;
        }
 
        currentIndex = me.indexOf(item);
 
        if (currentIndex !== -1) {
            items.removeAt(currentIndex);
        }
 
        items.insert(index, item);
 
        if (currentIndex === -1) {
            item.onAdded(me, !!instanced);
        }
 
        if (isInnerItem) {
            me.insertInner(item, index);
        }
 
        if (currentIndex !== -1) {
            me.onItemMove(item, index, currentIndex);
        }
        else {
            me.onItemAdd(item, index);
        }
    },
 
    /**
     * @private
     */
    insertFirst: function(item) {
        return this.insert(0, item);
    },
 
    /**
     * @private
     */
    insertLast: function(item) {
        return this.insert(this.getItems().length, item);
    },
 
    /**
     * @private
     */
    insertBefore: function(item, relativeToItem) {
        var items = this.getItems(),
            ret, index, itemIndex;
 
        if (relativeToItem === null) {
            ret = this.add(item);
        }
        else {
            index = items.indexOf(relativeToItem);
 
            //<debug>
            if (index === -1) {
                Ext.raise('Item does not exist in the container');
            }
            //</debug>
 
            itemIndex = items.indexOf(item);
 
            if (itemIndex !== -1 && itemIndex < index) {
                --index;
            }
 
            ret = this.insert(index, item);
        }
 
        return ret;
    },
 
    /**
     * @private
     */
    insertAfter: function(item, relativeToItem) {
        var index = this.indexOf(relativeToItem);
 
        // TODO this is just as buggy as insertBefore was...
        if (index !== -1) {
            this.insert(index + 1, item);
        }
 
        return this;
    },
 
    /**
     * @private
     */
    onItemAdd: function(item, index) {
        var me = this;
 
        me.doItemLayoutAdd(item, index);
 
        if (me.initialized) {
            if (item.hasListeners.added) {
                item.fireEvent('added', item, me, index);
            }
 
            if (me.hasListeners.add) {
                me.fireEvent('add', me, item, index);
            }
        }
    },
 
    doItemLayoutAdd: function(item, index) {
        var layout = this.getLayout();
 
        if (this.rendered && !item.rendered) {
            item.fireAction(
                'renderedchange',
                [this, item, true], 'onItemAdd', layout,
                { args: [item, index] }
            );
        }
        else {
            layout.onItemAdd(item, index);
        }
    },
 
    /**
     * @private
     */
    onItemRemove: function(item, index, destroying) {
        var me = this;
 
        me.doItemLayoutRemove(item, index, destroying);
 
        if (item.hasListeners.removed) {
            item.fireEvent('removed', item, me, index);
        }
 
        if (me.hasListeners.remove) {
            me.fireEvent('remove', me, item, index);
        }
    },
 
    doItemLayoutRemove: function(item, index, destroying) {
        var layout = this.getLayout();
 
        if (item.rendered) {
            item.setRendered(false);
            item.fireAction(
                'renderedchange',
                [this, item, false], 'onItemRemove',
                layout,
                { args: [item, index, destroying] }
            );
        }
        else {
            layout.onItemRemove(item, index, destroying);
        }
    },
 
    /**
     * @private
     */
    onItemMove: function(item, toIndex, fromIndex) {
        var me = this;
 
        me.doItemLayoutMove(item, toIndex, fromIndex);
 
        if (item.hasListeners.moved) {
            item.fireEvent('moved', item, me, toIndex, fromIndex);
        }
 
        if (me.hasListeners.move) {
            me.fireEvent('move', me, item, toIndex, fromIndex);
        }
    },
 
    doItemLayoutMove: function(item, toIndex, fromIndex) {
        this.getLayout().onItemMove(item, toIndex, fromIndex);
    },
 
    onItemInnerStateChange: function(item, isInner) {
        var layout = this.getLayout();
 
        if (isInner) {
            this.insertInner(item, this.items.indexOf(item));
        }
        else {
            this.removeInner(item);
        }
 
        layout.onItemInnerStateChange.apply(layout, arguments);
    },
 
    onItemFloatedChange: function(item, floated) {
        var layout = this.getLayout();
 
        layout.onItemFloatedChange(item, floated);
    },
 
    /**
     * Returns all inner {@link #property-items} of this container. `inner` means that the item is
     * not `docked` or `positioned`.
     * @return {Array} The inner items of this container.
     */
    getInnerItems: function() {
        return this.innerItems;
    },
 
    /**
     * Returns all the {@link Ext.Component#docked} items in this container.
     * @return {Array} The docked items of this container.
     */
    getDockedItems: function() {
        var items = this.getItems().items,
            dockedItems = [],
            ln = items.length,
            item, i;
 
        for (= 0; i < ln; i++) {
            item = items[i];
 
            if (item.isDocked()) {
                dockedItems.push(item);
            }
        }
 
        return dockedItems;
    },
 
    /**
     * @private
     */
    applyActiveItem: function(activeItem, currentActiveItem) {
        var me = this,
            innerItems = me.getInnerItems(),
            initialConfig = me.initialConfig,
            initialActive = initialConfig.activeItem || activeItem,
            item;
 
        // Make sure the items are already initialized
        me.getItems();
 
        if (me.isConfiguring && !initialConfig.activeItem) {
            activeItem = initialActive;
        }
 
        // No items left to be active, reset back to 0 on falsy changes
        if (!activeItem && innerItems.length === 0) {
            return 0;
        }
        else if (typeof activeItem === 'number') {
            activeItem = Math.max(0, Math.min(activeItem, innerItems.length - 1));
            activeItem = innerItems[activeItem];
 
            if (activeItem) {
                return activeItem;
            }
            else if (currentActiveItem) {
                return null;
            }
        }
        else if (activeItem) {
            // ComponentQuery selector?
            if (typeof activeItem === 'string') {
                item = me.child(activeItem);
            }
            else if (activeItem.isComponent) {
                item = activeItem;
            }
            else {
                activeItem = Ext.apply({ $initParent: me }, activeItem);
                item = me.factoryItem(activeItem);
            }
 
            if (!item) {
                return null;
            }
 
            me.pendingActiveItem = item;
 
            //<debug>
            if (!item.isInnerItem()) {
                Ext.Logger.error("Setting activeItem to be a non-inner item");
            }
            //</debug>
 
            if (!me.has(item)) {
                me.add(item);
            }
 
            delete item.$initParent;
 
            return item;
        }
    },
 
    /**
     * Animates to the supplied `activeItem` with a specified animation. Currently this only works
     * with a Card layout.  This passed animation will override any default animations on the
     * container, for a single card switch. The animation will be destroyed when complete.
     * @param {Object/Number} activeItem The item or item index to make active.
     * @param {Object/Ext.layout.card.fx.Abstract} animation Card animation configuration or
     * instance.
     */
    animateActiveItem: function(activeItem, animation) {
        var layout = this.getLayout(),
            defaultAnimation;
 
        if (this.activeItemAnimation) {
            this.activeItemAnimation.destroy();
        }
 
        this.activeItemAnimation = animation = new Ext.Factory.layoutCardFx(animation);
 
        if (animation && layout.isCard) {
            animation.setLayout(layout);
            defaultAnimation = layout.getAnimation();
 
            if (defaultAnimation) {
                defaultAnimation.disable();
            }
 
            animation.on('animationend', function() {
                if (defaultAnimation) {
                    defaultAnimation.enable();
                }
 
                animation.destroy();
            }, this);
        }
 
        return this.setActiveItem(activeItem);
    },
 
    updateActiveItem: function(newActiveItem, oldActiveItem) {
        delete this.pendingActiveItem;
 
        if (oldActiveItem && !oldActiveItem.destroyed) {
            oldActiveItem.fireEvent('deactivate', oldActiveItem, this, newActiveItem);
        }
 
        if (newActiveItem) {
            newActiveItem.fireEvent('activate', newActiveItem, this, oldActiveItem);
        }
 
        this.setActiveItemIndex(this.innerItems.indexOf(newActiveItem));
    },
 
    updateActiveItemIndex: function(index) {
        this.setActiveItem(this.innerItems[index]);
    },
 
    /**
     * Used by ComponentQuery to retrieve all of the items
     * which can potentially be considered a child of this Container.
     * This should be overridden by components which have child items
     * that are not contained in items. For example `dockedItems`, `menu`, etc
     * @private
     */
    getRefItems: function(deep) {
        var items = this.getItems().items,
            result, ln, i, item;
 
        if (items) {
            // If deep, we have to collect descendants in tree walking order.
            if (deep) {
                result = [];
 
                for (= 0, ln = items.length; i < ln; i++) {
                    item = items[i];
                    result[result.length] = item;
 
                    if (item.getRefItems) {
                        result.push.apply(result, item.getRefItems(true));
                    }
                }
            }
            // Not deep, just return a copy of the items array.
            else {
                result = items.slice();
            }
        }
 
        // Subclasses might push items into this array, so use a new empty array when
        // there are no results, not Ext.emptyArray.
        return result || [];
    },
 
    /**
     * Examines this container's `{@link #property-items}` property
     * and gets a direct child component of this container.
     * @param {String/Number} component This parameter may be any of the following:
     *
     * - {String} : representing the `itemId`
     * or `{@link Ext.Component#getId id}` of the child component.
     * - {Number} : representing the position of the child component
     * within the `{@link #property-items}` property.
     *
     * For additional information see {@link Ext.util.MixedCollection#get}.
     * @return {Ext.Component} The component (if found).
     */
    getComponent: function(component) {
        if (typeof component === 'number') {
            return this.getItems().getAt(component);
        }
 
        if (Ext.isObject(component)) {
            component = component.getItemId();
        }
 
        return this.getItems().get(component);
    },
 
    /**
     * Finds a docked item of this container using a reference, `id `or an `index` of its location
     * in {@link #getDockedItems}.
     * @param {String/Number} component The `id` or `index` of the component to find.
     * @return {Ext.Component/Boolean} The docked component, if found.
     */
    getDockedComponent: function(component) {
        var dockedItems, ln, item, i;
 
        if (Ext.isObject(component)) {
            component = component.getItemId();
        }
 
        dockedItems = this.getDockedItems();
        ln = dockedItems.length;
 
        if (Ext.isNumber(component)) {
            return dockedItems[component];
        }
 
        for (= 0; i < ln; i++) {
            item = dockedItems[i];
 
            if (item.id === component) {
                return item;
            }
        }
 
        return false;
    },
 
    doDestroy: function() {
        var me = this;
 
        if (me.focusableContainer) {
            me.destroyFocusableContainer();
        }
 
        me.removeAll(true, true);
        Ext.destroy(
            me.items,
            me.getMasked()
        );
        me.items = null;
 
        // We don't want to create one
        if (me._layout) {
            me._layout = Ext.destroy(me._layout);
        }
 
        me.callParent();
    },
 
    /**
     * @protected
     * Returns the focus holder element associated with this Container.
     * By default, this is the Container's {@link #focusEl} element;
     * however if {@link #cfg!defaultFocus} is defined, the child component
     * referenced by that property will be found and returned instead.
     *
     * @return {Ext.dom.Element} the focus holding element.
     */
    getFocusEl: function() {
        var delegate = this.findDefaultFocus();
 
        if (delegate) {
            return delegate.isWidget ? delegate.getFocusEl() : delegate;
        }
        else if (this.focusable) {
            return this.focusEl;
        }
 
        // Containers that are not focusable should not return a focusEl
        return undefined;
    },
 
    /**
     * Finds the configured default focus item. See {@link #cfg!defaultFocus}.
     */
    findDefaultFocus: function() {
        var result = this.getDefaultFocus();
 
        // If we have not been configured with a Widget instance, look for a focusable
        // by selector. Then check whether it is focusable. It may be disabled,
        // destroying, or simply set to focusable: false, and the element could
        // still be focusable amd therefore be focused by calling code.
        if (result && !result.isWidget) {
            result = this.down(result);
 
            if (result && !result.canFocus()) {
                return;
            }
        }
 
        // Returning undefined is ok
        return result;
    },
 
    onFocusEnter: function(e) {
        var me = this;
 
        me.callParent([e]);
 
        // We DO NOT check if `me` is focusable here. The reason is that
        // non-focusable containers need to track focus entering their
        // children so that revertFocus would work if these children
        // become unavailable.
        if (me.focusableContainer && !me.destroying && !me.destroyed) {
            me.mixins.focusablecontainer.onFocusEnter.call(me, e);
        }
    },
 
    onFocusLeave: function(e) {
        var me = this;
 
        me.callParent([e]);
 
        // Ditto
        if (me.focusableContainer && !me.destroying && !me.destroyed) {
            me.mixins.focusablecontainer.onFocusLeave.call(me, e);
        }
    },
 
    updateInnerCls: function(innerCls, old) {
        var el = this.getRenderTarget();
 
        el.replaceCls(old, innerCls);
    },
 
    updateAutoSize: function(autoSize) {
        var me = this,
            bodySizerElement = me.bodySizerElement;
 
        if (autoSize === false) {
            if (!bodySizerElement) {
                me.bodySizerElement = me.bodyElement.wrap({
                    cls: Ext.baseCSSPrefix + 'body-sizer-el'
                });
            }
        }
        else if (bodySizerElement) {
            me.bodyElement.unwrap();
            bodySizerElement.destroy();
            me.bodySizerElement = null;
        }
    },
 
    updateMaxHeight: function(maxHeight, oldMaxHeight) {
        var me = this;
 
        me.callParent([maxHeight, oldMaxHeight]);
 
        if (Ext.isIE11 && (maxHeight != null) && (me.getAutoSize() !== false)) {
            me.getMaxHeightElement().setMaxHeight(maxHeight);
            me.addCls(Ext.baseCSSPrefix + 'max-height-wrapped');
        }
    },
 
    privates: {
        /**
         * This method is in place on the instance during construction to ensure that any
         * {@link #lookup} or {@link #getReferences} calls have the {@link #items} initialized
         * prior to the lookup.
         * @private
         */
        getFirstReferences: function() {
            var me = this;
 
            delete me.getReferences;
            me.getItems(); // create our items if we haven't yet
 
            return me.getReferences.apply(me, arguments);
        },
 
        /**
         * Similar to `getRenderTarget` but for `positioned` items.
         * @param {Ext.Component} item The positioned item being added.
         * @return {Ext.dom.Element} 
         * @private
         * @since 6.5.0
         */
        getPositionedItemTarget: function() {
            return this.getRenderTarget();
        },
 
        /**
         * Applies the container's {@link #defaults} onto a child item. The item
         * can be a config object or an instance but has to be an inner item.
         * @param {Object/Ext.Component} item The item to apply the defaults to.
         * @return {Object/Ext.Component} The item that was passed in
         */
        applyItemDefaults: function(item) {
            var defaults = this.getDefaults();
 
            if (defaults && !item.ignoreDefaults) {
                if (item.isComponent) {
                    if (item.isInnerItem() && !this.has(item)) {
                        if (Ext.isFunction(defaults)) {
                            defaults = defaults(item);
                        }
 
                        item.setConfig(defaults, null, {
                            defaults: true
                        });
                    }
                }
                // TODO: revisit this when we have a better story for how to apply defaults
                /* else if (
                    !(
                        //check if config has a config that will make it floating or docked
                        item.hasOwnProperty('left') ||
                        item.hasOwnProperty('right') ||
                        item.hasOwnProperty('top') ||
                        item.hasOwnProperty('bottom') ||
                        item.hasOwnProperty('docked') ||
                        item.hasOwnProperty('centered')
                    )
                ) */
                else {
                    if (Ext.isFunction(defaults)) {
                        defaults = defaults(item);
                    }
 
                    // make a new object so the config object stays intact
                    item = Ext.merge({}, defaults, item);
                }
            }
 
            return item;
        },
 
        setChildRendered: function(rendered, item) {
            if (item.isInnerItem()) {
                this.getLayout().renderInnerItem(item);
            }
            else if (!rendered || !item.getFloated()) {
                // We do not flag floateds as rendered - they flag themselves as rendered
                // on first show. However, we MUST UNrender and extract floateds from
                // their floatRoot ready to be rendered anew when they are next shown.
                item.setRendered(rendered);
            }
        },
 
        /**
         * @private
         * In IE11 vertically flexed elements (such as container body-el or panel body-wrap-el)
         * are not flexed properly when the container has a max-height, but no height.
         * We can workaround the issue by wrapping the vertical box in a horizontal box.
         * See EXTJS-24498
         */
        getMaxHeightElement: function() {
            var el = this.el,
                maxHeightElement = this.maxHeightElement,
                selector = '.x-dock,.x-panelheader,.x-body-el,.x-body-wrap-el,.x-tab-guard-el',
                childNodes, node, i, ln;
 
            if (!maxHeightElement) {
                this.maxHeightElement = maxHeightElement = el.insertFirst({
                    cls: Ext.baseCSSPrefix + 'max-height-el'
                });
 
                childNodes = Ext.Array.clone(el.dom.childNodes);
 
                for (= 1, ln = childNodes.length; i < ln; i++) {
                    node = childNodes[i];
 
                    if (Ext.fly(node).is(selector)) {
                        maxHeightElement.appendChild(node);
                    }
                }
            }
 
            return maxHeightElement;
        }
    }
 
}, function() {
    this.prototype.defaultItemClass = this;
});