<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js"><span id='Ext-layout-container-Form'>/**
|
</span> * This is a layout that will render form Fields, one under the other all stretched to the Container width.
|
*
|
* @example
|
* Ext.create('Ext.Panel', {
|
* width: 500,
|
* height: 300,
|
* title: "FormLayout Panel",
|
* layout: 'form',
|
* renderTo: Ext.getBody(),
|
* bodyPadding: 5,
|
* defaultType: 'textfield',
|
* items: [{
|
* fieldLabel: 'First Name',
|
* name: 'first',
|
* allowBlank:false
|
* },{
|
* fieldLabel: 'Last Name',
|
* name: 'last'
|
* },{
|
* fieldLabel: 'Company',
|
* name: 'company'
|
* }, {
|
* fieldLabel: 'Email',
|
* name: 'email',
|
* vtype:'email'
|
* }, {
|
* fieldLabel: 'DOB',
|
* name: 'dob',
|
* xtype: 'datefield'
|
* }, {
|
* fieldLabel: 'Age',
|
* name: 'age',
|
* xtype: 'numberfield',
|
* minValue: 0,
|
* maxValue: 100
|
* }, {
|
* xtype: 'timefield',
|
* fieldLabel: 'Time',
|
* name: 'time',
|
* minValue: '8:00am',
|
* maxValue: '6:00pm'
|
* }]
|
* });
|
*
|
* Note that any configured {@link Ext.Component#padding padding} will be ignored on items within a Form layout.
|
*/
|
Ext.define('Ext.layout.container.Form', {
|
|
/* Begin Definitions */
|
|
alias: 'layout.form',
|
extend: 'Ext.layout.container.Container',
|
alternateClassName: 'Ext.layout.FormLayout',
|
|
<span id='Ext-layout-container-Form-property-tableCls'> /* End Definitions */
|
</span>
|
tableCls: Ext.baseCSSPrefix + 'form-layout-table',
|
|
<span id='Ext-layout-container-Form-property-type'> type: 'form',
|
</span>
|
<span id='Ext-layout-container-Form-property-createsInnerCt'> createsInnerCt: true,
|
</span>
|
<span id='Ext-layout-container-Form-property-manageOverflow'> manageOverflow: true,
|
</span>
|
<span id='Ext-layout-container-Form-property-lastOverflowAdjust'> // Begin with no previous adjustments
|
</span> lastOverflowAdjust: {
|
width: 0,
|
height: 0
|
},
|
|
<span id='Ext-layout-container-Form-property-childEls'> childEls: ['formTable'],
|
</span>
|
<span id='Ext-layout-container-Form-property-padRow'> padRow: '<tr><td class="' + Ext.baseCSSPrefix + 'form-item-pad" colspan="3"></td></tr>',
|
</span>
|
<span id='Ext-layout-container-Form-property-renderTpl'> renderTpl: [
|
</span> '<table id="{ownerId}-formTable" class="{tableCls}" style="width:100%" cellpadding="0">',
|
'{%this.renderBody(out,values)%}',
|
'</table>',
|
'{%this.renderPadder(out,values)%}'
|
],
|
|
<span id='Ext-layout-container-Form-method-getRenderData'> getRenderData: function(){
|
</span> var data = this.callParent();
|
data.tableCls = this.tableCls;
|
return data;
|
},
|
|
<span id='Ext-layout-container-Form-method-calculate'> calculate : function (ownerContext) {
|
</span> var me = this,
|
containerSize = me.getContainerSize(ownerContext, true),
|
tableWidth,
|
childItems,
|
i = 0, length,
|
shrinkwrapHeight = ownerContext.sizeModel.height.shrinkWrap;
|
|
if (shrinkwrapHeight) {
|
if (ownerContext.hasDomProp('containerChildrenSizeDone')) {
|
ownerContext.setProp('contentHeight', me.formTable.dom.offsetHeight + ownerContext.targetContext.getPaddingInfo().height);
|
} else {
|
me.done = false;
|
}
|
}
|
|
// Once we have been widthed, we can impose that width (in a non-dirty setting) upon all children at once
|
if (containerSize.gotWidth) {
|
tableWidth = me.formTable.dom.offsetWidth;
|
childItems = ownerContext.childItems;
|
|
for (length = childItems.length; i < length; ++i) {
|
childItems[i].setWidth(tableWidth, false);
|
}
|
} else {
|
me.done = false;
|
}
|
},
|
|
<span id='Ext-layout-container-Form-method-getRenderTarget'> getRenderTarget: function() {
|
</span> return this.formTable;
|
},
|
|
<span id='Ext-layout-container-Form-method-getRenderTree'> getRenderTree: function() {
|
</span> var me = this,
|
result = me.callParent(arguments),
|
i, len;
|
|
for (i = 0, len = result.length; i < len; i++) {
|
result[i] = me.transformItemRenderTree(result[i]);
|
}
|
return result;
|
},
|
|
<span id='Ext-layout-container-Form-method-transformItemRenderTree'> transformItemRenderTree: function(item) {
|
</span>
|
if (item.tag && item.tag == 'table') {
|
item.tag = 'tbody';
|
delete item.cellspacing;
|
delete item.cellpadding;
|
|
// IE6 doesn't separate cells nicely to provide input field
|
// vertical separation. It also does not support transparent borders
|
// which is how the extra 1px is added to the 2px each side cell spacing.
|
// So it needs a 5px high pad row.
|
if (Ext.isIE6) {
|
item.cn = this.padRow;
|
}
|
|
return item;
|
}
|
|
return {
|
tag: 'tbody',
|
cn: {
|
tag: 'tr',
|
cn: {
|
tag: 'td',
|
colspan: 3,
|
style: 'width:100%',
|
cn: item
|
}
|
}
|
};
|
|
},
|
|
<span id='Ext-layout-container-Form-method-isValidParent'> isValidParent: function(item, target, position) {
|
</span> return true;
|
},
|
|
<span id='Ext-layout-container-Form-method-isItemShrinkWrap'> isItemShrinkWrap: function(item) {
|
</span> return ((item.shrinkWrap === true) ? 3 : item.shrinkWrap||0) & 2;
|
},
|
|
<span id='Ext-layout-container-Form-method-getItemSizePolicy'> getItemSizePolicy: function(item) {
|
</span> return {
|
setsWidth: 1,
|
setsHeight: 0
|
};
|
},
|
|
|
<span id='Ext-layout-container-Form-method-beginLayoutCycle'>// All of the below methods are old methods moved here from Container layout
|
</span>// TODO: remove these methods once Form layout extends Auto layout
|
|
beginLayoutCycle: function (ownerContext, firstCycle) {
|
var padEl = this.overflowPadderEl;
|
|
if (padEl) {
|
padEl.setStyle('display', 'none');
|
}
|
|
// Begin with the scrollbar adjustment that we used last time - this is more likely to be correct
|
// than beginning with no adjustment at all
|
if (!ownerContext.state.overflowAdjust) {
|
ownerContext.state.overflowAdjust = this.lastOverflowAdjust;
|
}
|
},
|
|
<span id='Ext-layout-container-Form-method-calculateOverflow'> /**
|
</span> * Handles overflow processing for a container. This should be called once the layout
|
* has determined contentWidth/Height. In addition to the ownerContext passed to the
|
* {@link #calculate} method, this method also needs the containerSize (the object
|
* returned by {@link #getContainerSize}).
|
*
|
* @param {Ext.layout.ContextItem} ownerContext
|
* @param {Object} containerSize
|
* @param {Number} dimensions A bit mask for the overflow managed dimensions. The 0-bit
|
* is for `width` and the 1-bit is for `height`. In other words, a value of 1 would be
|
* only `width`, 2 would be only `height` and 3 would be both.
|
*/
|
calculateOverflow: function (ownerContext, containerSize, dimensions) {
|
var me = this,
|
targetContext = ownerContext.targetContext,
|
manageOverflow = me.manageOverflow,
|
state = ownerContext.state,
|
overflowAdjust = state.overflowAdjust,
|
padWidth, padHeight, padElContext, padding, scrollRangeFlags,
|
scrollbarSize, contentW, contentH, ownerW, ownerH, scrollbars,
|
xauto, yauto;
|
|
if (manageOverflow && !state.secondPass && !me.reserveScrollbar) {
|
// 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 the container layout is not using width, we don't need to adjust for the
|
// vscroll (likewise for height). Perhaps we don't even need to run the layout
|
// again if the adjustments won't have any effect on the result!
|
if (!containerSize.gotWidth) {
|
xauto = false;
|
}
|
if (!containerSize.gotHeight) {
|
yauto = false;
|
}
|
|
if (xauto || yauto) {
|
scrollbarSize = Ext.getScrollbarSize();
|
|
// as a container we calculate contentWidth/Height, so we don't want
|
// to use getProp and make it look like we are triggered by them...
|
contentW = ownerContext.peek('contentWidth');
|
contentH = ownerContext.peek('contentHeight');
|
// The content size includes the target element's padding. Since
|
// the containerSize excludes the target element's padding, we need
|
// to subtract this padding from the content size before checking
|
// to see if scrollbars are needed.
|
padding = targetContext.getPaddingInfo();
|
contentW -= padding.width;
|
contentH -= padding.height;
|
ownerW = containerSize.width;
|
ownerH = containerSize.height;
|
|
scrollbars = me.getScrollbarsNeeded(ownerW, ownerH, contentW, contentH);
|
state.overflowState = scrollbars;
|
|
if (typeof dimensions == 'number') {
|
scrollbars &= ~dimensions; // ignore dimensions that have no effect
|
}
|
|
overflowAdjust = {
|
width: (xauto && (scrollbars & 2)) ? scrollbarSize.width : 0,
|
height: (yauto && (scrollbars & 1)) ? scrollbarSize.height : 0
|
};
|
|
// We can have 0-sized scrollbars (new Mac OS) and so don't invalidate
|
// the layout unless this will change something...
|
if (overflowAdjust.width !== me.lastOverflowAdjust.width || overflowAdjust.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: overflowAdjust,
|
overflowState: state.overflowState,
|
secondPass: true
|
}
|
});
|
}
|
}
|
}
|
|
if (!me.done) {
|
return;
|
}
|
|
padElContext = ownerContext.padElContext ||
|
(ownerContext.padElContext = ownerContext.getEl('overflowPadderEl', me));
|
|
// Even if overflow does not effect the layout, we still do need the padEl to be
|
// sized or hidden appropriately...
|
if (padElContext) {
|
scrollbars = state.overflowState; // the true overflow state
|
padWidth = ownerContext.peek('contentWidth');
|
// the padder element must have a height of at least 1px, or its width will be ignored by the container.
|
// The extra height is adjusted for by adding a -1px top margin to the padder element
|
padHeight = 1;
|
|
if (scrollbars) {
|
padding = targetContext.getPaddingInfo();
|
scrollRangeFlags = me.scrollRangeFlags;
|
|
if ((scrollbars & 2) && (scrollRangeFlags & 1)) { // if (vscroll and loses bottom)
|
padHeight += padding.bottom;
|
}
|
|
if ((scrollbars & 1) && (scrollRangeFlags & 4)) { // if (hscroll and loses right)
|
padWidth += padding.right;
|
}
|
padElContext.setProp('display', '');
|
padElContext.setSize(padWidth, padHeight);
|
} else {
|
padElContext.setProp('display', 'none');
|
}
|
}
|
},
|
|
<span id='Ext-layout-container-Form-method-completeLayout'> completeLayout: function (ownerContext) {
|
</span> // Cache the scrollbar adjustment
|
this.lastOverflowAdjust = ownerContext.state.overflowAdjust;
|
},
|
|
<span id='Ext-layout-container-Form-method-doRenderPadder'> /**
|
</span> * Creates an element that makes bottom/right body padding consistent across browsers.
|
* This element is sized based on the need for scrollbars in {@link #calculateOverflow}.
|
* If the {@link #manageOverflow} option is false, this element is not created.
|
*
|
* See {@link #getScrollRangeFlags} for more details.
|
*/
|
doRenderPadder: 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 = me.owner,
|
scrollRangeFlags = me.getScrollRangeFlags();
|
|
if (me.manageOverflow) {
|
if (scrollRangeFlags & 5) { // if (loses parent bottom and/or right padding)
|
out.push('<div id="',owner.id,'-overflowPadderEl" ',
|
// the padder element must have a height of at least 1px, or its width will be ignored by the container.
|
// The extra height is adjusted for by adding a -1px top margin to the padder element
|
// A side effect of this approach is that click events will not register on bottom 1px of the container's
|
// last child item, unless that item has a bottom margin. To fix this we make the padder element
|
// relatively positioned and give it a large negative z-index. -99999 seems sufficient.
|
'style="font-size: 1px; height: 1px; margin-top: -1px; position: relative; z-index: -99999');
|
|
out.push('"></div>');
|
|
me.scrollRangeFlags = scrollRangeFlags; // remember for calculateOverflow
|
}
|
}
|
},
|
|
<span id='Ext-layout-container-Form-method-getContainerSize'> /**
|
</span> * 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 may 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.
|
* @param {Boolean} [ignoreOverflow=true] if true scrollbar size will not be
|
* subtracted from container size.
|
* @return {Object} The size
|
* @return {Number} return.width The width
|
* @return {Number} return.height The height
|
* @protected
|
*/
|
getContainerSize : function(ownerContext, inDom, ignoreOverflow) {
|
// 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 targetContext = ownerContext.targetContext,
|
frameInfo = targetContext.getFrameInfo(),
|
// if we're managing padding the padding is on the innerCt instead of the targetEl
|
padding = targetContext.getPaddingInfo(),
|
got = 0,
|
needed = 0,
|
overflowAdjust = ignoreOverflow ? null : ownerContext.state.overflowAdjust,
|
gotWidth, gotHeight, width, height;
|
|
// In an shrinkWrap width/height case, we must not ask for any of these dimensions
|
// because they will be determined by contentWidth/Height which is calculated by
|
// this layout...
|
|
// Fit/Card layouts are able to set just the width of children, allowing child's
|
// resulting height to autosize the Container.
|
// See examples/tabs/tabs.html for an example of this.
|
|
if (!ownerContext.widthModel.shrinkWrap) {
|
++needed;
|
width = inDom ? targetContext.getDomProp('width') : targetContext.getProp('width');
|
gotWidth = (typeof width == 'number');
|
if (gotWidth) {
|
++got;
|
width -= frameInfo.width + padding.width;
|
if (overflowAdjust) {
|
width -= overflowAdjust.width;
|
}
|
}
|
}
|
|
if (!ownerContext.heightModel.shrinkWrap) {
|
++needed;
|
height = inDom ? targetContext.getDomProp('height') : targetContext.getProp('height');
|
gotHeight = (typeof height == 'number');
|
if (gotHeight) {
|
++got;
|
height -= frameInfo.height + padding.height;
|
if (overflowAdjust) {
|
height -= overflowAdjust.height;
|
}
|
}
|
}
|
|
return {
|
width: width,
|
height: height,
|
needed: needed,
|
got: got,
|
gotAll: got == needed,
|
gotWidth: gotWidth,
|
gotHeight: gotHeight
|
};
|
},
|
|
<span id='Ext-layout-container-Form-method-getOverflowXStyle'> /**
|
</span> * returns the overflow-x style of the render target
|
* @protected
|
* @param {Ext.layout.ContextItem} ownerContext
|
* @return {String}
|
*/
|
getOverflowXStyle: function(ownerContext) {
|
var me = this;
|
|
return me.overflowXStyle ||
|
(me.overflowXStyle = me.owner.scrollFlags.overflowX || ownerContext.targetContext.getStyle('overflow-x'));
|
},
|
|
<span id='Ext-layout-container-Form-method-getOverflowYStyle'> /**
|
</span> * returns the overflow-y style of the render target
|
* @protected
|
* @param {Ext.layout.ContextItem} ownerContext
|
* @return {String}
|
*/
|
getOverflowYStyle: function(ownerContext) {
|
var me = this;
|
|
return me.overflowYStyle ||
|
(me.overflowYStyle = me.owner.scrollFlags.overflowY || ownerContext.targetContext.getStyle('overflow-y'));
|
},
|
|
<span id='Ext-layout-container-Form-property-getScrollRangeFlags'> /**
|
</span> * Returns flags indicating cross-browser handling of scrollHeight/Width. In particular,
|
* IE has issues with padding-bottom in a scrolling element (it does not include that
|
* padding in the scrollHeight). Also, margin-bottom on a child in a scrolling element
|
* can be lost.
|
*
|
* All browsers seem to ignore margin-right on children and padding-right on the parent
|
* element (the one with the overflow)
|
*
|
* This method returns a number with the follow bit positions set based on things not
|
* accounted for in scrollHeight and scrollWidth:
|
*
|
* - 1: Scrolling element's padding-bottom is not included in scrollHeight.
|
* - 2: Last child's margin-bottom is not included in scrollHeight.
|
* - 4: Scrolling element's padding-right is not included in scrollWidth.
|
* - 8: Child's margin-right is not included in scrollWidth.
|
*
|
* To work around the margin-bottom issue, it is sufficient to create a 0px tall last
|
* child that will "hide" the margin. This can also be handled by wrapping the children
|
* in an element, again "hiding" the margin. Wrapping the elements is about the only
|
* way to preserve their right margins. This is the strategy used by Column layout.
|
*
|
* To work around the padding-bottom problem, since it is comes from a style on the
|
* parent element, about the only simple fix is to create a last child with height
|
* equal to padding-bottom. To preserve the right padding, the sizing element needs to
|
* have a width that includes the right padding.
|
*/
|
getScrollRangeFlags: (function () {
|
var flags = -1;
|
|
return function () {
|
if (flags < 0) {
|
var div = Ext.getBody().createChild({
|
//cls: 'x-border-box x-hide-offsets',
|
cls: Ext.baseCSSPrefix + 'border-box',
|
style: {
|
width: '100px', height: '100px', padding: '10px',
|
overflow: 'auto'
|
},
|
children: [{
|
style: {
|
border: '1px solid red',
|
width: '150px', height: '150px',
|
margin: '0 5px 5px 0' // TRBL
|
}
|
}]
|
}),
|
scrollHeight = div.dom.scrollHeight,
|
scrollWidth = div.dom.scrollWidth,
|
heightFlags = {
|
// right answer, nothing missing:
|
175: 0,
|
// missing parent padding-bottom:
|
165: 1,
|
// missing child margin-bottom:
|
170: 2,
|
// missing both
|
160: 3
|
},
|
widthFlags = {
|
// right answer, nothing missing:
|
175: 0,
|
// missing parent padding-right:
|
165: 4,
|
// missing child margin-right:
|
170: 8,
|
// missing both
|
160: 12
|
};
|
|
flags = (heightFlags[scrollHeight] || 0) | (widthFlags[scrollWidth] || 0);
|
//Ext.log('flags=',flags.toString(2));
|
div.remove();
|
}
|
|
return flags;
|
};
|
}()),
|
|
<span id='Ext-layout-container-Form-method-initLayout'> initLayout: function() {
|
</span> var me = this,
|
scrollbarWidth = Ext.getScrollbarSize().width;
|
|
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 (me.owner.scrollFlags.y || me.reserveScrollbar) {
|
me.lastOverflowAdjust = {
|
width: scrollbarWidth,
|
height: 0
|
};
|
}
|
}
|
},
|
|
<span id='Ext-layout-container-Form-method-setupRenderTpl'> setupRenderTpl: function (renderTpl) {
|
</span> this.callParent(arguments);
|
|
renderTpl.renderPadder = this.doRenderPadder;
|
}
|
});
|
</pre>
|
</body>
|
</html>
|