/**
 * 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: 300,
 *         title: 'Border Layout',
 *         layout: 'border',
 *         items: [{
 *             title: 'South Region is resizable',
 *             region: 'south',     // position for region
 *             xtype: 'panel',
 *             height: 100,
 *             split: true,         // enable resizing
 *             margin: '0 5 5 5'
 *         },{
 *             // xtype: 'panel' implied by default
 *             title: 'West Region is collapsible',
 *             region:'west',
 *             xtype: 'panel',
 *             margin: '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',
 *             margin: '5 5 0 0'
 *         }],
 *         renderTo: Ext.getBody()
 *     });
 *
 * # Notes
 * 
 *   - When using the split option, the layout will automatically insert a {@link Ext.resizer.Splitter}
 *     into the appropriate place. This will modify the underlying
 *     {@link Ext.container.Container#property-items items} collection in the container.
 *
 *   - 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`.
 *
 *   - **There is no BorderLayout.Region class in ExtJS 4.0+**
 */
Ext.define('Ext.layout.container.Border', {
 
    extend: 'Ext.layout.container.Container',
    alias: 'layout.border',
    alternateClassName: 'Ext.layout.BorderLayout',
 
    requires: [
        'Ext.resizer.BorderSplitter',
        'Ext.fx.Anim',
 
        // Overrides for Panel that provide border layout features
        'Ext.layout.container.border.Region'
    ],
 
 
    targetCls: Ext.baseCSSPrefix + 'border-layout-ct',
 
    itemCls: [Ext.baseCSSPrefix + 'border-item', Ext.baseCSSPrefix + 'box-item'],
 
    type: 'border',
 
    isBorderLayout: true,
 
    /**
     * @cfg {Boolean/Ext.resizer.BorderSplitter} split
     * This configuration option is to be applied to the **child `items`** managed by this layout.
     * Each region with `split:true` will get a {@link Ext.resizer.BorderSplitter Splitter} that
     * allows for manual resizing of the container. Except for the `center` region.
     *
     * This option can also accept an object of configurations from the {@link Ext.resizer.BorderSplitter}.
     * An example of this would be:
     *
     *     {
     *         title: 'North',
     *         region: 'north',
     *         height: 100,
     *         collapsible: true,
     *         split: {
     *             size: 20
     *         }
     *     }
     */
    
    /**
     * @cfg {Boolean} [splitterResize=true]
     * This configuration option is to be applied to the **child `items`** managed by this layout and
     * is used in conjunction with {@link #split}. By default, when specifying {@link #split}, the region
     * can be dragged to be resized. Set this option to false to show the split bar but prevent resizing.
     */
 
    /**
     * @cfg {Number/String/Object} padding
     * Sets the padding to be applied to all child items managed by this layout.
     * 
     * This property can be specified as a string containing space-separated, numeric
     * padding values. The order of the sides associated with each value matches the way
     * CSS processes padding values:
     *
     *  - If there is only one value, it applies to all sides.
     *  - If there are two values, the top and bottom borders are set to the first value
     *    and the right and left are set to the second.
     *  - If there are three values, the top is set to the first value, the left and right
     *    are set to the second, and the bottom is set to the third.
     *  - If there are four values, they apply to the top, right, bottom, and left,
     *    respectively.
     *
     */
    padding: undefined,
 
    percentageRe: /(\d+)%/,
    
    horzPositionProp: 'left',
    padOnContainerProp: 'left',
    padNotOnContainerProp: 'right',
 
    /**
     * Reused meta-data objects that describe axis properties.
     * @private
     */
    axisProps: {
        horz: {
            borderBegin: 'west',
            borderEnd: 'east',
            horizontal: true,
            posProp: 'x',
            sizeProp: 'width',
            sizePropCap: 'Width'
        },
        vert: {
            borderBegin: 'north',
            borderEnd: 'south',
            horizontal: false,
            posProp: 'y',
            sizeProp: 'height',
            sizePropCap: 'Height'
        }
    },
 
    /**
     * @private
     */
    centerRegion: null,
 
    manageMargins: true,
 
    panelCollapseAnimate: true,
 
    panelCollapseMode: 'placeholder',
 
    /**
     * @cfg {Object} regionWeights
     * The default weights to assign to regions in the border layout. These values are
     * used when a region does not contain a `weight` property. This object must have
     * properties for all regions ("north", "south", "east" and "west").
     * 
     * **IMPORTANT:** Since this is an object, changing its properties will impact ALL
     * instances of Border layout. If this is not desired, provide a replacement object as
     * a config option instead:
     * 
     *      layout: {
     *          type: 'border',
     *          regionWeights: {
     *              west: 20,
     *              north: 10,
     *              south: -10,
     *              east: -20
     *          }
     *      }
     *
     * The region with the highest weight is assigned space from the border before other
     * regions. Regions of equal weight are assigned space based on their position in the
     * owner's items list (first come, first served).
     */
    regionWeights: {
        north: 20,
        south: 10,
        center: 0,
        west: -10,
        east: -20
    },
 
    //----------------------------------
    // Layout processing
 
    /**
     * Creates the axis objects for the layout. These are only missing size information
     * which is added during {@link #calculate}.
     * @private
     */
    beginAxis: function (ownerContext, regions, name) {
        var me = this,
            props = me.axisProps[name],
            isVert = !props.horizontal,
            sizeProp = props.sizeProp,
            totalFlex = 0,
            childItems = ownerContext.childItems,
            length = childItems.length,
            center, i, childContext, centerFlex, comp, region, match, size, type, target, placeholder;
 
        for (= 0; i < length; ++i) {
            childContext = childItems[i];
            comp = childContext.target;
 
            childContext.layoutPos = {};
 
            if (comp.region) {
                childContext.region = region = comp.region;
 
                childContext.isCenter = comp.isCenter;
                childContext.isHorz = comp.isHorz;
                childContext.isVert = comp.isVert;
 
                childContext.weight = comp.weight || me.regionWeights[region] || 0;
                comp.weight = childContext.weight;
                
                regions[comp.id] = childContext;
 
                if (comp.isCenter) {
                    center = childContext;
                    centerFlex = comp.flex;
                    ownerContext.centerRegion = center;
 
                    continue;
                }
 
                if (isVert !== childContext.isVert) {
                    continue;
                }
 
                // process regions "isVert ? north||south : east||center||west"
 
                childContext.reverseWeighting = (region === props.borderEnd);
 
                size = comp[sizeProp];
                type = typeof size;
 
                if (!comp.collapsed) {
                    if (type === 'string' && (match = me.percentageRe.exec(size))) {
                        childContext.percentage = parseInt(match[1], 10);
                    } else if (comp.flex) {
                        totalFlex += childContext.flex = comp.flex;
                    }
                }
            }
        }
 
        // Special cases for a collapsed center region
        if (center) {
            target = center.target;
 
            if ((placeholder = target.placeholderFor)) {
                if (!centerFlex && isVert === placeholder.collapsedVertical()) {
                    // The center region is a placeholder, collapsed in this axis
                    centerFlex = 0;
                    center.collapseAxis = name;
                }
            } else if (target.collapsed && (isVert === target.collapsedVertical())) {
                // The center region is a collapsed header, collapsed in this axis
                centerFlex = 0;
                center.collapseAxis = name;
            }
        }
 
        if (centerFlex == null) {
            // If we still don't have a center flex, default to 1
            centerFlex = 1;
        }
 
        totalFlex += centerFlex;
 
        return Ext.apply({
            before         : isVert ? 'top' : 'left',
            totalFlex      : totalFlex
        }, props);
    },
 
    beginLayout: function (ownerContext) {
        var me = this,
            items = me.getLayoutItems(),
            pad = me.padding,
            type = typeof pad,
            padOnContainer = false,
            childContext, item, length, i, regions, collapseTarget,
            doShow, hidden, region;
 
        //<debug>
        // TODO: EXTJSIV-13015
        if (ownerContext.heightModel.shrinkWrap) {
            Ext.raise("Border layout does not currently support shrinkWrap height. " +
                "Please specify a height on component: " + me.owner.id +
                ", or use a container layout that sets the component's height.");
        }
        if (ownerContext.widthModel.shrinkWrap) {
            Ext.raise("Border layout does not currently support shrinkWrap width. " +
                "Please specify a width on component: " + me.owner.id +
                ", or use a container layout that sets the component's width.");
        }
        //</debug>
 
        // We sync the visibility state of splitters with their region:
        if (pad) {
            if (type === 'string' || type === 'number') {
                pad = Ext.util.Format.parseBox(pad);
            }
        } else {
            pad = ownerContext.getEl('getTargetEl').getPaddingInfo();
            padOnContainer = true;
        }
        ownerContext.outerPad = pad;
        ownerContext.padOnContainer = padOnContainer;
 
        for (= 0, length = items.length; i < length; ++i) {
            item = items[i];
            collapseTarget = me.getSplitterTarget(item);
            if (collapseTarget) {  // if (splitter)
                doShow = undefined;
                hidden = !!item.hidden;
                if (!collapseTarget.split) {
                    if (collapseTarget.isCollapsingOrExpanding) {
                        doShow = !!collapseTarget.collapsed;
                    }
                } else if (hidden !== collapseTarget.hidden) {
                    doShow = !collapseTarget.hidden;
                }
 
                if (doShow) {
                    item.show();
                } else if (doShow === false) {
                    item.hide();
                }
            }
        }
 
        // The above synchronized visibility of splitters with their regions, so we need
        // to make this call after that so that childItems and visibleItems are correct:
        //
        me.callParent(arguments);
 
        items = ownerContext.childItems;
        length = items.length;
        regions = {};
 
        ownerContext.borderAxisHorz = me.beginAxis(ownerContext, regions, 'horz');
        ownerContext.borderAxisVert = me.beginAxis(ownerContext, regions, 'vert');
 
        // Now that weights are assigned to the region's contextItems, we assign those
        // same weights to the contextItem for the splitters. We also cross link the
        // contextItems for the collapseTarget and its splitter.
        for (= 0; i < length; ++i) {
            childContext = items[i];
            collapseTarget = me.getSplitterTarget(childContext.target);
 
            if (collapseTarget) { // if (splitter)
                region = regions[collapseTarget.id];
                if (!region) {
                        // if the region was hidden it will not be part of childItems, and
                        // so beginAxis() won't add it to the regions object, so we have
                        // to create the context item here.
                        region = ownerContext.getEl(collapseTarget.el, me);
                        region.region = collapseTarget.region;
                }
                childContext.collapseTarget = collapseTarget = region;
                childContext.weight = collapseTarget.weight;
                childContext.reverseWeighting = collapseTarget.reverseWeighting;
                collapseTarget.splitter = childContext;
                childContext.isHorz = collapseTarget.isHorz;
                childContext.isVert = collapseTarget.isVert;
            }
        }
 
        // Now we want to sort the childItems by their weight.
        me.sortWeightedItems(items, 'reverseWeighting');
        me.setupSplitterNeighbors(items);
    },
 
    calculate: function (ownerContext) {
        var me = this,
            containerSize = me.getContainerSize(ownerContext),
            childItems = ownerContext.childItems,
            length = childItems.length,
            horz = ownerContext.borderAxisHorz,
            vert = ownerContext.borderAxisVert,
            pad = ownerContext.outerPad,
            padOnContainer = ownerContext.padOnContainer,
            i, childContext, childMargins, size, horzPercentTotal, vertPercentTotal;
 
        horz.begin = pad[me.padOnContainerProp];
        vert.begin = pad.top;
        // If the padding is already on the container we need to add it to the space
        // If not on the container, it's "virtual" padding.
        
        horzPercentTotal = horz.end = horz.flexSpace = containerSize.width + (padOnContainer ? pad[me.padOnContainerProp] : -pad[me.padNotOnContainerProp]);
        vertPercentTotal = vert.end = vert.flexSpace = containerSize.height + (padOnContainer ? pad.top : -pad.bottom);
 
        // Reduce flexSpace on each axis by the fixed/auto sized dimensions of items that
        // aren't flexed along that axis.
        for (= 0; i < length; ++i) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();
 
            // Margins are always fixed size and must be removed from the space used for percentages and flexes
            if (childContext.isHorz || childContext.isCenter) {
                horz.addUnflexed(childMargins.width);
                horzPercentTotal -= childMargins.width;
            }
 
            if (childContext.isVert || childContext.isCenter) {
                vert.addUnflexed(childMargins.height);
                vertPercentTotal -= childMargins.height;
            }
 
            // Fixed size components must have their sizes removed from the space used for flex
            if (!childContext.flex && !childContext.percentage) {
                if (childContext.isHorz || (childContext.isCenter && childContext.collapseAxis === 'horz')) {
                    size = childContext.getProp('width');
 
                    horz.addUnflexed(size);
 
                    // splitters should not count towards percentages
                    if (childContext.collapseTarget) {
                        horzPercentTotal -= size;
                    }
                } else if (childContext.isVert || (childContext.isCenter && childContext.collapseAxis === 'vert')) {
                    size = childContext.getProp('height');
 
                    vert.addUnflexed(size);
 
                    // splitters should not count towards percentages
                    if (childContext.collapseTarget) {
                        vertPercentTotal -= size;
                    }
                }
                // else ignore center since it is fully flexed
            }
        }
 
        for (= 0; i < length; ++i) {
            childContext = childItems[i];
            childMargins = childContext.getMarginInfo();
 
            // Calculate the percentage sizes. After this calculation percentages are very similar to fixed sizes
            if (childContext.percentage) {
                if (childContext.isHorz) {
                    size = Math.ceil(horzPercentTotal * childContext.percentage / 100);
                    size = childContext.setWidth(size);
                    horz.addUnflexed(size);
                } else if (childContext.isVert) {
                    size = Math.ceil(vertPercentTotal * childContext.percentage / 100);
                    size = childContext.setHeight(size);
                    vert.addUnflexed(size);
                }
                // center shouldn't have a percentage but if it does it should be ignored
            }
        }
 
 
        // If we haven't gotten sizes for all unflexed dimensions on an axis, the flexSpace
        // will be NaN so we won't be calculating flexed dimensions until that is resolved.
 
        for (= 0; i < length; ++i) {
            childContext = childItems[i];
 
            if (!childContext.isCenter) {
                me.calculateChildAxis(childContext, horz);
                me.calculateChildAxis(childContext, vert);
            }
        }
 
        // Once all items are placed, the final size of the center can be determined. If we
        // can determine both width and height, we are done. We use '+' instead of '&&' to
        // avoid short-circuiting (we want to call both):
        if (me.finishAxis(ownerContext, vert) + me.finishAxis(ownerContext, horz) < 2) {
            me.done = false;
        } else {
            // Size information is published as we place regions but position is hard to do
            // that way (while avoiding published multiple times) so we publish all the
            // positions at the end.
            me.finishPositions(childItems);
        }
    },
 
    /**
     * Performs the calculations for a region on a specified axis.
     * @private
     */
    calculateChildAxis: function (childContext, axis) {
        var collapseTarget = childContext.collapseTarget,
            setSizeMethod = 'set' + axis.sizePropCap,
            sizeProp = axis.sizeProp,
            childMarginSize = childContext.getMarginInfo()[sizeProp],
            region, isBegin, flex, pos, size;
 
        if (collapseTarget) { // if (splitter)
            region = collapseTarget.region;
        } else {
            region = childContext.region;
            flex = childContext.flex;
        }
 
        isBegin = region === axis.borderBegin;
 
        if (!isBegin && region !== axis.borderEnd) {
            // a north/south region on the horizontal axis or an east/west region on the
            // vertical axis: stretch to fill remaining space:
            childContext[setSizeMethod](axis.end - axis.begin - childMarginSize);
            pos = axis.begin;
        } else {
            if (flex) {
                size = Math.ceil(axis.flexSpace * (flex / axis.totalFlex));
                size = childContext[setSizeMethod](size);
            } else if (childContext.percentage) {
                // Like getProp but without registering a dependency - we calculated the size, we don't depend on it
                size = childContext.peek(sizeProp);
            } else {
                size = childContext.getProp(sizeProp);
            }
 
            size += childMarginSize;
 
            if (isBegin) {
                pos = axis.begin;
                axis.begin += size;
            } else {
                axis.end = pos = axis.end - size;
            }
        }
 
        childContext.layoutPos[axis.posProp] = pos;
    },
    
    eachItem: function (region, fn, scope) {
        var me = this,
            items = me.getLayoutItems(),
            i = 0,
            item;
        
        if (Ext.isFunction(region)) {
            fn = region;
            scope = fn;
        }
        
        for (i; i < items.length; i++) {
            item = items[i];
            
            if (!region || item.region === region) {
                if (fn.call(scope, item) === false) {
                    break;
                }
            }
        }
    },
 
    /**
     * Finishes the calculations on an axis. This basically just assigns the remaining
     * space to the center region.
     * @private
     */
    finishAxis: function (ownerContext, axis) {
        var size = axis.end - axis.begin,
            center = ownerContext.centerRegion;
 
        if (center) {
            center['set' + axis.sizePropCap](size - center.getMarginInfo()[axis.sizeProp]);
            center.layoutPos[axis.posProp] = axis.begin;
        }
 
        return Ext.isNumber(size) ? 1 : 0;
    },
 
    /**
     * Finishes by setting the positions on the child items.
     * @private
     */
    finishPositions: function (childItems) {
        var length = childItems.length,
            index, childContext,
            marginProp = this.horzPositionProp;
 
        for (index = 0; index < length; ++index) {
            childContext = childItems[index];
 
            childContext.setProp('x', childContext.layoutPos.x + childContext.marginInfo[marginProp]);
            childContext.setProp('y', childContext.layoutPos.y + childContext.marginInfo.top);
        }
    },
 
    getLayoutItems: function() {
        var owner = this.owner,
            ownerItems = (owner && owner.items && owner.items.items) || [],
            length = ownerItems.length,
            items = [],
            i = 0,
            ownerItem, placeholderFor;
 
        for (; i < length; i++) {
            ownerItem = ownerItems[i];
            placeholderFor = ownerItem.placeholderFor;
            // There are a couple of scenarios where we do NOT want an item to
            // be included in the layout items:
            //
            // 1. If the item is floated. This can happen when a region's header
            // is clicked to "float" the item, then another region's header or
            // is clicked quickly before the first floated region has had a
            // chance to slide out. When this happens, the second click triggers
            // a layout, the purpose of which is to determine what the size of the 
            // second region will be after it is floated, so it can be animated
            // to that size. In this case the existing floated item should not be
            // included in the layout items because it will not be visible once
            // it's slideout animation has completed.
            //
            // 2. If the item is a placeholder for a panel that is currently
            // being expanded. Similar to scenario 1, a second layout can be
            // triggered by another panel being expanded/collapsed/floated before
            // the first panel has finished it's expand animation. If this is the
            // case we do not want the placeholder to be included in the layout
            // items because it will not be once the panel has finished expanding.
            //
            // If the component is hidden, we need none of these shenanigans
            if (ownerItem.hidden || ((!ownerItem.floated || ownerItem.isCollapsingOrExpanding === 2) &&
                !(placeholderFor && placeholderFor.isCollapsingOrExpanding === 2))) {
                items.push(ownerItem);
            } 
        }
 
        return items;
    },
 
    getPlaceholder: function (comp) {
        return comp.getPlaceholder && comp.getPlaceholder();
    },
    
    getMaxWeight: function (region) {
        return this.getMinMaxWeight(region);
    },
        
    getMinWeight: function (region) {
        return this.getMinMaxWeight(region, true);
    },
    
    getMinMaxWeight: function (region, min) {
        var me = this,
            weight = null;
        
        me.eachItem(region, function (item) {
            if (item.hasOwnProperty('weight')) {
                if (weight === null) {
                    weight = item.weight;
                    
                    return;
                }
                
                if ((min && item.weight < weight) || item.weight > weight) {
                    weight = item.weight;
                }
            }
        }, this);
        
        return weight;
    },
    
    getSplitterTarget: function (splitter) {
        var collapseTarget = splitter.collapseTarget;
 
        if (collapseTarget && collapseTarget.collapsed) {
            return collapseTarget.placeholder || collapseTarget;
        }
 
        return collapseTarget;
    },
 
    isItemBoxParent: function (itemContext) {
        return true;
    },
 
    isItemShrinkWrap: function (item) {
        return true;
    },
 
    //----------------------------------
    // Event handlers
 
    /**
     * Inserts the splitter for a given region. A reference to the splitter is also stored
     * on the component as "splitter".
     * @private
     */
    insertSplitter: function (item, index, hidden, splitterCfg) {
        var region = item.region,
            splitter = Ext.apply({
                xtype: 'bordersplitter',
                collapseTarget: item,
                id: item.id + '-splitter',
                hidden: hidden,
                canResize: item.splitterResize !== false,
                splitterFor: item,
                synthetic: true // not user-defined
            }, splitterCfg),
            at = index + ((region === 'south' || region === 'east') ? 0 : 1);
 
        if (item.collapseMode === 'mini') {
            splitter.collapsedCls = item.collapsedCls;
        }
 
        item.splitter = this.owner.add(at, splitter);
    },
    
    getMoveAfterIndex: function (after) {
        var index = this.callParent(arguments);
        
        if (after.splitter) {
            index++;
        }
        
        return index;
    },
    
    moveItemBefore: function (item, before) {
        var beforeRegion;
            
        if (before && before.splitter) {
            beforeRegion = before.region;
 
            if (beforeRegion === 'south' || beforeRegion === 'east') {
                before = before.splitter;
            }
        }
          
        return this.callParent([item, before]);
    },
    
    /**
     * Called when a region (actually when any component) is added to the container. The
     * region is decorated with some helpful properties (isCenter, isHorz, isVert) and its
     * splitter is added if its "split" property is true.
     * @private
     */
    onAdd: function (item, index) {
        var me = this,
            placeholderFor = item.placeholderFor,
            region = item.region,
            isCenter,
            split,
            hidden,
            cfg;
 
        me.callParent(arguments);
 
        if (region) {
            Ext.apply(item, me.regionFlags[region]);
            
            if (me.owner.isViewport) {
                item.isViewportBorderChild = true;
            }
 
            if (item.initBorderRegion) {
                // This method should always be present but perhaps the override is being
                // excluded.
                item.initBorderRegion();
            }
 
            isCenter = region === 'center';
            if (isCenter) {
                //<debug>
                if (me.centerRegion) {
                    Ext.raise("Cannot have multiple center regions in a BorderLayout.");
                }
                //</debug>
                me.centerRegion = item;
            } else {
                split = item.split;
                hidden = !!item.hidden;
                
                if (typeof split === 'object') {
                    cfg = split;
                    split = true;
                }
                
                if ((item.isHorz || item.isVert) && (split || item.collapseMode === 'mini')) {
                    if (item.collapseMode === 'mini' && item.collapsed) {
                        hidden = false;
                    }
                    else if (!split) {
                        hidden = true;
                    }
                    
                    me.insertSplitter(item, index, hidden, cfg);
                }
            }
 
            if (!isCenter && !item.hasOwnProperty('collapseMode')) {
                item.collapseMode = me.panelCollapseMode;
            }
 
            if (!item.hasOwnProperty('animCollapse')) {
                if (item.collapseMode !== 'placeholder') {
                    // other collapse modes do not animate nicely in a border layout, so
                    // default them to off:
                    item.animCollapse = false;
                } else {
                    item.animCollapse = me.panelCollapseAnimate;
                }
            }
            
            // Item can be collapsed when added
            if (hidden && item.placeholder && item.placeholder.isVisible()) {
                me.owner.insert(index, item.placeholder);
            }
        } else if (placeholderFor) {
            Ext.apply(item, me.regionFlags[placeholderFor.region]);
            item.region = placeholderFor.region;
            item.weight = placeholderFor.weight;
        }
    },
 
    onDestroy: function() {
        this.centerRegion = null;
        this.callParent();
    },
 
    onRemove: function (comp, isDestroying) {
        var me = this,
            region = comp.region,
            splitter = comp.splitter,
            owner = me.owner,
            destroying = owner.destroying,
            el;
 
        if (region) {
            if (comp.isCenter) {
                me.centerRegion = null;
            }
 
            delete comp.isCenter;
            delete comp.isHorz;
            delete comp.isVert;
 
            // If the owner is destroying, the splitter will be cleared anyway
            if (splitter && !owner.destroying) {
                owner.doRemove(splitter, true); // avoid another layout
            }
            delete comp.splitter;
        }
 
        me.callParent(arguments);
        
        if (!destroying && !isDestroying && comp.rendered) {
            // Clear top/left styles
            el = comp.getEl();
            if (el) {
                el.setStyle('top', '');
                el.setStyle(me.horzPositionProp, '');
            }
        }
    },
 
    //----------------------------------
    // Misc
 
    regionMeta: {
        center: { splitterDelta: 0 },
 
        north: { splitterDelta:  1 },
        south: { splitterDelta: -1 },
 
        west:  { splitterDelta:  1 },
        east:  { splitterDelta: -1 }
    },
 
    /**
     * Flags and configs that get set of regions based on their `region` property.
     * @private
     */
    regionFlags: {
        center: {
            isCenter: true, isHorz: false, isVert: false
        },
 
        north: {
            isCenter: false, isHorz: false, isVert: true, collapseDirection: 'top'
        },
        south: {
            isCenter: false, isHorz: false, isVert: true, collapseDirection: 'bottom'
        },
 
        west: {
            isCenter: false, isHorz: true, isVert: false, collapseDirection: 'left'
        },
        east: {
            isCenter: false, isHorz: true, isVert: false, collapseDirection: 'right'
        }
    },
 
    setupSplitterNeighbors: function (items) {
        var edgeRegions = {
                //north: null,
                //south: null,
                //east: null,
                //west: null
            },
            length = items.length,
            touchedRegions = this.touchedRegions,
            i, j, center, count, edge, comp, region, splitter, touched;
 
        for (= 0; i < length; ++i) {
            comp = items[i].target;
            region = comp.region;
 
            if (comp.isCenter) {
                center = comp;
            } else if (region) {
                touched = touchedRegions[region];
 
                for (= 0, count = touched.length; j < count; ++j) {
                    edge = edgeRegions[touched[j]];
                    if (edge) {
                        edge.neighbors.push(comp);
                    }
                }
                
                if (comp.placeholderFor) {
                    // placeholder, so grab the splitter for the actual panel
                    splitter = comp.placeholderFor.splitter;
                } else {
                    splitter = comp.splitter;
                }
                if (splitter) {
                    splitter.neighbors = [];
                }
 
                edgeRegions[region] = splitter;
            }
        }
 
        if (center) {
            touched = touchedRegions.center;
 
            for (= 0, count = touched.length; j < count; ++j) {
                edge = edgeRegions[touched[j]];
                if (edge) {
                    edge.neighbors.push(center);
                }
            }
        }
    },
 
    /**
     * Lists the regions that would consider an interior region a neighbor. For example,
     * a north region would consider an east or west region its neighbords (as well as
     * an inner north region).
     * @private
     */
    touchedRegions: {
        center: [ 'north', 'south', 'east',  'west' ],
 
        north:  [ 'north', 'east',  'west'  ],
        south:  [ 'south', 'east',  'west'  ],
        east:   [ 'east',  'north', 'south' ],
        west:   [ 'west',  'north', 'south' ]
    },
 
    sizePolicies: {
        vert: {
            readsWidth: 0,
            readsHeight: 1,
            setsWidth: 1,
            setsHeight: 0
        },
        horz: {
            readsWidth: 1,
            readsHeight: 0,
            setsWidth: 0,
            setsHeight: 1
        },
        flexAll: {
            readsWidth: 0,
            readsHeight: 0,
            setsWidth: 1,
            setsHeight: 1
        }
    },
 
    getItemSizePolicy: function (item) {
        var me = this,
            policies = this.sizePolicies,
            collapseTarget, size, policy, placeholderFor;
 
        if (item.isCenter) {
            placeholderFor = item.placeholderFor;
 
            if (placeholderFor) {
                if (placeholderFor.collapsedVertical()) {
                    return policies.vert;
                }
                return policies.horz;
            }
            if (item.collapsed) {
                if (item.collapsedVertical()) {
                    return policies.vert;
                }
                return policies.horz;
            }
            return policies.flexAll;
        }
 
        collapseTarget = item.collapseTarget;
 
        if (collapseTarget) {
            return collapseTarget.isVert ? policies.vert : policies.horz;
        }
 
        if (item.region) {
            if (item.isVert) {
                size = item.height;
                policy = policies.vert;
            } else {
                size = item.width;
                policy = policies.horz;
            }
 
            if (item.flex || (typeof size === 'string' && me.percentageRe.test(size))) {
                return policies.flexAll;
            }
 
            return policy;
        }
 
        return me.autoSizePolicy;
    }
}, function () {
    var methods = {
        addUnflexed: function (px) {
            this.flexSpace = Math.max(this.flexSpace - px, 0);
        }
    },
    props = this.prototype.axisProps;
 
    Ext.apply(props.horz, methods);
    Ext.apply(props.vert, methods);
});