    /**
     * This is a multi-pane, application-oriented UI layout style that supports multiple nested panels, automatic bars
     * between regions and built-in {@link Ext.panel.Panel#collapsible expanding and collapsing} of regions.
     *
     * This class is intended to be extended or created via the `layout:'border'` {@link Ext.container.Container#layout}
     * config, and should generally not need to be created directly via the new keyword.
     *
     *     @example
     *     Ext.create('Ext.panel.Panel', {
     *         width: 500,
     *         height: 400,
     *         title: 'Border Layout',
     *         layout: 'border',
     *         items: [{
     *             title: 'South Region is resizable',
     *             region: 'south',     // position for region
     *             xtype: 'panel',
     *             height: 100,
     *             split: true,         // enable resizing
     *             margins: '0 5 5 5'
     *         },{
     *             // xtype: 'panel' implied by default
     *             title: 'West Region is collapsible',
     *             region:'west',
     *             xtype: 'panel',
     *             margins: '5 0 0 5',
     *             width: 200,
     *             collapsible: true,   // make collapsible
     *             id: 'west-region-container',
     *             layout: 'fit'
     *         },{
     *             title: 'Center Region',
     *             region: 'center',     // center region is required, no width/height specified
     *             xtype: 'panel',
     *             layout: 'fit',
     *             margins: '5 5 0 0'
     *         }],
     *         renderTo: Ext.getBody()
     *     });
     *
     * # Notes
     *
     * - Any Container using the Border layout **must** have a child item with `region:'center'`.
     *   The child item in the center region will always be resized to fill the remaining space
     *   not used by the other regions in the layout.
     *
     * - Any child items with a region of `west` or `east` may be configured with either an initial
     *   `width`, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width
     *   **string** (Which is simply divided by 100 and used as a flex value).
     *   The 'center' region has a flex value of `1`.
     *
     * - Any child items with a region of `north` or `south` may be configured with either an initial
     *   `height`, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height
     *   **string** (Which is simply divided by 100 and used as a flex value).
     *   The 'center' region has a flex value of `1`.
     *
     * - The regions of a BorderLayout are **fixed at render time** and thereafter, its child
     *   Components may not be removed or added**. To add/remove Components within a BorderLayout,
     *   have them wrapped by an additional Container which is directly managed by the BorderLayout.
     *   If the region is to be collapsible, the Container used directly by the BorderLayout manager
     *   should be a Panel. In the following example a Container (an Ext.panel.Panel) is added to
     *   the west region:
     *
     *       wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
     *       wrc.{@link Ext.container.Container#removeAll removeAll}();
     *       wrc.{@link Ext.container.Container#add add}({
     *           title: 'Added Panel',
     *           html: 'Some content'
     *       });
     *
     * - **There is no BorderLayout.Region class in ExtJS 4.0+**
     */
    Ext.define('Ext.layout.container.Border', {

        alias: ['layout.border'],
        extend: 'Ext.layout.container.Container',
        requires: ['Ext.resizer.Splitter', 'Ext.container.Container', 'Ext.fx.Anim'],
        alternateClassName: 'Ext.layout.BorderLayout',

        targetCls: Ext.baseCSSPrefix + 'border-layout-ct',

        itemCls: Ext.baseCSSPrefix + 'border-item',

        bindToOwnerCtContainer: true,

        percentageRe: /(\d+)%/,

        slideDirection: {
            north: 't',
            south: 'b',
            west: 'l',
            east: 'r'
        },

        constructor: function(config) {
            this.initialConfig = config;
            this.callParent(arguments);
        },

        onLayout: function() {
            var me = this;
            if (!me.borderLayoutInitialized) {
                me.initializeBorderLayout();
            }

            // Delegate this operation to the shadow "V" or "H" box layout, and then down to any embedded layout.
            me.fixHeightConstraints();
            me.shadowLayout.onLayout();
            if (me.embeddedContainer) {
                me.embeddedContainer.layout.onLayout();
            }

            // If the panel was originally configured with collapsed: true, it will have
            // been initialized with a "borderCollapse" flag: Collapse it now before the first layout.
            if (!me.initialCollapsedComplete) {
                Ext.iterate(me.regions, function(name, region){
                    if (region.borderCollapse) {
                        me.onBeforeRegionCollapse(region, region.collapseDirection, false, 0);
                    }
                });
                me.initialCollapsedComplete = true;
            }
        },

        isValidParent : function(item, target, position) {
            if (!this.borderLayoutInitialized) {
                this.initializeBorderLayout();
            }

            // Delegate this operation to the shadow "V" or "H" box layout.
            return this.shadowLayout.isValidParent(item, target, position);
        },

        beforeLayout: function() {
            if (!this.borderLayoutInitialized) {
                this.initializeBorderLayout();
            }

            // Delegate this operation to the shadow "V" or "H" box layout.
            this.shadowLayout.beforeLayout();

            // note: don't call base because that does a renderItems again
        },

        renderItems: function(items, target) {
            //<debug>
            Ext.Error.raise('This should not be called');
            //</debug>
        },

        renderItem: function(item) {
            //<debug>
            Ext.Error.raise('This should not be called');
            //</debug>
        },

        renderChildren: function() {
            if (!this.borderLayoutInitialized) {
                this.initializeBorderLayout();
            }

            this.shadowLayout.renderChildren();
        },

        /*
         * Gathers items for a layout operation. Injected into child Box layouts through configuration.
         * We must not include child items which are floated over the layout (are primed with a slide out animation)
         */
        getVisibleItems: function() {
            return Ext.ComponentQuery.query(':not([slideOutAnim])', this.callParent(arguments));
        },

        initializeBorderLayout: function() {
            var me = this,
                i = 0,
                items = me.getLayoutItems(),
                ln = items.length,
                regions = (me.regions = {}),
                vBoxItems = [],
                hBoxItems = [],
                horizontalFlex = 0,
                verticalFlex = 0,
                comp, percentage;

            // Map of Splitters for each region
            me.splitters = {};

            // Map of regions
            for (; i < ln; i++) {
                comp = items[i];
                regions[comp.region] = comp;

                // Intercept collapsing to implement showing an alternate Component as a collapsed placeholder
                if (comp.region != 'center' && comp.collapsible && comp.collapseMode != 'header') {

                    // This layout intercepts any initial collapsed state. Panel must not do this itself.
                    comp.borderCollapse = comp.collapsed;
                    comp.collapsed = false;

                    comp.on({
                        beforecollapse: me.onBeforeRegionCollapse,
                        beforeexpand: me.onBeforeRegionExpand,
                        destroy: me.onRegionDestroy,
                        scope: me
                    });
                    me.setupState(comp);
                }
            }
            //<debug>
            if (!regions.center) {
                Ext.Error.raise("You must specify a center region when defining a BorderLayout.");
            }
            //</debug>
            comp = regions.center;
            if (!comp.flex) {
                comp.flex = 1;
            }
            delete comp.width;
            comp.maintainFlex = true;

            // Begin the VBox and HBox item list.
            comp = regions.west;
            if (comp) {
                comp.collapseDirection = Ext.Component.DIRECTION_LEFT;
                hBoxItems.push(comp);
                if (comp.split) {
                    hBoxItems.push(me.splitters.west = me.createSplitter(comp));
                }
                percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
                if (percentage) {
                    horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
                    delete comp.width;
                }
            }
            comp = regions.north;
            if (comp) {
                comp.collapseDirection = Ext.Component.DIRECTION_TOP;
                vBoxItems.push(comp);
                if (comp.split) {
                    vBoxItems.push(me.splitters.north = me.createSplitter(comp));
                }
                percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
                if (percentage) {
                    verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
                    delete comp.height;
                }
            }

            // Decide into which Collection the center region goes.
            if (regions.north || regions.south) {
                if (regions.east || regions.west) {

                    // Create the embedded center. Mark it with the region: 'center' property so that it can be identified as the center.
                    vBoxItems.push(me.embeddedContainer = Ext.create('Ext.container.Container', {
                        xtype: 'container',
                        region: 'center',
                        id: me.owner.id + '-embedded-center',
                        cls: Ext.baseCSSPrefix + 'border-item',
                        flex: regions.center.flex,
                        maintainFlex: true,
                        layout: {
                            type: 'hbox',
                            align: 'stretch',
                            getVisibleItems: me.getVisibleItems
                        }
                    }));
                    hBoxItems.push(regions.center);
                }
                // No east or west: the original center goes straight into the vbox
                else {
                    vBoxItems.push(regions.center);
                }
            }
            // If we have no north or south, then the center is part of the HBox items
            else {
                hBoxItems.push(regions.center);
            }

            // Finish off the VBox and HBox item list.
            comp = regions.south;
            if (comp) {
                comp.collapseDirection = Ext.Component.DIRECTION_BOTTOM;
                if (comp.split) {
                    vBoxItems.push(me.splitters.south = me.createSplitter(comp));
                }
                percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
                if (percentage) {
                    verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
                    delete comp.height;
                }
                vBoxItems.push(comp);
            }
            comp = regions.east;
            if (comp) {
                comp.collapseDirection = Ext.Component.DIRECTION_RIGHT;
                if (comp.split) {
                    hBoxItems.push(me.splitters.east = me.createSplitter(comp));
                }
                percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
                if (percentage) {
                    horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
                    delete comp.width;
                }
                hBoxItems.push(comp);
            }

            // Create the injected "items" collections for the Containers.
            // If we have north or south, then the shadow Container will be a VBox.
            // If there are also east or west regions, its center will be a shadow HBox.
            // If there are *only* east or west regions, then the shadow layout will be an HBox (or Fit).
            if (regions.north || regions.south) {

                me.shadowContainer = Ext.create('Ext.container.Container', {
                    ownerCt: me.owner,
                    el: me.getTarget(),
                    layout: Ext.applyIf({
                        type: 'vbox',
                        align: 'stretch',
                        getVisibleItems: me.getVisibleItems
                    }, me.initialConfig)
                });
                me.createItems(me.shadowContainer, vBoxItems);

                // Allow the Splitters to orientate themselves
                if (me.splitters.north) {
                    me.splitters.north.ownerCt = me.shadowContainer;
                }
                if (me.splitters.south) {
                    me.splitters.south.ownerCt = me.shadowContainer;
                }

                // Inject items into the HBox Container if there is one - if there was an east or west.
                if (me.embeddedContainer) {
                    me.embeddedContainer.ownerCt = me.shadowContainer;
                    me.createItems(me.embeddedContainer, hBoxItems);

                    // Allow the Splitters to orientate themselves
                    if (me.splitters.east) {
                        me.splitters.east.ownerCt = me.embeddedContainer;
                    }
                    if (me.splitters.west) {
                        me.splitters.west.ownerCt = me.embeddedContainer;
                    }

                    // These spliiters need to be constrained by components one-level below
                    // the component in their vobx. We update the min/maxHeight on the helper
                    // (embeddedContainer) prior to starting the split/drag. This has to be
                    // done on-the-fly to allow min/maxHeight of the E/C/W regions to be set
                    // dynamically.
                    Ext.each([me.splitters.north, me.splitters.south], function (splitter) {
                        if (splitter) {
                            splitter.on('beforedragstart', me.fixHeightConstraints, me);
                        }
                    });

                    // The east or west region wanted a percentage
                    if (horizontalFlex) {
                        regions.center.flex -= horizontalFlex;
                    }
                    // The north or south region wanted a percentage
                    if (verticalFlex) {
                        me.embeddedContainer.flex -= verticalFlex;
                    }
                } else {
                    // The north or south region wanted a percentage
                    if (verticalFlex) {
                        regions.center.flex -= verticalFlex;
                    }
                }
            }
            // If we have no north or south, then there's only one Container, and it's
            // an HBox, or, if only a center region was specified, a Fit.
            else {
                me.shadowContainer = Ext.create('Ext.container.Container', {
                    ownerCt: me.owner,
                    el: me.getTarget(),
                    layout: Ext.applyIf({
                        type: (hBoxItems.length == 1) ? 'fit' : 'hbox',
                        align: 'stretch'
                    }, me.initialConfig)
                });
                me.createItems(me.shadowContainer, hBoxItems);

                // Allow the Splitters to orientate themselves
                if (me.splitters.east) {
                    me.splitters.east.ownerCt = me.shadowContainer;
                }
                if (me.splitters.west) {
                    me.splitters.west.ownerCt = me.shadowContainer;
                }

                // The east or west region wanted a percentage
                if (horizontalFlex) {
                    regions.center.flex -= verticalFlex;
                }
            }

            // Create upward links from the region Components to their shadow ownerCts
            for (i = 0, items = me.shadowContainer.items.items, ln = items.length; i < ln; i++) {
                items[i].shadowOwnerCt = me.shadowContainer;
            }
            if (me.embeddedContainer) {
                for (i = 0, items = me.embeddedContainer.items.items, ln = items.length; i < ln; i++) {
                    items[i].shadowOwnerCt = me.embeddedContainer;
                }
            }

            // This is the layout that we delegate all operations to
            me.shadowLayout = me.shadowContainer.getLayout();

            me.borderLayoutInitialized = true;
        },

        setupState: function(comp){
            var getState = comp.getState;
            comp.getState = function(){
                // call the original getState
                var state = getState.call(comp) || {},
                    region = comp.region;

                state.collapsed = !!comp.collapsed;
                if (region == 'west' || region == 'east') {
                    state.width = comp.getWidth();
                } else {
                    state.height = comp.getHeight();
                }
                return state;
            };
            comp.addStateEvents(['collapse', 'expand', 'resize']);
        },

        /**
         * Create the items collection for our shadow/embedded containers
         * @private
         */
        createItems: function(container, items){
            // Have to inject an items Collection *after* construction.
            // The child items of the shadow layout must retain their original, user-defined ownerCt
            delete container.items;
            container.initItems();
            container.items.addAll(items);
        },

        // Private
        // Create a splitter for a child of the layout.
        createSplitter: function(comp) {
            var me = this,
                interceptCollapse = (comp.collapseMode != 'header'),
                resizer;

            resizer = Ext.create('Ext.resizer.Splitter', {
                hidden: !!comp.hidden,
                collapseTarget: comp,
                performCollapse: !interceptCollapse,
                listeners: interceptCollapse ? {
                    click: {
                        fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
                        element: 'collapseEl'
                    }
                } : null
            });

            // Mini collapse means that the splitter is the placeholder Component
            if (comp.collapseMode == 'mini') {
                comp.placeholder = resizer;
                resizer.collapsedCls = comp.collapsedCls;
            }

            // Arrange to hide/show a region's associated splitter when the region is hidden/shown
            comp.on({
                hide: me.onRegionVisibilityChange,
                show: me.onRegionVisibilityChange,
                scope: me
            });
            return resizer;
        },

        // Private
        // Propagates the min/maxHeight values from the inner hbox items to its container.
        fixHeightConstraints: function () {
            var me = this,
                ct = me.embeddedContainer,
                maxHeight = 1e99, minHeight = -1;

            if (!ct) {
                return;
            }

            ct.items.each(function (item) {
                if (Ext.isNumber(item.maxHeight)) {
                    maxHeight = Math.max(maxHeight, item.maxHeight);
                }
                if (Ext.isNumber(item.minHeight)) {
                    minHeight = Math.max(minHeight, item.minHeight);
                }
            });

            ct.maxHeight = maxHeight;
            ct.minHeight = minHeight;
        },

        // Hide/show a region's associated splitter when the region is hidden/shown
        onRegionVisibilityChange: function(comp){
            this.splitters[comp.region][comp.hidden ? 'hide' : 'show']();
            this.layout();
        },

        // Called when a splitter mini-collapse tool is clicked on.
        // The listener is only added if this layout is controlling collapsing,
        // not if the component's collapseMode is 'mini' or 'header'.
        onSplitterCollapseClick: function(comp) {
            if (comp.collapsed) {
                this.onPlaceHolderToolClick(null, null, null, {client: comp});
            } else {
                comp.collapse();
            }
        },

        /**
         * Return the {@link Ext.panel.Panel#placeholder placeholder} Component to which the passed child Panel of the
         * layout will collapse. By default, this will be a {@link Ext.panel.Header Header} component (Docked to the
         * appropriate border). See {@link Ext.panel.Panel#placeholder placeholder}. config to customize this.
         *
         * **Note that this will be a fully instantiated Component, but will only be _rendered_ when the Panel is first
         * collapsed.**
         * @param {Ext.panel.Panel} panel The child Panel of the layout for which to return the {@link
         * Ext.panel.Panel#placeholder placeholder}.
         * @return {Ext.Component} The Panel's {@link Ext.panel.Panel#placeholder placeholder} unless the {@link
         * Ext.panel.Panel#collapseMode collapseMode} is `'header'`, in which case _undefined_ is returned.
         */
        getPlaceholder: function(comp) {
            var me = this,
                placeholder = comp.placeholder,
                shadowContainer = comp.shadowOwnerCt,
                shadowLayout = shadowContainer.layout,
                oppositeDirection = Ext.panel.Panel.prototype.getOppositeDirection(comp.collapseDirection),
                horiz = (comp.region == 'north' || comp.region == 'south');

            // No placeholder if the collapse mode is not the Border layout default
            if (comp.collapseMode == 'header') {
                return;
            }

            // Provide a replacement Container with an expand tool
            if (!placeholder) {
                if (comp.collapseMode == 'mini') {
                    placeholder = Ext.create('Ext.resizer.Splitter', {
                        id: 'collapse-placeholder-' + comp.id,
                        collapseTarget: comp,
                        performCollapse: false,
                        listeners: {
                            click: {
                                fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
                                element: 'collapseEl'
                            }
                        }
                    });
                    placeholder.addCls(placeholder.collapsedCls);
                } else {
                    placeholder = {
                        id: 'collapse-placeholder-' + comp.id,
                        margins: comp.initialConfig.margins || Ext.getClass(comp).prototype.margins,
                        xtype: 'header',
                        orientation: horiz ? 'horizontal' : 'vertical',
                        title: comp.title,
                        textCls: comp.headerTextCls,
                        iconCls: comp.iconCls,
                        baseCls: comp.baseCls + '-header',
                        ui: comp.ui,
                        indicateDrag: comp.draggable,
                        cls: Ext.baseCSSPrefix + 'region-collapsed-placeholder ' + Ext.baseCSSPrefix + 'region-collapsed-' + comp.collapseDirection + '-placeholder ' + comp.collapsedCls,
                        listeners: comp.floatable ? {
                            click: {
                                fn: function(e) {
                                    me.floatCollapsedPanel(e, comp);
                                },
                                element: 'el'
                            }
                        } : null
                    };
                    // Hack for IE6/7/IEQuirks's inability to display an inline-block
                    if ((Ext.isIE6 || Ext.isIE7 || (Ext.isIEQuirks)) && !horiz) {
                        placeholder.width = 25;
                    }
                    if (!comp.hideCollapseTool) {
                        placeholder[horiz ? 'tools' : 'items'] = [{
                            xtype: 'tool',
                            client: comp,
                            type: 'expand-' + oppositeDirection,
                            handler: me.onPlaceHolderToolClick,
                            scope: me
                        }];
                    }
                }
                placeholder = me.owner.createComponent(placeholder);
                if (comp.isXType('panel')) {
                    comp.on({
                        titlechange: me.onRegionTitleChange,
                        iconchange: me.onRegionIconChange,
                        scope: me
                    });
                }
            }

            // The collapsed Component holds a reference to its placeholder and vice versa
            comp.placeholder = placeholder;
            placeholder.comp = comp;

            return placeholder;
        },

        /**
         * @private
         * Update the placeholder title when panel title has been set or changed.
         */
        onRegionTitleChange: function(comp, newTitle) {
            comp.placeholder.setTitle(newTitle);
        },

        /**
         * @private
         * Update the placeholder iconCls when panel iconCls has been set or changed.
         */
        onRegionIconChange: function(comp, newIconCls) {
            comp.placeholder.setIconCls(newIconCls);
        },

        /**
         * @private
         * Calculates the size and positioning of the passed child item. Must be present because Panel's expand,
         * when configured with a flex, calls this method on its ownerCt's layout.
         * @param {Ext.Component} child The child Component to calculate the box for
         * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height.
         */
        calculateChildBox: function(comp) {
            var me = this;
            if (me.shadowContainer.items.contains(comp)) {
                return me.shadowContainer.layout.calculateChildBox(comp);
            }
            else if (me.embeddedContainer && me.embeddedContainer.items.contains(comp)) {
                return me.embeddedContainer.layout.calculateChildBox(comp);
            }
        },

        /**
         * @private
         * Intercepts the Panel's own collapse event and perform's substitution of the Panel
         * with a placeholder Header orientated in the appropriate dimension.
         * @param comp The Panel being collapsed.
         * @param direction
         * @param animate
         * @returns {Boolean} false to inhibit the Panel from performing its own collapse.
         */
        onBeforeRegionCollapse: function(comp, direction, animate) {
            if (comp.collapsedChangingLayout) {
                //<debug warn>
                if (Ext.global.console && Ext.global.console.warn) {
                    Ext.global.console.warn(Ext.getDisplayName(arguments.callee), 'aborted because the collapsed state is in the middle of changing');
                }
                //</debug>
                return false;
            }
            comp.collapsedChangingLayout = true;
            var me = this,
                compEl = comp.el,
                width,
                miniCollapse = comp.collapseMode == 'mini',
                shadowContainer = comp.shadowOwnerCt,
                shadowLayout = shadowContainer.layout,
                placeholder = comp.placeholder,
                sl = me.owner.suspendLayout,
                scsl = shadowContainer.suspendLayout,
                isNorthOrWest = (comp.region == 'north' || comp.region == 'west'); // Flag to keep the placeholder non-adjacent to any Splitter

            // Do not trigger a layout during transition to collapsed Component
            me.owner.suspendLayout = true;
            shadowContainer.suspendLayout = true;

            // Prevent upward notifications from downstream layouts
            shadowLayout.layoutBusy = true;
            if (shadowContainer.componentLayout) {
                shadowContainer.componentLayout.layoutBusy = true;
            }
            me.shadowContainer.layout.layoutBusy = true;
            me.layoutBusy = true;
            me.owner.componentLayout.layoutBusy = true;

            // Provide a replacement Container with an expand tool
            if (!placeholder) {
                placeholder = me.getPlaceholder(comp);
            }

            // placeholder already in place; show it.
            if (placeholder.shadowOwnerCt === shadowContainer) {
                placeholder.show();
            }
            // Insert the collapsed placeholder Component into the appropriate Box layout shadow Container
            // It must go next to its client Component, but non-adjacent to the splitter so splitter can find its collapse client.
            // Inject an ownerCt value pointing to the owner, border layout Container as the user will expect.
            else {
                shadowContainer.insert(shadowContainer.items.indexOf(comp) + (isNorthOrWest ? 0 : 1), placeholder);
                placeholder.shadowOwnerCt = shadowContainer;
                placeholder.ownerCt = me.owner;
            }

            // Flag the collapsing Component as hidden and show the placeholder.
            // This causes the shadow Box layout's calculateChildBoxes to calculate the correct new arrangement.
            // We hide or slideOut the Component's element
            comp.hidden = true;

            if (!placeholder.rendered) {
                shadowLayout.renderItem(placeholder, shadowLayout.innerCt);

                // The inserted placeholder does not have the proper size, so copy the width
                // for N/S or the height for E/W from the component. This fixes EXTJSIV-1562
                // without recursive layouts. This is only an issue initially. After this time,
                // placeholder will have the correct width/height set by the layout (which has
                // already happened when we get here initially).
                if (comp.region == 'north' || comp.region == 'south') {
                    placeholder.setCalculatedSize(comp.getWidth());
                } else {
                    placeholder.setCalculatedSize(undefined, comp.getHeight());
                }
            }

            // Jobs to be done after the collapse has been done
            function afterCollapse() {
                // Reinstate automatic laying out.
                me.owner.suspendLayout = sl;
                shadowContainer.suspendLayout = scsl;
                delete shadowLayout.layoutBusy;
                if (shadowContainer.componentLayout) {
                    delete shadowContainer.componentLayout.layoutBusy;
                }
                delete me.shadowContainer.layout.layoutBusy;
                delete me.layoutBusy;
                delete me.owner.componentLayout.layoutBusy;
                delete comp.collapsedChangingLayout;

                // Fire the collapse event: The Panel has in fact been collapsed, but by substitution of an alternative Component
                comp.collapsed = true;
                comp.fireEvent('collapse', comp);
            }

            /*
             * Set everything to the new positions. Note that we
             * only want to animate the collapse if it wasn't configured
             * initially with collapsed: true
             */
            if (comp.animCollapse && me.initialCollapsedComplete) {
                shadowLayout.layout();
                compEl.dom.style.zIndex = 100;

                // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
                if (!miniCollapse) {
                    placeholder.el.hide();
                }
                compEl.slideOut(me.slideDirection[comp.region], {
                    duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            compEl.show().setLeftTop(-10000, -10000);
                            compEl.dom.style.zIndex = '';

                            // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
                           if (!miniCollapse) {
                                placeholder.el.slideIn(me.slideDirection[comp.region], {
                                    easing: 'linear',
                                    duration: 100
                                });
                            }
                            afterCollapse();
                        }
                    }
                });
            } else {
                compEl.setLeftTop(-10000, -10000);
                shadowLayout.layout();
                afterCollapse();
            }

            return false;
        },

        // Hijack the expand operation to remove the placeholder and slide the region back in.
        onBeforeRegionExpand: function(comp, animate) {
            // We don't check for comp.collapsedChangingLayout here because onPlaceHolderToolClick does it
            this.onPlaceHolderToolClick(null, null, null, {client: comp, shouldFireBeforeexpand: false});
            return false;
        },

        // Called when the collapsed placeholder is clicked to reinstate a "collapsed" (in reality hidden) Panel.
        onPlaceHolderToolClick: function(e, target, owner, tool) {
            var me = this,
                comp = tool.client,

                // Hide the placeholder unless it was the Component's preexisting splitter
                hidePlaceholder = (comp.collapseMode != 'mini') || !comp.split,
                compEl = comp.el,
                toCompBox,
                placeholder = comp.placeholder,
                placeholderEl = placeholder.el,
                shadowContainer = comp.shadowOwnerCt,
                shadowLayout = shadowContainer.layout,
                curSize,
                sl = me.owner.suspendLayout,
                scsl = shadowContainer.suspendLayout,
                isFloating;

            if (comp.collapsedChangingLayout) {
                //<debug warn>
                if (Ext.global.console && Ext.global.console.warn) {
                    Ext.global.console.warn(Ext.getDisplayName(arguments.callee), 'aborted because the collapsed state is in the middle of changing');
                }
                //</debug>
                return false;
            }
            if (tool.shouldFireBeforeexpand !== false && comp.fireEvent('beforeexpand', comp, true) === false) {
                return false;
            }
            comp.collapsedChangingLayout = true;
            // If the slide in is still going, stop it.
            // This will either leave the Component in its fully floated state (which is processed below)
            // or in its collapsed state. Either way, we expand it..
            if (comp.getActiveAnimation()) {
                comp.stopAnimation();
            }

            // If the Component is fully floated when they click the placeholder Tool,
            // it will be primed with a slide out animation object... so delete that
            // and remove the mouseout listeners
            if (comp.slideOutAnim) {
                // Remove mouse leave monitors
                compEl.un(comp.panelMouseMon);
                placeholderEl.un(comp.placeholderMouseMon);

                delete comp.slideOutAnim;
                delete comp.panelMouseMon;
                delete comp.placeholderMouseMon;

                // If the Panel was floated and primed with a slideOut animation, we don't want to animate its layout operation.
                isFloating = true;
            }

            // Do not trigger a layout during transition to expanded Component
            me.owner.suspendLayout = true;
            shadowContainer.suspendLayout = true;

            // Prevent upward notifications from downstream layouts
            shadowLayout.layoutBusy = true;
            if (shadowContainer.componentLayout) {
                shadowContainer.componentLayout.layoutBusy = true;
            }
            me.shadowContainer.layout.layoutBusy = true;
            me.layoutBusy = true;
            me.owner.componentLayout.layoutBusy = true;

            // Unset the hidden and collapsed flags set in onBeforeRegionCollapse. The shadowLayout will now take it into account
            // Find where the shadow Box layout plans to put the expanding Component.
            comp.hidden = false;
            comp.collapsed = false;
            if (hidePlaceholder) {
                placeholder.hidden = true;
            }
            toCompBox = shadowLayout.calculateChildBox(comp);

            // Show the collapse tool in case it was hidden by the slide-in
            if (comp.collapseTool) {
                comp.collapseTool.show();
            }

            // If we're going to animate, we need to hide the component before moving it back into position
            if (comp.animCollapse && !isFloating) {
                compEl.setStyle('visibility', 'hidden');
            }
            compEl.setLeftTop(toCompBox.left, toCompBox.top);

            // Equalize the size of the expanding Component prior to animation
            // in case the layout area has changed size during the time it was collapsed.
            curSize = comp.getSize();
            if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
                me.setItemSize(comp, toCompBox.width, toCompBox.height);
            }

            // Jobs to be done after the expand has been done
            function afterExpand() {
                // Reinstate automatic laying out.
                me.owner.suspendLayout = sl;
                shadowContainer.suspendLayout = scsl;
                delete shadowLayout.layoutBusy;
                if (shadowContainer.componentLayout) {
                    delete shadowContainer.componentLayout.layoutBusy;
                }
                delete me.shadowContainer.layout.layoutBusy;
                delete me.layoutBusy;
                delete me.owner.componentLayout.layoutBusy;
                delete comp.collapsedChangingLayout;

                // In case it was floated out and they clicked the re-expand tool
                comp.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');

                // Fire the expand event: The Panel has in fact been expanded, but by removal of an alternative Component
                comp.fireEvent('expand', comp);
            }

            // Hide the placeholder
            if (hidePlaceholder) {
                placeholder.el.hide();
            }

            // Slide the expanding Component to its new position.
            // When that is done, layout the layout.
            if (comp.animCollapse && !isFloating) {
                compEl.dom.style.zIndex = 100;
                compEl.slideIn(me.slideDirection[comp.region], {
                    duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
                    listeners: {
                        afteranimate: function() {
                            compEl.dom.style.zIndex = '';
                            comp.hidden = false;
                            shadowLayout.onLayout();
                            afterExpand();
                        }
                    }
                });
            } else {
                shadowLayout.onLayout();
                afterExpand();
            }
        },

        floatCollapsedPanel: function(e, comp) {

            if (comp.floatable === false) {
                return;
            }

            var me = this,
                compEl = comp.el,
                placeholder = comp.placeholder,
                placeholderEl = placeholder.el,
                shadowContainer = comp.shadowOwnerCt,
                shadowLayout = shadowContainer.layout,
                placeholderBox = shadowLayout.getChildBox(placeholder),
                scsl = shadowContainer.suspendLayout,
                curSize, toCompBox, compAnim;

            // Ignore clicks on tools.
            if (e.getTarget('.' + Ext.baseCSSPrefix + 'tool')) {
                return;
            }

            // It's *being* animated, ignore the click.
            // Possible future enhancement: Stop and *reverse* the current active Fx.
            if (compEl.getActiveAnimation()) {
                return;
            }

            // If the Component is already fully floated when they click the placeholder,
            // it will be primed with a slide out animation object... so slide it out.
            if (comp.slideOutAnim) {
                me.slideOutFloatedComponent(comp);
                return;
            }

            // Function to be called when the mouse leaves the floated Panel
            // Slide out when the mouse leaves the region bounded by the slid Component and its placeholder.
            function onMouseLeaveFloated(e) {
                var slideRegion = compEl.getRegion().union(placeholderEl.getRegion()).adjust(1, -1, -1, 1);

                // If mouse is not within slide Region, slide it out
                if (!slideRegion.contains(e.getPoint())) {
                    me.slideOutFloatedComponent(comp);
                }
            }

            // Monitor for mouseouting of the placeholder. Hide it if they exit for half a second or more
            comp.placeholderMouseMon = placeholderEl.monitorMouseLeave(500, onMouseLeaveFloated);

            // Do not trigger a layout during slide out of the Component
            shadowContainer.suspendLayout = true;

            // Prevent upward notifications from downstream layouts
            me.layoutBusy = true;
            me.owner.componentLayout.layoutBusy = true;

            // The collapse tool is hidden while slid.
            // It is re-shown on expand.
            if (comp.collapseTool) {
                comp.collapseTool.hide();
            }

            // Set flags so that the layout will calculate the boxes for what we want
            comp.hidden = false;
            comp.collapsed = false;
            placeholder.hidden = true;

            // Recalculate new arrangement of the Component being floated.
            toCompBox = shadowLayout.calculateChildBox(comp);
            placeholder.hidden = false;

            // Component to appear just after the placeholder, whatever "after" means in the context of the shadow Box layout.
            if (comp.region == 'north' || comp.region == 'west') {
                toCompBox[shadowLayout.parallelBefore] += placeholderBox[shadowLayout.parallelPrefix] - 1;
            } else {
                toCompBox[shadowLayout.parallelBefore] -= (placeholderBox[shadowLayout.parallelPrefix] - 1);
            }
            compEl.setStyle('visibility', 'hidden');
            compEl.setLeftTop(toCompBox.left, toCompBox.top);

            // Equalize the size of the expanding Component prior to animation
            // in case the layout area has changed size during the time it was collapsed.
            curSize = comp.getSize();
            if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
                me.setItemSize(comp, toCompBox.width, toCompBox.height);
            }

            // This animation slides the collapsed Component's el out to just beyond its placeholder
            compAnim = {
                listeners: {
                    afteranimate: function() {
                        shadowContainer.suspendLayout = scsl;
                        delete me.layoutBusy;
                        delete me.owner.componentLayout.layoutBusy;

                        // Prime the Component with an Anim config object to slide it back out
                        compAnim.listeners = {
                            afterAnimate: function() {
                                compEl.show().removeCls(Ext.baseCSSPrefix + 'border-region-slide-in').setLeftTop(-10000, -10000);

                                // Reinstate the correct, current state after slide out animation finishes
                                comp.hidden = true;
                                comp.collapsed = true;
                                delete comp.slideOutAnim;
                                delete comp.panelMouseMon;
                                delete comp.placeholderMouseMon;
                            }
                        };
                        comp.slideOutAnim = compAnim;
                    }
                },
                duration: 500
            };

            // Give the element the correct class which places it at a high z-index
            compEl.addCls(Ext.baseCSSPrefix + 'border-region-slide-in');

            // Begin the slide in
            compEl.slideIn(me.slideDirection[comp.region], compAnim);

            // Monitor for mouseouting of the slid area. Hide it if they exit for half a second or more
            comp.panelMouseMon = compEl.monitorMouseLeave(500, onMouseLeaveFloated);

        },

        slideOutFloatedComponent: function(comp) {
            var compEl = comp.el,
                slideOutAnim;

            // Remove mouse leave monitors
            compEl.un(comp.panelMouseMon);
            comp.placeholder.el.un(comp.placeholderMouseMon);

            // Slide the Component out
            compEl.slideOut(this.slideDirection[comp.region], comp.slideOutAnim);

            delete comp.slideOutAnim;
            delete comp.panelMouseMon;
            delete comp.placeholderMouseMon;
        },

        /*
         * @private
         * Ensure any collapsed placeholder Component is destroyed along with its region.
         * Can't do this in onDestroy because they may remove a Component and use it elsewhere.
         */
        onRegionDestroy: function(comp) {
            var placeholder = comp.placeholder;
            if (placeholder) {
                delete placeholder.ownerCt;
                placeholder.destroy();
            }
        },

        /*
         * @private
         * Ensure any shadow Containers are destroyed.
         * Ensure we don't keep references to Components.
         */
        onDestroy: function() {
            var me = this,
                shadowContainer = me.shadowContainer,
                embeddedContainer = me.embeddedContainer;

            if (shadowContainer) {
                delete shadowContainer.ownerCt;
                Ext.destroy(shadowContainer);
            }

            if (embeddedContainer) {
                delete embeddedContainer.ownerCt;
                Ext.destroy(embeddedContainer);
            }
            delete me.regions;
            delete me.splitters;
            delete me.shadowContainer;
            delete me.embeddedContainer;
            me.callParent(arguments);
        }
    });