/** * @class Ext.layout.container.Auto * * The AutoLayout is the default layout manager delegated by {@link Ext.container.Container} to * render any child Components when no `{@link Ext.container.Container#layout layout}` is configured into * a `{@link Ext.container.Container Container}.` AutoLayout provides only a passthrough of any layout calls * to any child containers. * * @example * Ext.create('Ext.Panel', { * width: 500, * height: 280, * title: "AutoLayout Panel", * layout: 'auto', * renderTo: document.body, * items: [{ * xtype: 'panel', * title: 'Top Inner Panel', * width: '75%', * height: 90 * }, * { * xtype: 'panel', * title: 'Bottom Inner Panel', * width: '75%', * height: 90 * }] * }); */ Ext.define('Ext.layout.container.Auto', { /* Begin Definitions */ alias: ['layout.auto', 'layout.autocontainer'], extend: 'Ext.layout.container.Container', /* End Definitions */ type: 'autocontainer', childEls: [ 'outerCt', 'innerCt', 'clearEl' ], /** * @cfg {Boolean} [reserveScrollbar=false] * Set to `true` to leave space for a vertical scrollbar (if the OS shows space-consuming scrollbars) regardless * of whether a scrollbar is needed. * * This is useful if content height changes during application usage, but you do not want the calculated width * of child items to change when a scrollbar appears or disappears. The scrollbar will appear in the reserved space, * and the calculated width of child Components will not change. * * @example * Ext.define('Employee', { * extend: 'Ext.data.Model', * fields: [ * {name: 'rating', type: 'int'}, * {name: 'salary', type: 'float'}, * {name: 'name'} * ] * }); * * function createFakeData(count) { * var firstNames = ['Ed', 'Tommy', 'Aaron', 'Abe', 'Jamie', 'Adam', 'Dave', 'David', 'Jay', 'Nicolas', 'Nige'], * lastNames = ['Spencer', 'Maintz', 'Conran', 'Elias', 'Avins', 'Mishcon', 'Kaneda', 'Davis', 'Robinson', 'Ferrero', 'White'], * ratings = [1, 2, 3, 4, 5], * salaries = [100, 400, 900, 1500, 1000000]; * * var data = []; * for (var i = 0; i < (count || 25); i++) { * var ratingId = Math.floor(Math.random() * ratings.length), * salaryId = Math.floor(Math.random() * salaries.length), * firstNameId = Math.floor(Math.random() * firstNames.length), * lastNameId = Math.floor(Math.random() * lastNames.length), * * rating = ratings[ratingId], * salary = salaries[salaryId], * name = Ext.String.format("{0} {1}", firstNames[firstNameId], lastNames[lastNameId]); * * data.push({ * rating: rating, * salary: salary, * name: name * }); * } * store.loadData(data); * } * * // create the Data Store * var store = Ext.create('Ext.data.Store', { * id: 'store', * model: 'Employee', * proxy: { * type: 'memory' * } * }); * createFakeData(10); * * var grid = Ext.create('Ext.grid.Panel', { * title: 'Grid loaded with varying number of records', * anchor: '100%', * store: store, * columns: [{ * xtype: 'rownumberer', * width: 40, * sortable: false * },{ * text: 'Name', * flex: 1, * sortable: true, * dataIndex: 'name' * },{ * text: 'Rating', * width: 125, * sortable: true, * dataIndex: 'rating' * },{ * text: 'Salary', * width: 125, * sortable: true, * dataIndex: 'salary', * align: 'right', * renderer: Ext.util.Format.usMoney * }] * }); * * Ext.create('Ext.panel.Panel', { * renderTo: document.body, * width: 800, * height: 600, * layout: { * type: 'anchor', * reserveScrollbar: true // There will be a gap even when there's no scrollbar * }, * autoScroll: true, * items: grid, * tbar: { * defaults: { * handler: function(b) { * createFakeData(b.count); * } * }, * items: [{ * text: '10 Items', * count: 10 * },{ * text: '100 Items', * count: 100 * },{ * text: '300 Items', * count: 300 * },{ * text: '1000 Items', * count: 1000 * },{ * text: '5000 Items', * count: 5000 * }] * } * }); * */ reserveScrollbar: false, /** * @property {Boolean} [managePadding=true] * indicates that this layout will correct cross browser padding differences when the * container has overflow. * * In some browsers the right and/or bottom padding of a container is lost when * the container has overflow. If managePadding is true the layout will apply the * padding to an inner wrapping element instead of the container element that has the * overflow so that paddding will be included in the scrollable area. * Note: padding will not be managed if it is configured on the container using * a style config or css class. In order to be managed, padding must be added to the * container using the appropriate {@link Ext.AbstractComponent#contentPaddingProperty * contentPaddingProperty}. For {@link Ext.panel.Panel Panels} use * {@link Ext.panel.AbstractPanel#bodyPadding}, and for * {@link Ext.container.Container Containers}, use * {@link Ext.AbstractComponent#padding padding} */ managePadding: true, /** * @property {Boolean} [manageOverflow=false] * true to rerun the layout if scrollbars are needed. */ manageOverflow: false, // Begin with no previous adjustments lastOverflowAdjust: { width: 0, height: 0 }, // Auto layout's renderTpl wraps the content in an outerCt which is used to accomplish // the following 3 goals: // // 1. When the container has a shrink wrapped width and/or height, the outerCt is used // to measure the size of the content. // 2. When the container has overflow some browsers lose the container's right and/or // bottom padding. To fix this, the padding is rendered to the outerCt instead of // the container target element. This ensures that the padding is included in the // container's scrollWidth/scrollHeight. In Old IE when a table is used, the padding // is rendered to the innerCt td element. // 3. The outerCt contains the margins of its children, that is to say, it prevents // them from collapsing. renderTpl: [ '{% if (!(Ext.isIEQuirks || Ext.isIE7m)) { %}', // All browsers that support display:table use this template. // An outerCt with display:table shrink-wraps contents, and contains child // margins. The table-cell innerCt is required in order to support percentage // heights on child elements. Originally the outerCt started out as a div, but // was changed to a span to work around an obscure firefox 3.6 bug where // placing a Container inside of a fieldset's legend element causes the legend // to blow up if the outerCt is a div. '<span id="{ownerId}-outerCt" style="display:table;">', // height:100% is required on the innerCt in order for percentage-height // children to work in IE, firefox, and opera '<div id="{ownerId}-innerCt" style="display:table-cell;height:100%;', 'vertical-align:top;{%this.renderPadding(out, values)%}" class="{innerCtCls}">', '{%this.renderBody(out,values)%}', '</div>', '</span>', '{% } else if (values.shrinkWrapWidth) { %}', // If the containers width is shrink wrapped a table-based outerCt/innerCt // is required in old IE. See getRenderData() for more details on the criteria // used to determine if the container has shrink wrapped width. '<table id="{ownerId}-outerCt" class="' + Ext.plainTableCls + '">', '<tr>', '<td id="{ownerId}-innerCt" style="vertical-align:top;padding:0;', '{%this.renderPadding(out, values)%}" class="{innerCtCls}">', '{%this.renderBody(out,values)%}', // clear element to contain the bottom margin of floated last child item '<div id="{ownerId}-clearEl" class="', Ext.baseCSSPrefix, 'clear"', 'role="presentation"></div>', '</td>', '</tr>', '</table>', '{% } else { %}', // If the container's width is not shrink wrapped, old IE can get by with // divs as the outerCt/innerCt. zoom:1 is required to contain the margins // of children. The padding is placed on the outerCt instead of the innerCt. // This is to because if the padding was placed on the innerCt, the top // margin of the first child item would collapse into the top padding of // the innerCt. '<div id="{ownerId}-outerCt" style="zoom:1;{%this.renderPadding(out, values)%}">', '<div id="{ownerId}-innerCt" style="zoom:1;height:100%;" class="{innerCtCls}">', '{%this.renderBody(out,values)%}', // clear element to contain the bottom margin of floated last child item '<div id="{ownerId}-clearEl" class="', Ext.baseCSSPrefix, 'clear"', 'role="presentation"></div>', '</div>', '</div>', // set a flag that indicates we are not using a "shrink wrap" template '{% values.$layout.isShrinkWrapTpl = false %}', '{% } %}' ], // This template is used for dynamically inserting a table outerCt/innerCt when needed. // It should be identical to the table template defined in renderTpl except that it // does not have renderBody or clearEl. It is an empty shell so that the contents // of an already existing innerCt can be moved into it. tableTpl: [ '<table id="{ownerId}-outerCt" class="' + Ext.plainTableCls + '">', '<tr>', '<td id="{ownerId}-innerCt" style="vertical-align:top;padding:0;', '{%this.renderPadding(out, values)%}" class="{innerCtCls}">', '</td>', '</tr>', '</table>' ], isShrinkWrapTpl: true, beginLayout: function(ownerContext) { var me = this, bottomPadding, overflowYStyle, overflowXStyle, needsTable; me.callParent(arguments); me.initContextItems(ownerContext); if (!me.isShrinkWrapTpl) { // In most cases the determination to use a table based template is made at // render time; however, if the initial determination was incorrect, we may // need to dynamically replace the existing outerCt/innerCt with a table // (see insertTableCt) if (ownerContext.widthModel.shrinkWrap) { needsTable = true; } // in IE7 strict right padding is lost when there is horizontal overflow // unless the outerCt is a table. if (Ext.isStrict && Ext.isIE7) { overflowXStyle = me.getOverflowXStyle(ownerContext); if ((overflowXStyle === 'auto' || overflowXStyle === 'scroll') && ownerContext.paddingContext.getPaddingInfo().right) { needsTable = true; } } if (needsTable) { me.insertTableCt(ownerContext); } } // When using the non-table renderTpl IE7 strict loses bottom padding if there // is vertical overflow. To adjust for the loss of padding, we add the bottom // padding to the height of the clearEl if (!me.isShrinkWrapTpl && Ext.isIE7 && Ext.isStrict && !me.clearElHasPadding) { bottomPadding = ownerContext.paddingContext.getPaddingInfo().bottom; overflowYStyle = me.getOverflowYStyle(ownerContext); if (bottomPadding && (overflowYStyle === 'auto' || overflowYStyle === 'scroll')) { me.clearEl.setStyle('height', bottomPadding); me.clearElHasPadding = true; } } }, beforeLayoutCycle: function(ownerContext){ var comp = this.owner, hierarchyState = comp.hierarchyState, hierarchyStateInner = comp.hierarchyStateInner; if (!hierarchyState || hierarchyState.invalid) { hierarchyState = comp.getHierarchyState(); // fixes both hierarchyStateInner = comp.hierarchyStateInner; } if (ownerContext.widthModel.shrinkWrap && this.isShrinkWrapTpl) { hierarchyStateInner.inShrinkWrapTable = true; } else { delete hierarchyStateInner.inShrinkWrapTable; } }, beginLayoutCycle: function(ownerContext) { var me = this, outerCt = me.outerCt, lastOuterCtWidth = me.lastOuterCtWidth || '', lastOuterCtHeight = me.lastOuterCtHeight || '', lastOuterCtTableLayout = me.lastOuterCtTableLayout || '', state = ownerContext.state, overflowXStyle, overflowYStyle, outerCtWidth, outerCtHeight, outerCtTableLayout, deferWidth, hierarchyStateInner; me.callParent(arguments); // Default to "shrink wrap styles". outerCtWidth = outerCtHeight = outerCtTableLayout = ''; if (!ownerContext.widthModel.shrinkWrap && me.isShrinkWrapTpl) { // if we're not shrink wrapping width, but we're using a shrink wrap template // we need to get the innerCt out of the way to avoid any shrink wrapping // effect on child items if (Ext.isIE7m && Ext.isStrict) { overflowYStyle = me.getOverflowYStyle(ownerContext); if (overflowYStyle === 'auto' || overflowYStyle === 'scroll') { // IE6/7 strict will have the outerCt's width set by setCtSizeIfNeeded() // when the container has potential vertical overflow, so there is // no need to set the outerCt's width to 100% here deferWidth = true; } } if (!deferWidth) { // fill the available width within the container outerCtWidth = '100%'; } hierarchyStateInner = me.owner.hierarchyStateInner; // expand no further than the available width, even if contents are wider // unless there is a potential for horizontal overflow, then allow // the outerCt to expand to the width of the contents overflowXStyle = me.getOverflowXStyle(ownerContext); outerCtTableLayout = (hierarchyStateInner.inShrinkWrapTable || overflowXStyle === 'auto' || overflowXStyle === 'scroll') ? '' : 'fixed'; } if (!ownerContext.heightModel.shrinkWrap && !Ext.supports.PercentageHeightOverflowBug) { // if we're not shrink wrapping height, we need to get the outerCt out of the // way so that percentage height children will be sized correctly. We do this // by giving the outerCt a height of '100%' unless the browser is affected by // the "percentage height overflow bug", in which case the outerCt will get a // pixel height set during the calculate phase after we know the targetEl size. outerCtHeight = '100%'; } // if the outerCt width changed since last time (becuase of a widthModel change) // or if we set a pixel width on the outerCt last time to work around a browser- // specific bug, we need to set the width of the outerCt if ((outerCtWidth !== lastOuterCtWidth) || me.hasOuterCtPxWidth) { outerCt.setStyle('width', outerCtWidth); me.lastOuterCtWidth = outerCtWidth; me.hasOuterCtPxWidth = false; } // Set the outerCt table-layout property if different from last time. if (outerCtTableLayout !== lastOuterCtTableLayout) { outerCt.setStyle('table-layout', outerCtTableLayout); me.lastOuterCtTableLayout = outerCtTableLayout; } // if the outerCt height changed since last time (becuase of a heightModel change) // or if we set a pixel height on the outerCt last time to work around a browser- // specific bug, we need to set the height of the outerCt if ((outerCtHeight !== lastOuterCtHeight) || me.hasOuterCtPxHeight) { outerCt.setStyle('height', outerCtHeight); me.lastOuterCtHeight = outerCtHeight; me.hasOuterCtPxHeight = false; } if (me.hasInnerCtPxHeight) { me.innerCt.setStyle('height', ''); me.hasInnerCtPxHeight = false; } // Begin with the scrollbar adjustment that we used last time - this is more likely // to be correct than beginning with no adjustment at all, but only if it is not // already defined - it may have already been set by invalidate() state.overflowAdjust = state.overflowAdjust || me.lastOverflowAdjust; }, calculate: function(ownerContext) { var me = this, state = ownerContext.state, containerSize = me.getContainerSize(ownerContext, true), // If subclass has a calculateItems method, call it and cache the result calculatedItems = state.calculatedItems || (state.calculatedItems = me.calculateItems ? me.calculateItems(ownerContext, containerSize) : true); me.setCtSizeIfNeeded(ownerContext, containerSize); if (calculatedItems && ownerContext.hasDomProp('containerChildrenSizeDone')) { me.calculateContentSize(ownerContext); if (containerSize.gotAll) { if (me.manageOverflow && !ownerContext.state.secondPass && !me.reserveScrollbar) { me.calculateOverflow(ownerContext, containerSize); } return; } } me.done = false; }, calculateContentSize: function (ownerContext) { var me = this, containerDimensions = ((ownerContext.widthModel.shrinkWrap ? 1 : 0) | (ownerContext.heightModel.shrinkWrap ? 2 : 0)), calcWidth = (containerDimensions & 1) || undefined, calcHeight = (containerDimensions & 2) || undefined, needed = 0, props = ownerContext.props; if (calcWidth) { if (isNaN(props.contentWidth)) { ++needed; } else { calcWidth = undefined; } } if (calcHeight) { if (isNaN(props.contentHeight)) { ++needed; } else { calcHeight = undefined; } } if (needed) { if (calcWidth && !ownerContext.setContentWidth(me.measureContentWidth(ownerContext))) { me.done = false; } if (calcHeight && !ownerContext.setContentHeight(me.measureContentHeight(ownerContext))) { me.done = false; } //if (me.done) { // var el = ownerContext.targetContext.el.dom; // Ext.log(this.owner.id, '.contentSize: ', contentWidth, 'x', contentHeight, // ' => scrollSize: ', el.scrollWidth, 'x', el.scrollHeight); //} } }, /** * Handles overflow processing for a container. In addition to the ownerContext * passed to the {@link #calculate} method, this method also needs the containerSize * (the object returned by {@link #getContainerSize}). * @protected * * @param {Ext.layout.ContextItem} ownerContext */ calculateOverflow: function (ownerContext) { var me = this, width, height, scrollbarSize, scrollbars, xauto, yauto, targetEl; // Determine the dimensions that have overflow:auto applied. If these come by // way of component config, this does not require a DOM read: xauto = (me.getOverflowXStyle(ownerContext) === 'auto'); yauto = (me.getOverflowYStyle(ownerContext) === 'auto'); if (xauto || yauto) { scrollbarSize = Ext.getScrollbarSize(); targetEl = ownerContext.overflowContext.el.dom; scrollbars = 0; if (targetEl.scrollWidth > targetEl.clientWidth) { // has horizontal scrollbar scrollbars |= 1; } if (targetEl.scrollHeight > targetEl.clientHeight) { // has vertical scrollbar scrollbars |= 2; } width = (yauto && (scrollbars & 2)) ? scrollbarSize.width : 0; height = (xauto && (scrollbars & 1)) ? scrollbarSize.height : 0; if (width !== me.lastOverflowAdjust.width || height !== me.lastOverflowAdjust.height) { me.done = false; // we pass overflowAdjust and overflowState in as state for the next // cycle (these are discarded if one of our ownerCt's invalidates): ownerContext.invalidate({ state: { overflowAdjust: { width: width, height: height }, overflowState: scrollbars, secondPass: true } }); } } }, completeLayout: function(ownerContext) { this.lastOverflowAdjust = ownerContext.state.overflowAdjust; }, doRenderPadding: function(out, renderData) { // Careful! This method is bolted on to the renderTpl so all we get for context is // the renderData! The "this" pointer is the renderTpl instance! var me = renderData.$layout, owner = renderData.$layout.owner, padding = owner[owner.contentPaddingProperty]; if (me.managePadding && padding) { out.push('padding:', owner.unitizeBox(padding)); } }, finishedLayout: function (ownerContext) { var innerCt = this.innerCt; this.callParent(arguments); if (Ext.isIEQuirks || Ext.isIE8m) { // IE6/7/quirks need a repaint to fix various rendering issues. TODO: narrow // down the specific issues that require a repaint. // IE8 strict needs a repaint to render percentage sized child items. innerCt.repaint(); } if (Ext.isOpera) { // Opera also needs a repaint to render percentage sized child items. but // the normal repaint() method doesn't seem to do the trick, but tweaking // the position property in combination with reading scrollWidth does. innerCt.setStyle('position', 'relative'); innerCt.dom.scrollWidth; innerCt.setStyle('position', ''); } }, /** * Returns the container size (that of the target). Only the fixed-sized dimensions can * be returned because the shrinkWrap dimensions are based on the contentWidth/Height * as determined by the container layout. * * If the {@link #calculateOverflow} method is used and if {@link #manageOverflow} is * true, this will adjust the width/height by the size of scrollbars. * * @param {Ext.layout.ContextItem} ownerContext The owner's context item. * @param {Boolean} [inDom=false] True if the container size must be in the DOM. * @return {Object} The size * @return {Number} return.width The width * @return {Number} return.height The height * @protected */ getContainerSize : function(ownerContext, inDom) { // Subtle But Important: // // We don't want to call getProp/hasProp et.al. unless we in fact need that value // for our results! If we call it and don't need it, the layout manager will think // we depend on it and will schedule us again should it change. var size = this.callParent(arguments), overflowAdjust = ownerContext.state.overflowAdjust; if (overflowAdjust) { size.width -= overflowAdjust.width; size.height -= overflowAdjust.height; } return size; }, getRenderData: function() { var owner = this.owner, data = this.callParent(); // The shrinkWrapWidth data property is used by the renderTpl to determine if // a table-based outerCt/innerCt is required in old IE. There are currently 2 // container configs that turn on the table at render time: // // 1. shrinkWrap:[1/3/true] on the container's initial config. There // are two reasons one would want to use this config: // a) If the container's width is initially shrink wrapped. // b) If the container is not initially shrink wrapped and it is known // at creation time that it will be shrink wrapped at some point // after the initial layout cycle, then it is recommended to // use the shrinkWrap config to initially configure the container // with shrink wrapped width so that the layout does not have to // dynamically insert a table after initial render. // // 2. If the container's "floating" config is set to true, and the container // does not have a configured width, we can be relatively certain at render // time that the container will be shrink wrapped at layout time, so // we go ahead and initially render using a table. if ((Ext.isIEQuirks || Ext.isIE7m) && // old IE ((owner.shrinkWrap & 1) || // shrink wrap width (handles value of 1, 3, or true) (owner.floating && !owner.width))) { // floating container with no configured width data.shrinkWrapWidth = true; } return data; }, // Overridden method from Ext.layout.container.Container. // Used in the beforeLayout method to render all items into. getRenderTarget: function() { return this.innerCt; }, // 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; }, /** * Returns the overflow-x style of the render target. * Note: If overflow is configured on a container using style or css class this method * will read the dom the first time it is called. It is therefore preferable for * performance reasons to use the autoScroll or overflowX config when horizontal * overflow is desired. * @protected * @param {Ext.layout.ContextItem} ownerContext * @return {String} */ getOverflowXStyle: function(ownerContext) { return ownerContext.overflowXStyle || (ownerContext.overflowXStyle = this.owner.scrollFlags.overflowX || ownerContext.overflowContext.getStyle('overflow-x')); }, /** * Returns the overflow-y style of the render target. * Note: If overflow is configured on a container using style or css class this method * will read the dom the first time it is called. It is therefore preferable for * performance reasons to use the autoScroll or overflowY config when vertical * overflow is desired. * @protected * @param {Ext.layout.ContextItem} ownerContext * @return {String} */ getOverflowYStyle: function(ownerContext) { return ownerContext.overflowYStyle || (ownerContext.overflowYStyle = this.owner.scrollFlags.overflowY || ownerContext.overflowContext.getStyle('overflow-y')); }, initContextItems: function(ownerContext) { var me = this, target = ownerContext.target, customOverflowEl = me.owner.customOverflowEl; ownerContext.outerCtContext = ownerContext.getEl('outerCt', me); ownerContext.innerCtContext = ownerContext.getEl('innerCt', me); if (customOverflowEl) { ownerContext.overflowContext = ownerContext.getEl(customOverflowEl); } else { ownerContext.overflowContext = ownerContext.targetContext; } if (target[target.contentPaddingProperty] !== undefined) { // If padding was defined using the contentPaddingProperty, we render the // the padding to the innerCt or outerCt (depending on the template that is // being used), so we need to set the paddingContext accordingly. // Otherwise we leave paddingContext as set by Container layout (defaults to // the targetContext) ownerContext.paddingContext = me.isShrinkWrapTpl ? ownerContext.innerCtContext : ownerContext.outerCtContext; } }, initLayout: function() { var me = this, scrollbarWidth = Ext.getScrollbarSize().width, owner = me.owner; me.callParent(); // Create a default lastOverflowAdjust based upon scrolling configuration. // If the Container is to overflow, or we *always* reserve space for a scrollbar // then reserve space for a vertical scrollbar if (scrollbarWidth && me.manageOverflow && !me.hasOwnProperty('lastOverflowAdjust')) { if (owner.autoScroll || me.reserveScrollbar) { me.lastOverflowAdjust = { width: scrollbarWidth, height: 0 }; } } }, /** * In some cases a table-based outerCt/innerCt is required in old IE (see renderTpl). * Most of the time this is determined at render time, however its possible that * we made the wrong determination at render time and now that the layout is in * progress we need a table. If so, this method should be called to replace the * existing outerCt with a new table outerCt, and move the child elements to the new * innerCt. * @private */ insertTableCt: function(ownerContext) { var me = this, owner = me.owner, i = 0, renderTpl, fragment, childNodes, childLength, targetEl; // get the table-based renderTpl renderTpl = Ext.XTemplate.getTpl(this, 'tableTpl'); renderTpl.renderPadding = me.doRenderPadding // To avoid unnecessary reflows, remove the innerCt from the dom // before operating on its children. me.outerCt.dom.removeChild(me.innerCt.dom); // create a document fragment to move all the childNodes to, so that // they can be batch appended to the new innerCt fragment = document.createDocumentFragment(); childNodes = me.innerCt.dom.childNodes; childLength = childNodes.length; // append all the children to the document fragment for (; i < childLength; i++) { fragment.appendChild(childNodes[0]); } // process the table template and insert it into the target el targetEl = me.getTarget(); targetEl.dom.innerHTML = renderTpl.apply({ $layout: me, ownerId: me.owner.id }); // append the document fragment containing the childNodes to the new innerCt targetEl.down('td').dom.appendChild(fragment); // reconfigure childEls to point to the new template's elements // we need to do this after the childNodes are appended to the new innerCt // because the clearEl is one of the childNodes me.applyChildEls(owner.el, owner.id) // set the flag that indicates we are using a "shrink wrap" template. // this needs to be done before reinitializeing the context items so that // the paddingContext will be configured correctly. me.isShrinkWrapTpl = true; // since we have new childEls we need to reinitialize the context items ownerContext.removeEl(me.outerCt); ownerContext.removeEl(me.innerCt); me.initContextItems(ownerContext); }, measureContentHeight: function (ownerContext) { // contentHeight includes padding, but not border, framing or margins var contentHeight = this.outerCt.getHeight(), target = ownerContext.target; if (this.managePadding && (target[target.contentPaddingProperty] === undefined)) { // if padding was not configured using the appropriate contentPaddingProperty // then the padding will not be on the paddingContext, and therfore not included // in the outerCt measurement, so we need to read the padding from the // targetContext contentHeight += ownerContext.targetContext.getPaddingInfo().height; } return contentHeight; }, measureContentWidth: function (ownerContext) { var dom, style, old, contentWidth, target; // In the newer Chrome versions, it won't measure the // width correctly without repainting the inner // cell in some circumstances. if (this.chromeCellMeasureBug) { dom = this.innerCt.dom; style = dom.style; old = style.display; if (old == 'table-cell') { style.display = ''; dom.offsetWidth; style.display = old; } } // contentWidth includes padding, but not border, framing or margins contentWidth = this.outerCt.getWidth(); target = ownerContext.target; if (this.managePadding && (target[target.contentPaddingProperty] === undefined)) { // if padding was not configured using the appropriate contentPaddingProperty // then the padding will not be on the paddingContext, and therfore not included // in the outerCt measurement, so we need to read the padding from the // targetContext contentWidth += ownerContext.targetContext.getPaddingInfo().width; } return contentWidth; }, /** * This method sets the height and/or width of the outerCt/innerCt to adjust for the * following browser-specific issues: * * 1. In IE6 and 7 strict if we are using the shrink wrap template, and the outerCt * has a 100% width (because the container is not shrink wrapping width currently), * and the target element has a vertical scrollbar, the browser disregards the * scrollbar when sizing the width of the outerCt. This can result in the target * element gaining a horizontal scrollbar. We fix this issue by setting a pixel * width on the outerCt * * 2. In IE quirks when using the "non shrink wrap" template, a long non-breaking word * can cause the outerCt's width to expand beyond the width of its container. This * behavior is desired if the container has the potential for horizontal overflow, * but can cause text to be hidden if the container's overflow is hidden. To prevent * this from happening we give the outerCt a fixed width in IE quirks when the * container does not have horizontal overflow. * * 3. In some browsers a percentage-height element ignores the horizontal scrollbar * of its parent (see Ext.supports.PercentageHeightOverflowBug). If the browser is * affected by this bug the outerCt needs a pixel height in order to support * percentage-height children when not shrink-wrapping height. If the browser is not * affected by this bug, a height of 100% is assigned to the outerCt (see * beginLayoutCycle). * * 4. In IE6/7 strict when using the "shrink wrap" template, percentage heights on * children do not work unless the innerCt td has a height set. We can't use height * 100% on the innerCt because conent-box sizing will cause any top/bottom padding to * be added to the height. The solution is to set a pixel height on the innerCt. * 5. IE8 strict mode has a bug with percentage height children. if the innerCt has * a height of 100%, has padding, and has a child item with a percentage height, that * child item will be sized as a percentage of the parent's height plus padding height. * In other words, a child with height:50% would have its height caclulated thusly: * (parentHeight + parentPaddingHeight) * 0.5 * To fix this, we have to give the innerCt a pixel height. * * 6. In IE7 strict if we're using the "non shrink wrap" template, and the target * element has overflow-y:auto, the outerCt reserves space for the target element's * vertical scrollbar even when there is no vertical scrollbar. This is fixed by * setting the targetEl's overflow property to "hidden" and then back to "auto". * * @protected * @param {Ext.layout.ContextItem} ownerContext * @param {Object} containerSize */ setCtSizeIfNeeded: function(ownerContext, containerSize) { var me = this, width = containerSize.width, height = containerSize.height, padding = ownerContext.paddingContext.getPaddingInfo(), targetEl = me.getTarget(), overflowXStyle = me.getOverflowXStyle(ownerContext), overflowYStyle = me.getOverflowYStyle(ownerContext), canOverflowX = (overflowXStyle === 'auto' || overflowXStyle === 'scroll'), canOverflowY = (overflowYStyle === 'auto' || overflowYStyle === 'scroll'), scrollbarSize = Ext.getScrollbarSize(), isShrinkWrapTpl = me.isShrinkWrapTpl, manageOverflow = me.manageOverflow, overflowStyleName, needsOuterHeight, needsInnerHeight, needsInnerCtPaddingHeight; if (width && !ownerContext.widthModel.shrinkWrap && // shrink wrap outerCt needs pixel width in IE6/7 strict because 100% width // on the outerCt causes it to overlap the vertical scrollbar ((Ext.isIE7m && Ext.isStrict && isShrinkWrapTpl && canOverflowY) || // non shrink wrap tpl outerCt in IE quirks needs pixel width to prevent // non-breaking text from causing the outerCt to expand beyond the width // of its container. (Ext.isIEQuirks && !isShrinkWrapTpl && !canOverflowX))) { if (!manageOverflow) { // If we're not managing overflow, the containerSize will not account for // vertical scrollbar width, so we need to see if there is a vertical // scrollbar and subtract its width if (canOverflowY && (targetEl.dom.scrollHeight > targetEl.dom.clientHeight)) { // has vertical scrollbar width -= scrollbarSize.width; } } ownerContext.outerCtContext.setProp('width', width + padding.width); me.hasOuterCtPxWidth = true; } if (height && !ownerContext.heightModel.shrinkWrap) { if (Ext.supports.PercentageHeightOverflowBug) { // set a pixel height on the outerCt if the browser ignores horizontal // scrollbar when rendering percentage-height elements needsOuterHeight = true; } if (((Ext.isIE8 && Ext.isStrict) || Ext.isIE7m && Ext.isStrict && isShrinkWrapTpl)) { // When using a shrink wrap template and not shrink wrapping, we set a // pixel height on the innerCt to support percentage height children in // IE6/7/8 strict. needsInnerHeight = true; // Do not add padding to the innerCt height in IE8 to prevent percentage- // height children from adding padding height in their height calculation. needsInnerCtPaddingHeight = !Ext.isIE8; } if ((needsOuterHeight || needsInnerHeight) && canOverflowX && (targetEl.dom.scrollWidth > targetEl.dom.clientWidth)) { // adjust the height for scrollbar size since it's not accounted for // in the containerSize. // IE8 in what passes for "standards" mode does not tolerate -ve sizes height = Math.max(height - scrollbarSize.height, 0); } if (needsOuterHeight) { ownerContext.outerCtContext.setProp('height', height + padding.height); me.hasOuterCtPxHeight = true; } if (needsInnerHeight) { if (needsInnerCtPaddingHeight) { height += padding.height; } ownerContext.innerCtContext.setProp('height', height); me.hasInnerCtPxHeight = true; } } if (Ext.isIE7 && Ext.isStrict && !isShrinkWrapTpl && (overflowYStyle === 'auto')) { // IE7 strict has an insane bug where an auto-width element reserves // space for its parent's vertical scrollbar if the parent has // overflow-y:auto; even when no scrollbar is present. To workaround // this issue we can set overflow-y to 'hidden' and then back to 'auto'. // If we have vertical overflow, however, tweaking overflow-y can cause an // illegitimate horizontal scrollbar to appear. So we have to tweak overflow-x // instead if the overflow-x style is "auto" overflowStyleName = (overflowXStyle === 'auto') ? 'overflow-x' : 'overflow-y'; targetEl.setStyle(overflowStyleName, 'hidden'); targetEl.setStyle(overflowStyleName, 'auto'); } }, setupRenderTpl: function (renderTpl) { this.callParent(arguments); renderTpl.renderPadding = this.doRenderPadding; }, getContentTarget: function(){ return this.innerCt; } }, function(){ this.prototype.chromeCellMeasureBug = Ext.isChrome && Ext.chromeVersion >= 26; });