    /**
     * Component layout for buttons
     * @private
     */
    Ext.define('Ext.layout.component.Button', {

        /* Begin Definitions */

        alias: ['layout.button'],

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

        /* End Definitions */

        type: 'button',

        cellClsRE: /-btn-(tl|br)\b/,
        htmlRE: /<.*>/,

        constructor: function () {
            this.callParent(arguments);

            this.hackWidth = Ext.isIE && (!Ext.isStrict || Ext.isIE6 || Ext.isIE7);
            this.heightIncludesPadding = Ext.isIE6 && Ext.isStrict;
        },

        // TODO - use last run results if text has not changed?

        beginLayout: function (ownerContext) {
            this.callParent(arguments);

            this.cacheTargetInfo(ownerContext);
        },

        beginLayoutCycle: function(ownerContext) {
            var me = this,
                empty = '',
                owner = me.owner,
                btnEl = owner.btnEl,
                btnInnerEl = owner.btnInnerEl,
                text = owner.text,
                htmlAutoHeight;

            me.callParent(arguments);

            btnInnerEl.setStyle('overflow', empty);

            // Clear all element widths
            if (!ownerContext.widthModel.natural) {
                owner.el.setStyle('width', empty);
            }

            // If the text is HTML we need to let the browser automatically size things to cope with the case where the text
            // is multi-line. This incurs a cost as we then have to measure those elements to derive other sizes
            htmlAutoHeight = ownerContext.heightModel.shrinkWrap && text && me.htmlRE.test(text);

            btnEl.setStyle('width', empty);
            btnEl.setStyle('height', htmlAutoHeight ? 'auto' : empty);
            btnInnerEl.setStyle('width', empty);
            btnInnerEl.setStyle('height', htmlAutoHeight ? 'auto' : empty);
            btnInnerEl.setStyle('line-height', htmlAutoHeight ? 'normal' : empty);
            btnInnerEl.setStyle('padding-top', empty);
            owner.btnIconEl.setStyle('width', empty);
        },

        calculateOwnerHeightFromContentHeight: function (ownerContext, contentHeight) {
            return contentHeight;
        },

        calculateOwnerWidthFromContentWidth: function (ownerContext, contentWidth) {
            return contentWidth;
        },

        measureContentWidth: function (ownerContext) {
            var me = this,
                owner = me.owner,
                btnEl = owner.btnEl,
                btnInnerEl = owner.btnInnerEl,
                text = owner.text,
                btnFrameWidth, metrics, sizeIconEl, width, btnElContext, btnInnerElContext;

            // IE suffers from various sizing problems, usually caused by relying on it to size elements automatically. Even
            // if an element is sized correctly it can prove necessary to set that size explicitly on the element to get it
            // to size and position its children correctly. While the exact nature of the problems varies depending on the
            // browser version, doctype and button configuration there is a common solution: set the sizes manually.
            if (owner.text && me.hackWidth && btnEl) {
                btnFrameWidth = me.btnFrameWidth;

                // If the button text is something like '<' or '<<' then we need to escape it or it won't be measured
                // correctly. The button text is supposed to be HTML and strictly speaking '<' and '<<' aren't valid HTML.
                // However in practice they are commonly used and have worked 'correctly' in previous versions.
                if (text.indexOf('>') === -1) {
                    text = text.replace(/</g, '&lt;');
                }

                metrics = Ext.util.TextMetrics.measure(btnInnerEl, text);

                width = metrics.width + btnFrameWidth + me.adjWidth;

                btnElContext = ownerContext.getEl('btnEl');
                btnInnerElContext = ownerContext.getEl('btnInnerEl');
                sizeIconEl = (owner.icon || owner.iconCls) &&
                        (owner.iconAlign == "top" || owner.iconAlign == "bottom");

                // This cheat works (barely) with publishOwnerWidth which calls setProp also
                // to publish the width. Since it is the same value we set here, the dirty bit
                // we set true will not be cleared by publishOwnerWidth.
                ownerContext.setWidth(width); // not setWidth (no framing)

                btnElContext.setWidth(metrics.width + btnFrameWidth);
                btnInnerElContext.setWidth(metrics.width + btnFrameWidth);

                if (sizeIconEl) {
                    owner.btnIconEl.setWidth(metrics.width + btnFrameWidth);
                }
            } else {
                width = ownerContext.el.getWidth();
            }

            return width;
        },

        measureContentHeight: function (ownerContext) {
            var me = this,
                owner = me.owner,
                btnInnerEl = owner.btnInnerEl,
                btnItem = ownerContext.getEl('btnEl'),
                btnInnerItem = ownerContext.getEl('btnInnerEl'),
                minTextHeight = me.minTextHeight,
                adjHeight = me.adjHeight,
                text = owner.getText(),
                height,
                textHeight,
                topPadding;

            if (owner.vertical) {
                height = Ext.util.TextMetrics.measure(btnInnerEl, owner.text).width;
                height += me.btnFrameHeight + adjHeight;

                // Vertical buttons need height explicitly set
                ownerContext.setHeight(height, /*dirty=*/true, /*force=*/true);
            }
            else {
                // If the button text is HTML we have to handle it specially as it could contain multiple lines
                if (text && me.htmlRE.test(text)) {
                    textHeight = btnInnerEl.getHeight();

                    // HTML content doesn't guarantee multiple lines: in the single line case it could now be too short for the icon
                    if (textHeight < minTextHeight) {
                        topPadding = Math.floor((minTextHeight - textHeight) / 2);

                        // Resize the span and use padding to center the text vertically. The hack to remove the padding
                        // from the height on IE6 is especially needed for link buttons
                        btnInnerItem.setHeight(minTextHeight - (me.heightIncludesPadding ? topPadding : 0));
                        btnInnerItem.setProp('padding-top', topPadding);

                        textHeight = minTextHeight;
                    }

                    // Calculate the height relative to the text span, auto can't be trusted in IE quirks
                    height = textHeight + adjHeight;
                }
                else {
                    height = ownerContext.el.getHeight();
                }
            }

            // IE quirks needs the button height setting using style or it won't position the icon correctly (even if the height was already correct)
            btnItem.setHeight(height - adjHeight);

            return height;
        },

        publishInnerHeight: function(ownerContext, height) {
            var me = this,
                owner = me.owner,
                isNum = Ext.isNumber,
                btnItem = ownerContext.getEl('btnEl'),
                btnInnerEl = owner.btnInnerEl,
                btnInnerItem = ownerContext.getEl('btnInnerEl'),
                btnHeight = isNum(height) ? height - me.adjHeight : height,
                btnFrameHeight = me.btnFrameHeight,
                text = owner.getText(),
                textHeight,
                paddingTop;

            btnItem.setHeight(btnHeight);
            btnInnerItem.setHeight(btnHeight);

            // Only need the line-height setting for regular, horizontal Buttons
            if (!owner.vertical && btnHeight >= 0) {
                btnInnerItem.setProp('line-height', btnHeight - btnFrameHeight + 'px');
            }

            // Button text may contain markup that would force it to wrap to more than one line (e.g. 'Button<br>Label').
            // When this happens, we cannot use the line-height set above for vertical centering; we instead reset the
            // line-height to normal, measure the rendered text height, and add padding-top to center the text block
            // vertically within the button's height. This is more expensive than the basic line-height approach so
            // we only do it if the text contains markup.
            if (text && me.htmlRE.test(text)) {
                btnInnerItem.setProp('line-height', 'normal');
                btnInnerEl.setStyle('line-height', 'normal');
                textHeight = Ext.util.TextMetrics.measure(btnInnerEl, text).height;
                paddingTop = Math.floor(Math.max(btnHeight - btnFrameHeight - textHeight, 0) / 2);
                btnInnerItem.setProp('padding-top', me.btnFrameTop + paddingTop);
                btnInnerItem.setHeight(btnHeight - (me.heightIncludesPadding ? paddingTop : 0));
            }
        },

        publishInnerWidth: function(ownerContext, width) {
            var me = this,
                isNum = Ext.isNumber,
                btnItem = ownerContext.getEl('btnEl'),
                btnInnerItem = ownerContext.getEl('btnInnerEl'),
                btnWidth = isNum(width) ? width - me.adjWidth : width;

            btnItem.setWidth(btnWidth);
            btnInnerItem.setWidth(btnWidth);
        },

        clearTargetCache: function(){
            delete this.adjWidth;
        },

        cacheTargetInfo: function(ownerContext) {
            var me = this,
                owner = me.owner,
                scale = owner.scale,
                padding, frameSize, btnWrapPadding, btnInnerEl, innerFrameSize;

            // The cache is only valid for a particular scale
            if (!('adjWidth' in me) || me.lastScale !== scale) {
                // If there has been a previous layout run it could have sullied the line-height
                if (me.lastScale) {
                    owner.btnInnerEl.setStyle('line-height', '');
                }

                me.lastScale = scale;

                padding = ownerContext.getPaddingInfo();
                frameSize = ownerContext.getFrameInfo();
                btnWrapPadding = ownerContext.getEl('btnWrap').getPaddingInfo();
                btnInnerEl = ownerContext.getEl('btnInnerEl');
                innerFrameSize = btnInnerEl.getPaddingInfo();

                Ext.apply(me, {
                    // Width adjustment must take into account the arrow area. The btnWrap is the <em> which has padding to accommodate the arrow.
                    adjWidth       : btnWrapPadding.width + frameSize.width + padding.width,
                    adjHeight      : btnWrapPadding.height + frameSize.height + padding.height,
                    btnFrameWidth  : innerFrameSize.width,
                    btnFrameHeight : innerFrameSize.height,
                    btnFrameTop    : innerFrameSize.top,

                    // Use the line-height rather than height because if the text is multi-line then the height will be 'wrong'
                    minTextHeight  : parseInt(btnInnerEl.getStyle('line-height'), 10)
                });
            }

            me.callParent(arguments);
        }
    });