/** * 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 (i = 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 (i = 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 (i = 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 (i = 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 (i = 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 (i = 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 (i = 0; i < length; ++i) { comp = items[i].target; region = comp.region; if (comp.isCenter) { center = comp; } else if (region) { touched = touchedRegions[region]; for (j = 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 (j = 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);});