    /**
     * The class is the default component layout for {@link Ext.Component} when no explicit
     * `{@link Ext.Component#componentLayout componentLayout}` is configured.
     *
     * This class uses template methods to perform the individual aspects of measurement,
     * calculation and publication of results. The methods called depend on the component's
     * {@link Ext.AbstractComponent#getSizeModel size model}.
     *
     * ## configured / calculated
     *
     * In either of these size models, the dimension of the outer element is of a known size.
     * The size is found in the `ownerContext` (the {@link Ext.layout.ContextItem} for the owner
     * component) as either "width" or "height". This value, if available, is passed to the
     * `publishInnerWidth` or `publishInnerHeight` method, respectively.
     *
     * ## shrinkWrap
     *
     * When a dimension uses the `shrinkWrap` size model, that means the content is measured,
     * then the outer (owner) size is calculated and published.
     *
     * For example, for a shrinkWrap width, the following sequence of calls are made:
     *
     * - `Ext.layout.component.Component#measureContentWidth`
     * - `publishOwnerWidth`
     *    - `calculateOwnerWidthFromContentWidth`
     *    - `publishInnerWidth` (in the event of hitting a min/maxWidth constraint)
     *
     * ## natural
     *
     * When a dimension uses the `natural` size model, the measurement is made on the outer
     * (owner) element. This size is then used to determine the content area in much the same
     * way as if the outer element had a `configured` or `calculated` size model.
     *
     * - `Ext.layout.component.Component#measureOwnerWidth`
     * - `publishInnerWidth`
     *
     * @protected
     */
    Ext.define('Ext.layout.component.Auto', {

        /* Begin Definitions */

        alias: 'layout.autocomponent',

        extend: 'Ext.layout.component.Component',

        /* End Definitions */

        type: 'autocomponent',

        /**
         * @cfg {Boolean} [setHeightInDom=false]
         * @protected
         * When publishing height of an auto Component, it is usually not written to the DOM.
         * Setting this to `true` overrides this behaviour.
         */
        setHeightInDom: false,

        /**
         * @cfg {Boolean} [setWidthInDom=false]
         * @protected
         * When publishing width of an auto Component, it is usually not written to the DOM.
         * Setting this to `true` overrides this behaviour.
         */
        setWidthInDom: false,

        waitForOuterHeightInDom: false,
        waitForOuterWidthInDom: false,

        beginLayoutCycle: function(ownerContext, firstCycle){
            var me = this,
                lastWidthModel = me.lastWidthModel,
                lastHeightModel = me.lastHeightModel,
                el = me.owner.el;

            me.callParent(arguments);

            if (lastWidthModel && lastWidthModel.fixed && ownerContext.widthModel.shrinkWrap) {
                el.setWidth(null);
            }

            if (lastHeightModel && lastHeightModel.fixed && ownerContext.heightModel.shrinkWrap) {
                el.setHeight(null);
            }
        },

        calculate: function(ownerContext) {
            var me = this,
                measurement = me.measureAutoDimensions(ownerContext),
                heightModel = ownerContext.heightModel,
                widthModel = ownerContext.widthModel,
                width, height;

            // It is generally important to process widths before heights, since widths can
            // often effect heights...
            if (measurement.gotWidth) {
                if (widthModel.shrinkWrap) {
                    me.publishOwnerWidth(ownerContext, measurement.contentWidth);
                } else if (me.publishInnerWidth) {
                    me.publishInnerWidth(ownerContext, measurement.width);
                }
            } else if (!widthModel.auto && me.publishInnerWidth) {
                width = me.waitForOuterWidthInDom ? ownerContext.getDomProp('width')
                            : ownerContext.getProp('width');
                if (width === undefined) {
                    me.done = false;
                } else {
                    me.publishInnerWidth(ownerContext, width);
                }
            }

            if (measurement.gotHeight) {
                if (heightModel.shrinkWrap) {
                    me.publishOwnerHeight(ownerContext, measurement.contentHeight);
                } else if (me.publishInnerHeight) {
                    me.publishInnerHeight(ownerContext, measurement.height);
                }
            } else if (!heightModel.auto && me.publishInnerHeight) {
                height = me.waitForOuterHeightInDom ? ownerContext.getDomProp('height')
                            : ownerContext.getProp('height');
                if (height === undefined) {
                    me.done = false;
                } else {
                   me.publishInnerHeight(ownerContext, height);
                }
            }

            if (!measurement.gotAll) {
                me.done = false;
            }
        },

        calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
            return contentHeight + ownerContext.getFrameInfo().height;
        },

        calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) {
            return contentWidth + ownerContext.getFrameInfo().width;
        },

        publishOwnerHeight: function (ownerContext, contentHeight) {
            var me = this,
                owner = me.owner,
                height = me.calculateOwnerHeightFromContentHeight(ownerContext, contentHeight),
                constrainedHeight, dirty, heightModel;

            if (isNaN(height)) {
                me.done = false;
            } else {
                constrainedHeight = Ext.Number.constrain(height, owner.minHeight, owner.maxHeight);

                if (constrainedHeight == height) {
                    dirty = me.setHeightInDom;
                } else {
                    heightModel = me.sizeModels[
                        (constrainedHeight < height) ? 'constrainedMax' : 'constrainedMin'];
                    height = constrainedHeight;

                    if (ownerContext.heightModel.calculatedFromShrinkWrap) {
                        // Don't bother to invalidate since that will come soon... but we need
                        // to signal our ownerLayout that we need an invalidate to actually
                        // make good on the determined (constrained) size!
                        ownerContext.heightModel = heightModel;
                    } else {
                        ownerContext.invalidate({ heightModel: heightModel });
                    }
                }

                ownerContext.setHeight(height, dirty);
            }
        },

        publishOwnerWidth: function (ownerContext, contentWidth) {
            var me = this,
                owner = me.owner,
                width = me.calculateOwnerWidthFromContentWidth(ownerContext, contentWidth),
                constrainedWidth, dirty, widthModel;

            if (isNaN(width)) {
                me.done = false;
            } else {
                constrainedWidth = Ext.Number.constrain(width, owner.minWidth, owner.maxWidth);

                if (constrainedWidth == width) {
                    dirty = me.setWidthInDom;
                } else {
                    widthModel = me.sizeModels[
                        (constrainedWidth < width) ? 'constrainedMax' : 'constrainedMin'];
                    width = constrainedWidth;

                    if (ownerContext.widthModel.calculatedFromShrinkWrap) {
                        // Don't bother to invalidate since that will come soon... but we need
                        // to signal our ownerLayout that we need an invalidate to actually
                        // make good on the determined (constrained) size!
                        ownerContext.widthModel = widthModel;
                    } else {
                        ownerContext.invalidate({ widthModel: widthModel });
                    }
                }

                ownerContext.setWidth(width, dirty);
            }
        }
    });