/**
 * @class Ext.layout.container.Border
 * @extends Ext.layout.container.Container
 * <p>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.</p>
 * <p>This class is intended to be extended or created via the <code>layout:'border'</code>
 * {@link Ext.container.Container#layout} config, and should generally not need to be created directly
 * via the new keyword.</p>
 * {@img Ext.layout.container.Border/Ext.layout.container.Border.png Ext.layout.container.Border container layout}
 * <p>Example usage:</p>
 * <pre><code>
     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()
    });
</code></pre>
 * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
 * <li>Any Container using the Border layout <b>must</b> have a child item with <code>region:'center'</code>.
 * 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.</li>
 * <li>Any child items with a region of <code>west</code> or <code>east</code> may be configured with either
 * an initial <code>width</code>, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width <b>string</b> (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of <code>1</code>.</li>
 * <li>Any child items with a region of <code>north</code> or <code>south</code> may be configured with either
 * an initial <code>height</code>, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height <b>string</b> (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of <code>1</code>.</li>
 * <li>The regions of a BorderLayout are <b>fixed at render time</b> and thereafter, its child Components may not be removed or added</b>.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:<pre><code>
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'
});
 * </code></pre>
 * </li>
 * <li><b>There is no BorderLayout.Region class in ExtJS 4.0+</b></li>
 * </ul></div>
 */

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,

    fixedLayout
: false,

    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
.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();
   
},

    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>
   
},

    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;
               
delete comp.collapsed;

                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'
                   
}
               
}));
                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'
               
}, 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;
               
}

               
// 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;
       
}

       
// 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;
   
},

   
// 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();
       
}
   
},

    /**
     * <p>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.</p>
     * <p><b>Note that this will be a fully instantiated Component, but will only be <i>rendered</i> when the Panel is first collapsed.</b></p>
     * @param {Panel} panel The child Panel of the layout for which to return the {@link Ext.panel.Panel#placeholder placeholder}.
     * @returns {Component} The Panel's {@link Ext.panel.Panel#placeholder placeholder} unless the {@link Ext.panel.Panel#collapseMode collapseMode} is
     * <code>'header'</code>, in which case <i>undefined</i> 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',
                    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;
               
}
                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 {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) {
       
var me = this,
            compEl
= comp.el,
            miniCollapse
= comp.collapseMode == 'mini',
            shadowContainer
= comp.shadowOwnerCt,
            shadowLayout
= shadowContainer.layout,
            placeholder
= comp.placeholder,
            placeholderBox
,
            targetSize
= shadowLayout.getLayoutTargetSize(),
            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);
       
}

       
// 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;

           
// 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
();

           
// Horrible workaround for https://sencha.jira.com/browse/EXTJSIV-1562
           
if (Ext.isIE) {
                placeholder
.setCalculatedSize(placeholder.el.getWidth());
           
}
       
}

       
return false;
   
},

   
// Hijack the expand operation to remove the placeholder and slide the region back in.
    onBeforeRegionExpand
: function(comp, animate) {
       
this.onPlaceHolderToolClick(null, null, null, {client: comp});
       
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 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;

           
// 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);
   
}
});