/**
* Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used
* directly.
*/
Ext.define('Ext.layout.container.Box', {
extend: 'Ext.layout.container.Container',
alternateClassName: 'Ext.layout.BoxLayout',
alias: 'layout.box',
requires: [
'Ext.layout.container.boxOverflow.None',
'Ext.layout.container.boxOverflow.Scroller',
'Ext.util.Format',
'Ext.dd.DragDropManager',
'Ext.resizer.Splitter'
],
type: 'box',
config: {
/**
* @cfg {String} [align="begin"]
* Controls how the child items of the container are aligned. The value is used to
* position items "perpendicularly". That is, for horizontal boxes (where `vertical`
* is `false`), then this will position items vertically. Otherwise, this will position
* items horizontally. The acceptable values for this property are best explained in
* context with the value of `vertical`.
*
* If `vertical` is `false` then this layout is behaving as an `hbox` and this config
* operates as follows:
*
* - **begin** : Child items are aligned vertically at the top of the container.
* - **middle** : Child items are vertically centered in the container.
* - **end** : Child items are aligned vertically at the bottom of the container.
* - **stretch** : Child items are stretched vertically to fill the height of the container.
* - **stretchmax** : Child items are stretched vertically to the height of the largest
* item.
*
* If `vertical` is `true` then this layout is behaving as an `vbox` and this config
* operates as follows:
*
* - **begin** : Child items are aligned horizontally at the left side of the container.
* - **middle** : Child items are horizontally centered in the container.
* - **end** : Child items are aligned horizontally at the right of the container.
* - **stretch** : Child items are stretched horizontally to fill the width of the
* container.
* - **stretchmax** : Child items are stretched horizontally to the size of the largest
* item.
*
* For backwards compatibility, the following values are also recognized:
*
* - **left** : Same as **begin**.
* - **top** : Same as **begin**.
* - **center** : Same as **middle**.
* - **right** : Same as **end**.
* - **bottom** : Same as **end**.
*/
align: 'begin', // end, middle, stretch, strechmax
/**
* @cfg {Boolean} constrainAlign
* Limits the size of {@link #align aligned} components to the size of the container
* under certain circumstances. Firstly, the container's height (for `hbox`) or width
* (for `vbox`) must not be determined by the size of the child components. Secondly,
* the child components must have {@link Ext.AbstractComponent#shrinkWrap shrinkwrap}
* enabled for this dimension.
*/
constrainAlign: false,
/**
* @cfg {Boolean} [enableSplitters=true]
* This flag can be set to `false` to ignore the `split` config on box items. This is
* set to `false` by `Ext.layout.container.Accordion`.
*/
enableSplitters: true,
// @cmd-auto-dependency { aliasPrefix: 'box.overflow.' }
/**
* @cfg {String/Ext.layout.container.boxOverflow.None}
* An overflow handler or config object for an overflow handler. This is typically
* specified as one of the following strings:
*
* - `scroller` - Scroller buttons are rendered before and after the content.
* - `menu` - Overflowing items are rendered into a menu, and a button is rendered
* after the items, which shows the menu when clicked.
*
* NOTE: This config is currently only supported when box layout is used by the
* following components:
*
* - {@link Ext.toolbar.Toolbar}
* - {@link Ext.menu.Menu}
* - {@link Ext.toolbar.Breadcrumb}
* - {@link Ext.tab.Bar}
*
* Components where `overflowHandler` is not supported should use
* `{@link Ext.Component#scrollable scrollable}:true` if they have overflowing
* content.
*/
overflowHandler: {
$value: null,
merge: function(newValue, oldValue) {
if (typeof newValue === 'string') {
newValue = {
type: newValue
};
}
return Ext.merge(oldValue ? Ext.Object.chain(oldValue) : {}, newValue);
}
},
/**
* @cfg {String} padding
* Sets the padding to be applied to all child items managed by this layout.
*
* This property must 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: 0,
/**
* @cfg {String} pack
* Controls how the child items of the container are packed together. Acceptable
* configuration values for this property are:
*
* - **start** - child items are packed together at **left** (HBox) or **top**
* (VBox) side of container (*default**)
* - **center** - child items are packed together at **mid-width** (HBox) or
* **mid-height** (VBox) of container
* - **end** - child items are packed together at **right** (HBox) or **bottom**
* (VBox) side of container
*/
pack: 'start',
/**
* @cfg {String/Ext.Component} stretchMaxPartner
* Allows stretchMax calculation to take into account the max perpendicular size
* (height for HBox layout and width for VBox layout) of another Box layout when
* calculating its maximum perpendicular child size.
*
* If specified as a string, this may be either a known Container ID, or a
* ComponentQuery selector which is rooted at this layout's Container (ie, to find
* a sibling, use `"^>#siblingItemId`).
*/
stretchMaxPartner: undefined,
/**
* @cfg {Boolean} [vertical=false]
* Set to `true` to switch the layout to `vbox`.
*/
vertical: false,
/**
* @cfg {"round"/"floor"/"ceil"} [alignRoundingMethod='round'] The Math method
* to use for rounding fractional pixels when `{@link #align}:middle` is used.
* The possible values are:
*
* - [round](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round)
* - [floor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor)
* - [ceil](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil)
*/
alignRoundingMethod: 'round'
},
itemCls: Ext.baseCSSPrefix + 'box-item',
targetCls: Ext.baseCSSPrefix + 'box-layout-ct',
targetElCls: Ext.baseCSSPrefix + 'box-target',
innerCls: Ext.baseCSSPrefix + 'box-inner',
manageMargins: true,
createsInnerCt: true,
childEls: [
'innerCt',
'targetEl'
],
/* eslint-disable indent, max-len */
renderTpl: [
'{%var oc,l=values.$comp.layout,oh=l.overflowHandler;' +
'if (oh && oh.getPrefixConfig!==Ext.emptyFn) {' +
'if(oc=oh.getPrefixConfig())dh.generateMarkup(oc, out)' +
'}%}' +
'<div id="{ownerId}-innerCt" data-ref="innerCt" role="presentation" class="{[l.innerCls]}' +
'{[oh ? (" " + oh.getOverflowCls(l.direction)) : ""]}">' +
'<div id="{ownerId}-targetEl" data-ref="targetEl" class="{targetElCls}" role="presentation">' +
'{%this.renderBody(out, values)%}' +
'</div>' +
'</div>' +
'{%if (oh && oh.getSuffixConfig!==Ext.emptyFn) {' +
'if(oc=oh.getSuffixConfig())dh.generateMarkup(oc, out)' +
'}%}',
{
disableFormats: true,
definitions: 'var dh=Ext.DomHelper;'
}
],
/* eslint-enable indent, max-len */
constructor: function(config) {
var me = this,
type;
me.callParent([config]);
me.setVertical(me.vertical);
// The sort function needs access to properties in this, so must be bound.
me.flexSortFn = me.flexSort.bind(me);
type = typeof me.padding;
if (type === 'string' || type === 'number') {
me.padding = Ext.util.Format.parseBox(me.padding);
me.padding.height = me.padding.top + me.padding.bottom;
me.padding.width = me.padding.left + me.padding.right;
}
},
_beginRe: /^(?:begin|left|top)$/,
_centerRe: /^(?:center|middle)$/,
_endRe: /^(?:end|right|bottom)$/,
// Matches: `<spaces>digits[.digits]<spaces>%<spaces>`
// Captures: `digits[.digits]`
_percentageRe: /^\s*(\d+(?:\.\d*)?)\s*[%]\s*$/,
getItemSizePolicy: function(item, ownerSizeModel) {
var me = this,
policy = me.sizePolicy,
align = me.align,
flex = item.flex,
key = align,
names = me.names,
heightName = names.height,
widthName = names.width,
width = item[widthName],
height = item[heightName],
percentageRe = me._percentageRe,
percentageWidth = percentageRe.test(width),
isStretch = (align === 'stretch'),
isStretchMax = (align === 'stretchmax'),
constrain = me.constrainAlign;
// Getting the size model is expensive, so we only want to do so if we really need it
if (!ownerSizeModel &&
(isStretch || flex || percentageWidth || (constrain && !isStretchMax))) {
ownerSizeModel = me.owner.getSizeModel();
}
if (isStretch) {
// If we are height.shrinkWrap, we behave as if we were stretchmax (for more
// details, see beginLayoutCycle)...
if (!percentageRe.test(height) && ownerSizeModel[heightName].shrinkWrap) {
key = 'stretchmax';
// We leave %age height as stretch since it will not participate in the
// stretchmax size calculation. This avoid running such a child in its
// shrinkWrap mode prior to supplying the calculated size.
}
}
else if (!isStretchMax) {
if (percentageRe.test(height)) {
// Height %ages are calculated based on container size, so they are the
// same as align=stretch for this purpose...
key = 'stretch';
}
else if (constrain && !ownerSizeModel[heightName].shrinkWrap) {
// Same functionality as stretchmax, only the max is going to be the size
// of the container, not the largest item
key = 'stretchmax';
}
else {
key = '';
}
}
if (flex || percentageWidth) {
// If we are width.shrinkWrap, we won't be flexing since that requires a
// container width...
if (!ownerSizeModel[widthName].shrinkWrap) {
policy = policy.flex; // both flex and %age width are calculated
}
}
return policy[key];
},
flexSort: function(a, b) {
// We need to sort the flexed items to ensure that we have
// the items with max/min width first since when we set the
// values we may have the value constrained, so we need to
// react accordingly. Precedence is given from the largest
// value through to the smallest value
var maxWidthName = this.names.maxWidth,
minWidthName = this.names.minWidth,
infiniteValue = Infinity,
aTarget = a.target,
bTarget = b.target,
aFlex = aTarget.flex,
bFlex = bTarget.flex,
result = 0,
aMin, bMin, aMax, bMax,
hasMin, hasMax;
aMax = aTarget[maxWidthName] || infiniteValue;
bMax = bTarget[maxWidthName] || infiniteValue;
aMin = aTarget[minWidthName] || 0;
bMin = bTarget[minWidthName] || 0;
hasMin = isFinite(aMin) || isFinite(bMin);
hasMax = isFinite(aMax) || isFinite(bMax);
if (hasMin || hasMax) {
if (hasMax) {
result = aMax - bMax;
}
// If the result is 0, it means either
// a) hasMax was false
// b) The max values were the same
if (result === 0 && hasMin) {
result = bMin - aMin;
}
// If 0, it means either the max and/or minimum was the same
if (result === 0) {
if (hasMax) {
result = bFlex - aFlex;
}
else {
result = aFlex - bFlex;
}
}
}
return result;
},
isItemBoxParent: function(itemContext) {
return true;
},
isItemShrinkWrap: function(item) {
return true;
},
roundFlex: function(width) {
return Math.floor(width);
},
/**
* @private
* Called by an owning Panel before the Panel begins its collapse process.
* Most layouts will not need to override the default Ext.emptyFn implementation.
*/
beginCollapse: function(child) {
var me = this;
if (me.direction === 'vertical' && child.collapsedVertical()) {
child.collapseMemento.capture(['flex']);
delete child.flex;
}
else if (me.direction === 'horizontal' && child.collapsedHorizontal()) {
child.collapseMemento.capture(['flex']);
delete child.flex;
}
},
/**
* @private
* Called by an owning Panel before the Panel begins its expand process.
* Most layouts will not need to override the default Ext.emptyFn implementation.
*/
beginExpand: function(child) {
// Restores the flex if we used to be flexed before
child.collapseMemento.restore(['flex']);
},
beginLayout: function(ownerContext) {
var me = this,
owner = me.owner,
smp = owner.stretchMaxPartner,
style = me.innerCt.dom.style,
names = me.names,
overflowHandler = me.overflowHandler;
ownerContext.boxNames = names;
// this must happen before callParent to allow the overflow handler to do its work
// that can effect the childItems collection...
if (overflowHandler) {
overflowHandler.beginLayout(ownerContext);
}
// get the contextItem for our stretchMax buddy:
if (typeof smp === 'string') {
smp = Ext.getCmp(smp) || owner.query(smp)[0];
}
ownerContext.stretchMaxPartner = smp && ownerContext.context.getCmp(smp);
me.callParent([ownerContext]);
ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);
ownerContext.targetElContext = ownerContext.getEl('targetEl', me);
ownerContext.ownerScrollable = owner.getScrollable();
// Don't allow sizes burned on to the innerCt to influence measurements.
style.width = style.height = '';
},
beginLayoutCycle: function(ownerContext, firstCycle) {
var me = this,
state = ownerContext.state,
scrollable = ownerContext.ownerScrollable,
align = me.align,
names = ownerContext.boxNames,
pack = me.pack,
centerRe = me._centerRe,
overflowHandler = me.overflowHandler,
canScroll = ownerContext.state.canScroll,
widthModel, heightModel;
// this must happen before callParent to allow the overflow handler to do its work
// that can effect the childItems collection...
if (overflowHandler) {
overflowHandler.beginLayoutCycle(ownerContext, firstCycle);
}
me.callParent([ownerContext, firstCycle]);
// Cache several of our string concat/compare results (since width/heightModel can
// change if we are invalidated, we cannot do this in beginLayout)
ownerContext.parallelSizeModel = widthModel = ownerContext[names.widthModel];
ownerContext.perpendicularSizeModel = heightModel = ownerContext[names.heightModel];
ownerContext.boxOptions = {
align: align = {
stretch: align === 'stretch',
stretchmax: align === 'stretchmax',
center: centerRe.test(align),
bottom: me._endRe.test(align)
},
pack: pack = {
center: centerRe.test(pack),
end: pack === 'end'
}
};
// Scrolling can occur if:
// a) The owner is configured to scroll - not if there's a boxOverflow scroller.
// We must not handle scroll if its this layout's innerCt which is scrolling
// which can be set by a Ext.layout.container.boxOverflow.Scroller.
// b) We're not shrink wrapping. If we shrink wrap, we should always size around the content
if (scrollable && !scrollable.isBoxOverflowScroller) {
if (!canScroll) {
// Use getX/getY here to indicate whether we will show visible scrollbars
// in that direction, we may have a scrollable and can scroll in that direction
// without having a visible scrollbar.
state.canScroll = {
parallel: !widthModel.shrinkWrap && scrollable[names.getX](),
perpendicular: !heightModel.shrinkWrap && scrollable[names.getY]()
};
}
if (!state.actualScroll) {
// Store the final calculated state for this cycle in here
state.actualScroll = {
parallel: false,
perpendicular: false
};
}
}
// Consider an hbox w/stretch which means "assign all items the container's height".
// The spirit of this request is make all items the same height, but when shrinkWrap
// height is also requested, the height of the tallest item determines the height.
// This is exactly what the stretchmax option does, so we jiggle the flags here to
// act as if stretchmax were requested.
if (align.stretch && heightModel.shrinkWrap) {
align.stretchmax = true;
align.stretch = false;
}
// This is handy for knowing that we might need to apply height %ages
align.nostretch = !(align.stretch || align.stretchmax);
// In our example hbox, packing items to the right (end) or center can only work if
// there is a container width. So, if we are shrinkWrap, we just turn off the pack
// options for the run.
if (widthModel.shrinkWrap) {
pack.center = pack.end = false;
}
me.cacheFlexes(ownerContext);
// We set the width of the target el equal to the width of the innerCt
// when the layout cycle is finished, so we need to clear the width here
// to prevent the children from being crushed.
// IE needs it because of its scrollIntoView bug: https://sencha.jira.com/browse/EXTJSIV-6520
// Webkit needs it because of its mouse drag bug: https://sencha.jira.com/browse/EXTJSIV-5962
// FF needs it because of a vertical tab bug: https://sencha.jira.com/browse/EXTJSIV-8614
me.targetEl.setWidth(20000);
},
/**
* This method is called to (re)cache our understanding of flexes.
* This happens during beginLayoutCycle and may need to be called again
* if the flexes are changed during the layout (e.g., like ColumnLayout).
* @param {Object} ownerContext
* @protected
*/
cacheFlexes: function(ownerContext) {
var me = this,
names = ownerContext.boxNames,
widthModelName = names.widthModel,
heightModelName = names.heightModel,
nostretch = ownerContext.boxOptions.align.nostretch,
totalFlex = 0,
childItems = ownerContext.childItems,
i = childItems.length,
flexedItems = [],
minWidth = 0,
smallestHeight = 0,
smallestWidth = 0,
minWidthName = names.minWidth,
minHeightName = names.minHeight,
percentageRe = me._percentageRe,
percentageWidths = 0,
percentageHeights = 0,
child, childContext, flex, match, heightModel, widthModel, width, height;
while (i--) {
childContext = childItems[i];
child = childContext.target;
widthModel = childContext[widthModelName];
// check widthModel to see if we are the sizing layout. If so, copy the flex
// from the item to the contextItem and add it to totalFlex
//
if (widthModel.calculated) {
childContext.flex = flex = child.flex;
if (flex) {
totalFlex += flex;
flexedItems.push(childContext);
minWidth += child[minWidthName] || 0;
}
// a %age width. Note that we are testing the value that is
// assigned to match.
else if ((match = percentageRe.exec(child[names.width]))) {
childContext.percentageParallel = parseFloat(match[1]) / 100;
++percentageWidths;
}
}
// the above means that "childContext.flex" is properly truthy/falsy, which is
// often times quite convenient...
if (widthModel.configured) {
width = child[names.width];
}
else {
width = child[minWidthName] || 0;
}
smallestWidth += width;
heightModel = childContext[heightModelName];
if (nostretch && heightModel.calculated) {
// the only reason we would be calculated height in this case is due to a
// height %age...
match = percentageRe.exec(child[names.height]);
childContext.percentagePerpendicular = parseFloat(match[1]) / 100;
++percentageHeights;
}
if (heightModel.configured) {
height = child[names.height];
}
else {
height = child[minHeightName] || 0;
}
if (height > smallestHeight) {
smallestHeight = height;
}
}
ownerContext.flexedItems = flexedItems;
ownerContext.flexedMinWidth = minWidth;
// These dimensions are the smallest possible dimensions (using known sizes) for
// the innerCt on each axis
ownerContext.smallestWidth = smallestWidth;
ownerContext.smallestHeight = smallestHeight;
ownerContext.totalFlex = totalFlex;
ownerContext.percentageWidths = percentageWidths;
ownerContext.percentageHeights = percentageHeights;
// The flexed boxes need to be sorted in ascending order of maxSize to work properly
// so that unallocated space caused by maxWidth being less than flexed width can be
// reallocated to subsequent flexed boxes.
Ext.Array.sort(flexedItems, me.flexSortFn);
},
calculate: function(ownerContext) {
var me = this,
names = ownerContext.boxNames,
state = ownerContext.state,
actualScroll = state.actualScroll,
needsScroll = state.needsScroll,
canScroll = state.canScroll,
plan = state.boxPlan || (state.boxPlan = {}),
overflowHandler = me.overflowHandler;
plan.targetSize = me.getContainerSize(ownerContext);
if (canScroll && !needsScroll) {
state.needsScroll = needsScroll = {
// Attempt to figure out early on if we need to scroll in the parallel direction.
// If the perpendicular is done and we need to scroll, we need to invalidate
// because it may need recalculation.
parallel: canScroll.parallel &&
plan.targetSize[names.width] < ownerContext.smallestWidth,
perpendicular: canScroll.perpendicular &&
plan.targetSize[names.height] < ownerContext.smallestHeight
};
}
if (!state.parallelDone) {
state.parallelDone = me.calculateParallel(ownerContext, names, plan);
}
if (!state.perpendicularDone) {
state.perpendicularDone = me.calculatePerpendicular(ownerContext, names, plan);
}
if (state.parallelDone && state.perpendicularDone) {
if (canScroll && !state.scrollPass) {
if (needsScroll.parallel !== actualScroll.parallel ||
needsScroll.perpendicular !== actualScroll.perpendicular) {
ownerContext.invalidate({
state: {
scrollPass: true,
canScroll: canScroll,
needsScroll: actualScroll
}
});
me.done = false;
return;
}
}
me.publishInnerCtSize(ownerContext);
// We always need to run calculateStretchMax, when relevant since we may
// have hit a constraint in an earlier calculation.
if (me.done && ownerContext.boxOptions.align.stretchmax && !state.stretchMaxDone) {
me.calculateStretchMax(ownerContext, names, plan);
state.stretchMaxDone = true;
}
if (overflowHandler) {
overflowHandler.calculate(ownerContext);
}
}
else {
me.done = false;
}
},
calculateParallel: function(ownerContext, names, plan) {
var me = this,
widthShrinkWrap = ownerContext.parallelSizeModel.shrinkWrap,
widthName = names.width,
childItems = ownerContext.childItems,
beforeXName = names.beforeX,
afterXName = names.afterX,
setWidthName = names.setWidth,
childItemsLength = childItems.length,
flexedItems = ownerContext.flexedItems,
flexedItemsLength = flexedItems.length,
pack = ownerContext.boxOptions.pack,
padding = me.padding,
targetSize = plan.targetSize,
containerWidth = targetSize[widthName],
state = ownerContext.state,
needsScroll = state.needsScroll,
canScroll = state.canScroll,
totalMargin = 0,
left = padding[beforeXName],
nonFlexWidth = left + padding[afterXName],
scrollbarSize = Ext.scrollbar.size(),
scrollbarWidth = scrollbarSize[names.width],
scrollbarHeight = scrollbarSize[names.height],
i, childMargins, remainingWidth, remainingFlex, childContext, flex, flexedWidth,
contentWidth, childWidth, percentageSpace, availableSpace;
// If we are not widthModel.shrinkWrap, we need the width before we can lay out boxes.
// This check belongs here so it does not prevent the perpendicular from attempting to
// calculate. It may have a dependency on the width, but it may be able to achieve
// the correct size without the width.
if (!widthShrinkWrap && !targetSize[names.gotWidth]) {
return false;
}
// Gather the total size taken up by non-flexed items:
for (i = 0; i < childItemsLength; ++i) {
childContext = childItems[i];
childMargins = childContext.marginInfo || childContext.getMarginInfo();
totalMargin += childMargins[widthName];
if (!childContext[names.widthModel].calculated) {
childWidth = childContext.getProp(widthName);
nonFlexWidth += childWidth; // min/maxWidth safe
if (isNaN(nonFlexWidth)) {
return false;
}
}
}
nonFlexWidth += totalMargin;
if (ownerContext.percentageWidths) {
percentageSpace = containerWidth - totalMargin;
if (isNaN(percentageSpace)) {
return false;
}
for (i = 0; i < childItemsLength; ++i) {
childContext = childItems[i];
if (childContext.percentageParallel) {
childWidth = Math.ceil(percentageSpace * childContext.percentageParallel);
childWidth = childContext[setWidthName](childWidth);
nonFlexWidth += childWidth;
}
}
}
// if we get here, we have all the childWidths for non-flexed items...
if (widthShrinkWrap) {
availableSpace = 0;
plan.tooNarrow = false;
}
else {
availableSpace = containerWidth - nonFlexWidth;
if (needsScroll && needsScroll.perpendicular) {
availableSpace -= scrollbarHeight;
}
plan.tooNarrow = availableSpace < ownerContext.flexedMinWidth;
if (plan.tooNarrow && canScroll && canScroll.parallel) {
state.actualScroll.parallel = true;
}
}
contentWidth = nonFlexWidth;
remainingWidth = availableSpace;
remainingFlex = ownerContext.totalFlex;
// Calculate flexed item sizes:
for (i = 0; i < flexedItemsLength; i++) {
childContext = flexedItems[i];
flex = childContext.flex;
flexedWidth = me.roundFlex((flex / remainingFlex) * remainingWidth);
flexedWidth = childContext[setWidthName](flexedWidth); // constrained
// due to minWidth constraints, it may be that flexedWidth > remainingWidth
contentWidth += flexedWidth;
// Remaining space has already had margins subtracted, so just subtract size
remainingWidth = Math.max(0, remainingWidth - flexedWidth); // no negatives!
remainingFlex -= flex;
}
if (pack.center) {
left += remainingWidth / 2;
// If content is too wide to pack to center, do not allow the centering calculation
// to place it off the left edge.
if (left < 0) {
left = 0;
}
}
else if (pack.end) {
left += remainingWidth;
}
// Assign parallel position for the boxes:
for (i = 0; i < childItemsLength; ++i) {
childContext = childItems[i];
childMargins = childContext.marginInfo; // already cached by first loop
left += childMargins[beforeXName];
childContext.setProp(names.x, left);
// We can read directly from "props.width" because we have already properly
// requested it in the calculation of nonFlexedWidths or we calculated it.
// We cannot call getProp because that would be inappropriate for flexed items
// and we don't need any extra function call overhead:
left += childMargins[afterXName] + childContext.props[widthName];
}
contentWidth += ownerContext.targetContext.getPaddingInfo()[widthName];
ownerContext.state.contentWidth = contentWidth;
// if there is perpendicular overflow, the published parallel content size includes
// the size of the perpendicular scrollbar.
if (needsScroll && needsScroll.perpendicular) {
if (widthShrinkWrap) {
contentWidth += scrollbarWidth;
}
ownerContext[names.hasOverflowY] = true;
// tell the component layout to set the parallel size in the dom
ownerContext.target.componentLayout[names.setWidthInDom] = true;
// IE8 will not create a scrollbar if there is just the *exactly correct*
// spare space created for it. We have to force that to happen once all the
// styles have been flushed to the DOM (see completeLayout):
ownerContext[names.invalidateScrollY] = Ext.isIE8;
}
ownerContext[names.setContentWidth](contentWidth);
return true;
},
calculatePerpendicular: function(ownerContext, names, plan) {
var me = this,
state = ownerContext.state,
needsScroll = state.needsScroll,
canScroll = state.canScroll,
heightShrinkWrap = ownerContext.perpendicularSizeModel.shrinkWrap,
targetSize = plan.targetSize,
childItems = ownerContext.childItems,
childItemsLength = childItems.length,
mmax = Math.max,
heightName = names.height,
setHeightName = names.setHeight,
beforeYName = names.beforeY,
topPositionName = names.y,
padding = me.padding,
top = padding[beforeYName],
availHeight = targetSize[heightName] - top - padding[names.afterY],
align = ownerContext.boxOptions.align,
isStretch = align.stretch, // never true if heightShrinkWrap (see beginLayoutCycle)
isStretchMax = align.stretchmax,
isCenter = align.center,
isBottom = align.bottom,
constrain = me.constrainAlign,
maxHeight = 0,
hasPercentageSizes = 0,
onBeforeInvalidateChild = me.onBeforeConstrainInvalidateChild,
onAfterInvalidateChild = me.onAfterConstrainInvalidateChild,
scrollbarHeight = Ext.scrollbar.height(),
childTop, i, childHeight, childMargins, diff, height, childContext,
stretchMaxPartner, stretchMaxChildren, shrinkWrapParallelOverflow,
percentagePerpendicular;
if (!heightShrinkWrap && !targetSize[names.gotHeight]) {
return false;
}
if (isStretch || ((isCenter || isBottom) && !heightShrinkWrap)) {
if (isNaN(availHeight)) {
return false;
}
}
// If the intention is to horizontally scroll child components, but the container
// is too narrow, then:
// if we are shrinkwrapping height:
// Set a flag because we are going to expand the height taken by the perpendicular
// dimension to accommodate the scrollbar
// else
// We must allow for the parallel scrollbar to intrude into the height
if (needsScroll && needsScroll.parallel) {
if (heightShrinkWrap) {
shrinkWrapParallelOverflow = true;
}
else {
availHeight -= scrollbarHeight;
plan.targetSize[heightName] -= scrollbarHeight;
}
}
if (isStretch) {
height = availHeight; // never heightShrinkWrap...
maxHeight = mmax(height, ownerContext.smallestHeight);
}
else {
for (i = 0; i < childItemsLength; i++) {
childContext = childItems[i];
childMargins =
(childContext.marginInfo || childContext.getMarginInfo())[heightName];
if (!(percentagePerpendicular = childContext.percentagePerpendicular)) {
childHeight = childContext.getProp(heightName);
}
else {
++hasPercentageSizes;
if (heightShrinkWrap) {
// height %age items cannot contribute to maxHeight... they are going
// to be a %age of that maxHeight!
continue;
}
else {
childHeight = percentagePerpendicular * availHeight - childMargins;
childHeight = childContext[setHeightName](childHeight);
}
}
// Summary:
// 1) Not shrink wrapping height, so the height is not determined by the children
// 2) Constrain is set
// 3) The child item is shrink wrapping
// 4) It exceeds the max
if (!heightShrinkWrap && constrain && childContext[names.heightModel].shrinkWrap &&
childHeight > availHeight) {
childContext.invalidate({
before: onBeforeInvalidateChild,
after: onAfterInvalidateChild,
layout: me,
childHeight: availHeight,
names: names
});
// By invalidating the height, it could mean the width can change, so we need
// to recalculate in the parallel direction.
ownerContext.state.parallelDone = false;
}
// Max perpendicular measurement (used for stretchmax) must take the min
// perpendicular size of each child into account in case any fall short.
if (isNaN(maxHeight = mmax(maxHeight, childHeight + childMargins,
childContext.target[names.minHeight] || 0))) {
return false; // heightShrinkWrap || isCenter || isStretchMax ??
}
}
}
// If there is going to be a parallel scrollbar maxHeight must include it
// to the outside world.
// ie: a stretchmaxPartner, and the setContentHeight
if (shrinkWrapParallelOverflow) {
maxHeight += scrollbarHeight;
ownerContext[names.hasOverflowX] = true;
// tell the component layout to set the perpendicular size in the dom
ownerContext.target.componentLayout[names.setHeightInDom] = true;
// IE8 will not create a scrollbar if there is just the *exactly correct*
// spare space created for it. We have to force that to happen once all
// the styles have been flushed to the DOM (see completeLayout):
ownerContext[names.invalidateScrollX] = Ext.isIE8;
}
// If we are associated with another box layout, grab its maxChildHeight
// This must happen before we calculate and publish our contentHeight
stretchMaxPartner = ownerContext.stretchMaxPartner;
if (stretchMaxPartner) {
// Publish maxChildHeight as soon as it has been calculated for our partner:
ownerContext.setProp('maxChildHeight', maxHeight);
stretchMaxChildren = stretchMaxPartner.childItems;
// Only wait for maxChildHeight if our partner has visible items:
if (stretchMaxChildren && stretchMaxChildren.length) {
maxHeight = mmax(maxHeight, stretchMaxPartner.getProp('maxChildHeight'));
if (isNaN(maxHeight)) {
return false;
}
}
}
ownerContext[names.setContentHeight](maxHeight + me.padding[heightName] +
ownerContext.targetContext.getPaddingInfo()[heightName]);
// We have to publish the contentHeight with the additional scrollbarHeight
// to encourage our container to accommodate it, but we must remove the height
// of the scrollbar as we go to sizing or centering the children.
if (shrinkWrapParallelOverflow) {
maxHeight -= scrollbarHeight;
}
if (maxHeight > targetSize[heightName] && canScroll && canScroll.perpendicular) {
state.actualScroll.perpendicular = true;
}
plan.maxSize = maxHeight;
if (isStretchMax) {
height = maxHeight;
}
else if (isCenter || isBottom || hasPercentageSizes) {
if (constrain) {
height = heightShrinkWrap ? maxHeight : availHeight;
}
else {
height = heightShrinkWrap ? maxHeight : mmax(availHeight, maxHeight);
}
// When calculating a centered position within the content box of the innerCt,
// the width of the borders must be subtracted from the size to yield the
// space available to center within. The publishInnerCtSize method explicitly
// adds the border widths to the set size of the innerCt.
height -= ownerContext.innerCtContext.getBorderInfo()[heightName];
}
for (i = 0; i < childItemsLength; i++) {
childContext = childItems[i];
childMargins = childContext.marginInfo || childContext.getMarginInfo();
childTop = top + childMargins[beforeYName];
if (isStretch) {
childContext[setHeightName](height - childMargins[heightName]);
}
else {
percentagePerpendicular = childContext.percentagePerpendicular;
if (heightShrinkWrap && percentagePerpendicular) {
childMargins = childContext.marginInfo || childContext.getMarginInfo();
childHeight = percentagePerpendicular * height - childMargins[heightName];
childHeight = childContext[setHeightName](childHeight);
}
if (isCenter) {
diff = height - childContext.props[heightName];
if (diff > 0) {
childTop = top + Math[me.alignRoundingMethod](diff / 2);
}
}
else if (isBottom) {
childTop = mmax(0, height - childTop - childContext.props[heightName]);
}
}
childContext.setProp(topPositionName, childTop);
}
return true;
},
onBeforeConstrainInvalidateChild: function(childContext, options) {
// NOTE: No "this" pointer in here...
var heightModelName = options.names.heightModel;
if (!childContext[heightModelName].constrainedMin) {
// if the child hit a min constraint, it needs to be at its configured size, so
// we leave the sizeModel alone
childContext[heightModelName] = Ext.layout.SizeModel.calculated;
}
},
onAfterConstrainInvalidateChild: function(childContext, options) {
// NOTE: No "this" pointer in here...
var names = options.names;
// We use 0 here because we know the size exceeds the available size.
// This was chosen on purpose, even for align: 'bottom', because it doesn't
// make practical sense to place the item at the bottom and then have it overflow
// over the top of the container, since it's not possible to scroll to it. As such,
// we always put the component at the top to follow normal document flow.
childContext.setProp(names.beforeY, 0);
if (childContext[names.heightModel].calculated) {
childContext[names.setHeight](options.childHeight);
}
},
calculateStretchMax: function(ownerContext, names, plan) {
var me = this,
heightName = names.height,
widthName = names.width,
childItems = ownerContext.childItems,
length = childItems.length,
height = plan.maxSize,
onBeforeStretchMaxInvalidateChild = me.onBeforeStretchMaxInvalidateChild,
onAfterStretchMaxInvalidateChild = me.onAfterStretchMaxInvalidateChild,
childContext, props, i, childHeight;
for (i = 0; i < length; ++i) {
childContext = childItems[i];
props = childContext.props;
childHeight = height - childContext.getMarginInfo()[heightName];
if (childHeight !== props[heightName] || // if (wrong height ...
childContext[names.heightModel].constrained) { // ...or needs invalidation)
// When we invalidate a child, since we won't be around to size or position
// it, we include an after callback that will be run after the invalidate
// that will (re)do that work. The good news here is that we can read the
// results of all that from the childContext props.
//
// We also include a before callback to change the sizeModel to calculated
// prior to the layout being invoked.
childContext.invalidate({
before: onBeforeStretchMaxInvalidateChild,
after: onAfterStretchMaxInvalidateChild,
layout: me,
// passing this data avoids a 'scope' and its Function.bind
childWidth: props[widthName],
// subtract margins from the maximum value
childHeight: childHeight,
childX: props.x,
childY: props.y,
names: names
});
}
}
},
onBeforeStretchMaxInvalidateChild: function(childContext, options) {
// NOTE: No "this" pointer in here...
var heightModelName = options.names.heightModel;
// Change the childItem to calculated (i.e., "set by ownerCt"). The component layout
// of the child can course-correct (like dock layout does for a collapsed panel),
// so we must make these changes here before that layout's beginLayoutCycle is
// called.
if (!childContext[heightModelName].constrainedMax) {
// if the child hit a max constraint, it needs to be at its configured size, so
// we leave the sizeModel alone...
childContext[heightModelName] = Ext.layout.SizeModel.calculated;
}
},
onAfterStretchMaxInvalidateChild: function(childContext, options) {
// NOTE: No "this" pointer in here...
var names = options.names,
childHeight = options.childHeight,
childWidth = options.childWidth;
childContext.setProp('x', options.childX);
childContext.setProp('y', options.childY);
if (childContext[names.heightModel].calculated) {
// We need to respect a child that is still not calculated (such as a collapsed
// panel)...
childContext[names.setHeight](childHeight);
}
if (childContext[names.widthModel].calculated) {
childContext[names.setWidth](childWidth);
}
},
completeLayout: function(ownerContext) {
var me = this,
invalidateScrollX = ownerContext.invalidateScrollX,
invalidateScrollY = ownerContext.invalidateScrollY,
overflowHandler = me.overflowHandler,
dom, el, overflowX, overflowY, styles;
if (overflowHandler) {
overflowHandler.completeLayout(ownerContext);
}
if (invalidateScrollX || invalidateScrollY) {
el = me.getTarget();
dom = el.dom;
styles = dom.style;
if (invalidateScrollX) {
// get computed style to see if we are 'auto'
overflowX = el.getStyle('overflowX');
if (overflowX === 'auto') {
// capture the inline style (if any) so we can restore it later:
overflowX = styles.overflowX;
styles.overflowX = 'scroll'; // force the scrollbar to appear
}
else {
invalidateScrollX = false; // no work really since not 'auto'
}
}
if (invalidateScrollY) {
// get computed style to see if we are 'auto'
overflowY = el.getStyle('overflowY');
if (overflowY === 'auto') {
// capture the inline style (if any) so we can restore it later:
overflowY = styles.overflowY;
styles.overflowY = 'scroll'; // force the scrollbar to appear
}
else {
invalidateScrollY = false; // no work really since not 'auto'
}
}
if (invalidateScrollX || invalidateScrollY) { // if (some form of 'auto' in play)
// force a reflow...
// eslint-disable-next-line no-unused-expressions
dom.scrollWidth;
if (invalidateScrollX) {
styles.overflowX = overflowX; // restore inline style
}
if (invalidateScrollY) {
styles.overflowY = overflowY; // restore inline style
}
}
}
},
finishedLayout: function(ownerContext) {
var overflowHandler = this.overflowHandler;
if (overflowHandler) {
overflowHandler.finishedLayout(ownerContext);
}
this.callParent([ownerContext]);
},
getLayoutItems: function() {
var items = this.callParent(),
n = items.length,
lastVisibleItem, hide, i, item, splitAfter, splitBefore, splitter;
for (i = 0; i < n; ++i) {
if ((item = items[i]).isSplitter) {
continue;
}
splitter = item.splitter;
if (item.hidden) {
if (splitter) {
// hidden items always need to hide their splitter
if (!splitter.hidden) {
splitter.hidden = true;
if (splitter.el) {
splitter.el.hide();
}
}
}
continue;
}
if (splitter) {
splitBefore = splitter.collapseTarget === 'next';
}
else { // item w/o splitter
splitBefore = false;
}
hide = null;
if (lastVisibleItem && splitAfter) {
// the last item had a splitter after it so we can keep it and hide
// this one if splitBefore
if (splitAfter.hidden) {
splitAfter.hidden = false;
if (splitAfter.el) {
splitAfter.el.show();
}
}
if (splitBefore) {
hide = true;
}
}
else if (splitBefore) {
hide = !lastVisibleItem;
}
// else we have no splitter or are !splitBefore, so we defer the fate of this
// splitter
if (hide !== null && splitter.hidden !== hide) {
splitter.hidden = hide;
if (splitter.el) {
splitter.el.setVisible(!hide);
}
}
splitAfter = !splitBefore && splitter;
lastVisibleItem = item;
}
// If we ended with a visible item and a splitAfter, we need to hide the tail
// splitter
if (lastVisibleItem && splitAfter && !splitAfter.hidden) {
splitAfter.hidden = true;
if (splitAfter.el) {
splitAfter.el.hide();
}
}
return items;
},
/**
* 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 splitter = {
xtype: 'splitter',
id: item.id + '-splitter',
hidden: hidden,
splitterFor: item,
synthetic: true // not user-defined
},
at = index + ((splitterCfg.collapseTarget === 'prev') ? 1 : 0);
splitter[this.names.height] = '100%';
if (splitterCfg) {
Ext.apply(splitter, splitterCfg);
}
item.splitter = this.owner.add(at, splitter);
},
publishInnerCtSize: function(ownerContext, widthOffset) {
var me = this,
state = ownerContext.state,
names = ownerContext.boxNames,
heightName = names.height,
widthName = names.width,
align = ownerContext.boxOptions.align,
padding = me.padding,
plan = state.boxPlan,
targetSize = plan.targetSize,
height = plan.maxSize,
needsScroll = state.needsScroll,
innerCtContext = ownerContext.innerCtContext,
innerCtWidth, innerCtHeight;
widthOffset = widthOffset || 0;
// The state.canScroll check is on purpose here, all we want to know is whether we have
// a scrollable instance, since even if UI scrolling isn't available, we may scroll it
// programmatically
if (ownerContext.parallelSizeModel.shrinkWrap || (plan.tooNarrow && state.canScroll)) {
innerCtWidth = state.contentWidth -
ownerContext.targetContext.getPaddingInfo()[widthName];
}
else {
innerCtWidth = targetSize[widthName];
if (needsScroll && needsScroll.perpendicular) {
innerCtWidth -= Ext.scrollbar.size()[widthName];
}
}
innerCtWidth -= widthOffset;
// Allow the other co-operating objects to know whether the columns overflow
// the available width.
me.owner.tooNarrow = plan.tooNarrow;
if (align.stretch) {
innerCtHeight = height;
}
else {
innerCtHeight = plan.maxSize + padding[names.beforeY] + padding[names.afterY] +
innerCtContext.getBorderInfo()[heightName];
if (!ownerContext.perpendicularSizeModel.shrinkWrap && (align.center || align.bottom)) {
innerCtHeight = Math.max(targetSize[heightName], innerCtHeight);
}
}
innerCtContext[names.setWidth](innerCtWidth);
innerCtContext[names.setHeight](innerCtHeight);
// Fix for an obscure webkit bug (EXTJSIV-5962) caused by the targetEl's 20000px
// width. We set a very large width on the targetEl at the beginning of the
// layout cycle to prevent any "crushing" effect on the child items, however
// in some cases the very large width makes it possible to scroll the innerCt
// by dragging on certain child elements. To prevent this from happening we ensure
// that the targetEl's width is the same as the innerCt.
// IE needs it because of its scrollIntoView bug: https://sencha.jira.com/browse/EXTJSIV-6520
// Webkit needs it because of its mouse drag bug: https://sencha.jira.com/browse/EXTJSIV-5962
// FF needs it because of a vertical tab bug: https://sencha.jira.com/browse/EXTJSIV-8614
ownerContext.targetElContext.setWidth(ownerContext.innerCtContext.props.width -
(me.vertical ? 0 : (widthOffset || 0)));
// If unable to publish both dimensions, this layout needs to run again
if (isNaN(innerCtWidth + innerCtHeight)) {
me.done = false;
}
},
onAdd: function(item, index) {
var me = this,
// Buttons have their own concept of "split" config
split = me.enableSplitters && !item.isButton && item.split;
me.callParent([item, index]);
if (split) {
if (split === true) {
split = {
collapseTarget: 'next'
};
}
else if (Ext.isString(split)) {
split = {
collapseTarget: split === 'before' ? 'next' : 'prev'
};
}
else {
split = Ext.apply({
collapseTarget: split.side === 'before' ? 'next' : 'prev'
}, split);
}
me.insertSplitter(item, index, !!item.hidden, split);
}
},
onRemove: function(comp, isDestroying) {
var me = this,
names = me.names,
owner = me.owner,
splitter = comp.splitter,
overflowHandler = me.overflowHandler,
el;
me.callParent([comp, isDestroying]);
if (splitter && owner.contains(splitter)) {
owner.doRemove(splitter, true);
comp.splitter = null;
}
if (overflowHandler) {
overflowHandler.onRemove(comp);
}
if (comp.layoutMarginCap === me.id) {
delete comp.layoutMarginCap;
}
if (!owner.destroying && !isDestroying && comp.rendered) {
// Clear top/left styles
el = comp.getEl();
if (el) {
el.setStyle(names.beforeY, '');
el.setStyle(names.beforeX, '');
// Box layout imposes margin:0 on its child items and the layout provides margins
// using its absolute positioning strategy. This has to be reversed on remove.
el.setStyle('margin', '');
}
}
},
applyOverflowHandler: function(overflowHandler, oldOverflowHandler) {
if (typeof overflowHandler === 'string') {
overflowHandler = {
type: overflowHandler
};
}
if (oldOverflowHandler && oldOverflowHandler.type === overflowHandler.type) {
delete overflowHandler.type;
oldOverflowHandler.setConfig(overflowHandler);
return oldOverflowHandler;
}
overflowHandler.layout = this;
return Ext.Factory.boxOverflow(overflowHandler);
},
// Overridden method from Ext.layout.container.Container.
// Used in the beforeLayout method to render all items into.
getRenderTarget: function() {
return this.targetEl;
},
// Overridden method from Ext.layout.container.Container.
// Used by Container classes to insert special DOM elements which must exist
// in addition to the child components
getElementTarget: function() {
return this.innerCt;
},
//<debug>
calculateChildBox: Ext.deprecated(),
calculateChildBoxes: Ext.deprecated(),
updateChildBoxes: Ext.deprecated(),
//</debug>
destroy: function() {
var me = this;
Ext.destroy(me.innerCt, me.overflowHandler);
me.flexSortFn = me.innerCt = null;
me.callParent();
},
getRenderData: function() {
var data = this.callParent();
data.targetElCls = this.targetElCls;
return data;
},
updateVertical: function(vertical) {
var me = this,
overflowHandler = me.overflowHandler,
owner = me.owner,
props = me._props;
Ext.apply(me, vertical ? props.vbox : props.hbox);
if (overflowHandler && owner && owner.rendered) {
overflowHandler.setVertical(vertical);
}
},
_props: {
// HBOX - this key is produced by setVertical
'hbox': {
direction: 'horizontal',
oppositeDirection: 'vertical',
horizontal: true,
vertical: false,
names: {
// parallel
beforeX: 'left',
beforeScrollX: 'left',
leftCap: 'Left',
afterX: 'right',
width: 'width',
contentWidth: 'contentWidth',
minWidth: 'minWidth',
maxWidth: 'maxWidth',
widthCap: 'Width',
widthModel: 'widthModel',
widthIndex: 0,
x: 'x',
getX: 'getX',
setX: 'setX',
scrollLeft: 'scrollLeft',
overflowX: 'overflowX',
hasOverflowX: 'hasOverflowX',
invalidateScrollX: 'invalidateScrollX',
parallelMargins: 'lr',
// perpendicular
center: 'middle',
beforeY: 'top',
afterY: 'bottom',
height: 'height',
contentHeight: 'contentHeight',
minHeight: 'minHeight',
maxHeight: 'maxHeight',
heightCap: 'Height',
heightModel: 'heightModel',
heightIndex: 1,
y: 'y',
getY: 'getY',
setY: 'setY',
overflowY: 'overflowY',
hasOverflowY: 'hasOverflowY',
invalidateScrollY: 'invalidateScrollY',
perpendicularMargins: 'tb',
// Methods
getWidth: 'getWidth',
getHeight: 'getHeight',
setWidth: 'setWidth',
setHeight: 'setHeight',
gotWidth: 'gotWidth',
gotHeight: 'gotHeight',
setContentWidth: 'setContentWidth',
setContentHeight: 'setContentHeight',
setWidthInDom: 'setWidthInDom',
setHeightInDom: 'setHeightInDom',
getScrollLeft: 'getScrollLeft',
setScrollLeft: 'setScrollLeft',
scrollTo: 'scrollTo'
},
sizePolicy: {
flex: {
'': {
readsWidth: 0,
readsHeight: 1,
setsWidth: 1,
setsHeight: 0
},
stretch: {
readsWidth: 0,
readsHeight: 0,
setsWidth: 1,
setsHeight: 1
},
stretchmax: {
readsWidth: 0,
readsHeight: 1,
setsWidth: 1,
setsHeight: 1
}
},
'': {
readsWidth: 1,
readsHeight: 1,
setsWidth: 0,
setsHeight: 0
},
stretch: {
readsWidth: 1,
readsHeight: 0,
setsWidth: 0,
setsHeight: 1
},
stretchmax: {
readsWidth: 1,
readsHeight: 1,
setsWidth: 0,
setsHeight: 1
}
}
},
// VBOX
'vbox': {
direction: 'vertical',
oppositeDirection: 'horizontal',
horizontal: false,
vertical: true,
names: {
// parallel
beforeX: 'top',
beforeScrollX: 'top',
leftCap: 'Top',
afterX: 'bottom',
width: 'height',
contentWidth: 'contentHeight',
minWidth: 'minHeight',
maxWidth: 'maxHeight',
widthCap: 'Height',
widthModel: 'heightModel',
widthIndex: 1,
x: 'y',
getX: 'getY',
setX: 'setY',
scrollLeft: 'scrollTop',
overflowX: 'overflowY',
hasOverflowX: 'hasOverflowY',
invalidateScrollX: 'invalidateScrollY',
parallelMargins: 'tb',
// perpendicular
center: 'center',
beforeY: 'left',
afterY: 'right',
height: 'width',
contentHeight: 'contentWidth',
minHeight: 'minWidth',
maxHeight: 'maxWidth',
heightCap: 'Width',
heightModel: 'widthModel',
heightIndex: 0,
y: 'x',
getY: 'getX',
setY: 'setX',
overflowY: 'overflowX',
hasOverflowY: 'hasOverflowX',
invalidateScrollY: 'invalidateScrollX',
perpendicularMargins: 'lr',
// Methods
getWidth: 'getHeight',
getHeight: 'getWidth',
setWidth: 'setHeight',
setHeight: 'setWidth',
gotWidth: 'gotHeight',
gotHeight: 'gotWidth',
setContentWidth: 'setContentHeight',
setContentHeight: 'setContentWidth',
setWidthInDom: 'setHeightInDom',
setHeightInDom: 'setWidthInDom',
getScrollLeft: 'getScrollTop',
setScrollLeft: 'setScrollTop',
scrollTo: 'scrollTo'
},
sizePolicy: {
flex: {
'': {
readsWidth: 1,
readsHeight: 0,
setsWidth: 0,
setsHeight: 1
},
stretch: {
readsWidth: 0,
readsHeight: 0,
setsWidth: 1,
setsHeight: 1
},
stretchmax: {
readsWidth: 1,
readsHeight: 0,
setsWidth: 1,
setsHeight: 1
}
},
'': {
readsWidth: 1,
readsHeight: 1,
setsWidth: 0,
setsHeight: 0
},
stretch: {
readsWidth: 0,
readsHeight: 1,
setsWidth: 1,
setsHeight: 0
},
stretchmax: {
readsWidth: 1,
readsHeight: 1,
setsWidth: 1,
setsHeight: 0
}
}
}
}
});