    /**
     * This is a layout that enables anchoring of contained elements relative to the container's dimensions.
     * If the container is resized, all anchored items are automatically rerendered according to their
     * `{@link #anchor}` rules.
     *
     * This class is intended to be extended or created via the {@link Ext.container.AbstractContainer#layout layout}: 'anchor'
     * config, and should generally not need to be created directly via the new keyword.
     *
     * AnchorLayout does not have any direct config options (other than inherited ones). By default,
     * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the
     * container using the AnchorLayout can supply an anchoring-specific config property of `anchorSize`.
     *
     * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating
     * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring
     * logic if necessary.
     *
     *     @example
     *     Ext.create('Ext.Panel', {
     *         width: 500,
     *         height: 400,
     *         title: "AnchorLayout Panel",
     *         layout: 'anchor',
     *         renderTo: Ext.getBody(),
     *         items: [
     *             {
     *                 xtype: 'panel',
     *                 title: '75% Width and 20% Height',
     *                 anchor: '75% 20%'
     *             },
     *             {
     *                 xtype: 'panel',
     *                 title: 'Offset -300 Width & -200 Height',
     *                 anchor: '-300 -200'
     *             },
     *             {
     *                 xtype: 'panel',
     *                 title: 'Mixed Offset and Percent',
     *                 anchor: '-250 20%'
     *             }
     *         ]
     *     });
     */
    Ext.define('Ext.layout.container.Anchor', {

        /* Begin Definitions */

        alias: 'layout.anchor',
        extend: 'Ext.layout.container.Container',
        alternateClassName: 'Ext.layout.AnchorLayout',

        /* End Definitions */

        type: 'anchor',

        manageOverflow: 2,

        renderTpl: [
            '{%this.renderBody(out,values);this.renderPadder(out,values)%}'
        ],

        /**
         * @cfg {String} anchor
         *
         * This configuation option is to be applied to **child `items`** of a container managed by
         * this layout (ie. configured with `layout:'anchor'`).
         *
         * This value is what tells the layout how an item should be anchored to the container. `items`
         * added to an AnchorLayout accept an anchoring-specific config property of **anchor** which is a string
         * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%').
         * The following types of anchor values are supported:
         *
         * - **Percentage** : Any value between 1 and 100, expressed as a percentage.
         *
         *   The first anchor is the percentage width that the item should take up within the container, and the
         *   second is the percentage height.  For example:
         *
         *       // two values specified
         *       anchor: '100% 50%' // render item complete width of the container and
         *                          // 1/2 height of the container
         *       // one value specified
         *       anchor: '100%'     // the width value; the height will default to auto
         *
         * - **Offsets** : Any positive or negative integer value.
         *
         *   This is a raw adjustment where the first anchor is the offset from the right edge of the container,
         *   and the second is the offset from the bottom edge. For example:
         *
         *       // two values specified
         *       anchor: '-50 -100' // render item the complete width of the container
         *                          // minus 50 pixels and
         *                          // the complete height minus 100 pixels.
         *       // one value specified
         *       anchor: '-50'      // anchor value is assumed to be the right offset value
         *                          // bottom offset will default to 0
         *
         * - **Sides** : Valid values are `right` (or `r`) and `bottom` (or `b`).
         *
         *   Either the container must have a fixed size or an anchorSize config value defined at render time in
         *   order for these to have any effect.
         *
         * - **Mixed** :
         *
         *   Anchor values can also be mixed as needed.  For example, to render the width offset from the container
         *   right edge by 50 pixels and 75% of the container's height use:
         *
         *       anchor:   '-50 75%'
         */

        /**
         * @cfg {String} defaultAnchor
         * Default anchor for all child **container** items applied if no anchor or specific width is set on the child item.
         */
        defaultAnchor: '100%',

        parseAnchorRE: /^(r|right|b|bottom)$/i,

        beginLayout: function (ownerContext) {
            var me = this,
                dimensions = 0,
                anchorSpec, childContext, childItems, i, length, target;

            me.callParent(arguments);

            childItems = ownerContext.childItems; // populated by callParent
            length = childItems.length;

            for (i = 0; i < length; ++i) {
                childContext = childItems[i];
                anchorSpec = childContext.target.anchorSpec;

                if (anchorSpec) {
                    if (childContext.widthModel.calculated && anchorSpec.right) {
                        dimensions |= 1;
                    }
                    if (childContext.heightModel.calculated && anchorSpec.bottom) {
                        dimensions |= 2;
                    }

                    if (dimensions == 3) { // if (both dimensions in play)
                        break;
                    }
                }
            }

            ownerContext.anchorDimensions = dimensions;

            // Work around WebKit RightMargin bug. We're going to inline-block all the children
            // only ONCE and remove it when we're done
            if (!Ext.supports.RightMargin && !me.rightMarginCleanerFn) {
                target = ownerContext.targetContext.el; // targetContext is added by superclass

                me.rightMarginCleanerFn = Ext.Element.getRightMarginFixCleaner(target);
                target.addCls(Ext.baseCSSPrefix + 'inline-children');
            }

            //<debug>
            me.sanityCheck(ownerContext);
            //</debug>
        },

        calculate: function (ownerContext) {
            var me = this,
                containerSize = me.getContainerSize(ownerContext);

            if (ownerContext.anchorDimensions !== ownerContext.state.calculatedAnchors) {
                me.calculateAnchors(ownerContext, containerSize);
            }

            if (ownerContext.hasDomProp('containerChildrenDone')) {
                // Once the child layouts are done we can determine the content sizes...

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

                me.calculateContentSize(ownerContext, ownerContext.anchorDimensions);

                if (me.done) {
                    me.calculateOverflow(ownerContext, containerSize, ownerContext.anchorDimensions);
                    return;
                }
            }

            me.done = false;
        },

        calculateAnchors: function (ownerContext, containerSize) {
            var me = this,
                childItems = ownerContext.childItems,
                length = childItems.length,
                gotHeight = containerSize.gotHeight,
                gotWidth = containerSize.gotWidth,
                ownerHeight = containerSize.height,
                ownerWidth = containerSize.width,
                state = ownerContext.state,
                calculatedAnchors = (gotWidth ? 1 : 0) | (gotHeight ? 2 : 0),
                anchorSpec, childContext, childMargins, height, i, width;

            state.calculatedAnchors = (state.calculatedAnchors || 0) | calculatedAnchors;

            for (i = 0; i < length; i++) {
                childContext = childItems[i];
                childMargins = childContext.getMarginInfo();
                anchorSpec = childContext.target.anchorSpec;

                // Check widthModel in case "defaults" has applied an anchor to a component
                // that also has width (which must win). If we did not make this check in this
                // way, we would attempt to calculate a width where it had been configured.
                //
                if (gotWidth && childContext.widthModel.calculated) {
                    width = anchorSpec.right(ownerWidth) - childMargins.width;
                    width = me.adjustWidthAnchor(width, childContext);

                    childContext.setWidth(width);
                }

                // Repeat for height
                if (gotHeight && childContext.heightModel.calculated) {
                    height = anchorSpec.bottom(ownerHeight) - childMargins.height;
                    height = me.adjustHeightAnchor(height, childContext);

                    childContext.setHeight(height);
                }
            }
        },

        finishedLayout: function (ownerContext) {
            var cleanerFn = this.rightMarginCleanerFn;

            if (cleanerFn) {
                delete this.rightMarginCleanerFn;
                ownerContext.targetContext.el.removeCls(Ext.baseCSSPrefix + 'inline-children');
                cleanerFn();
            }
        },

        //<debug>
        sanityCheck: function (ownerContext) {
            var shrinkWrapWidth = ownerContext.widthModel.shrinkWrap,
                shrinkWrapHeight = ownerContext.heightModel.shrinkWrap,
                children = ownerContext.childItems,
                anchorSpec, comp, childContext,
                i, length;

            for (i = 0, length = children.length; i < length; ++i) {
                childContext = children[i];
                comp = childContext.target;
                anchorSpec = comp.anchorSpec;

                if (anchorSpec) {
                    if (childContext.widthModel.calculated && anchorSpec.right) {
                        if (shrinkWrapWidth) {
                            Ext.log({
                                level: 'warn',
                                msg: 'Right anchor on '+comp.id+' in shrinkWrap width container'
                            });
                        }
                    }

                    if (childContext.heightModel.calculated && anchorSpec.bottom) {
                        if (shrinkWrapHeight) {
                            Ext.log({
                                level: 'warn',
                                msg: 'Bottom anchor on '+comp.id+' in shrinkWrap height container'
                            });
                        }
                    }
                }
            }
        },
        //</debug>

        // private
        anchorFactory: {
            offset: function (delta) {
                return function(v) {
                    return v + delta;
                };
            },
            ratio: function (ratio) {
                return function(v) {
                    return Math.floor(v * ratio);
                };
            },
            standard: function (diff) {
                return function(v) {
                    return v - diff;
                };
            }
        },

        parseAnchor: function(a, start, cstart) {
            if (a && a != 'none') {
                var factory = this.anchorFactory,
                    delta;

                if (this.parseAnchorRE.test(a)) {
                    return factory.standard(cstart - start);
                }
                if (a.indexOf('%') != -1) {
                    return factory.ratio(parseFloat(a.replace('%', '')) * 0.01);
                }
                delta = parseInt(a, 10);
                if (!isNaN(delta)) {
                    return factory.offset(delta);
                }
            }
            return null;
        },

        // private
        adjustWidthAnchor: function(value, childContext) {
            return value;
        },

        // private
        adjustHeightAnchor: function(value, childContext) {
            return value;
        },

        configureItem: function(item) {
            var me = this,
                owner = me.owner,
                anchor= item.anchor,
                anchorsArray,
                anchorWidth,
                anchorHeight;

            me.callParent(arguments);

            if (!item.anchor && item.items && !Ext.isNumber(item.width) && !(Ext.isIE6 && Ext.isStrict)) {
                item.anchor = anchor = me.defaultAnchor;
            }

            /**
             * @cfg {Number/Object} anchorSize
             * Defines the anchoring size of container.
             * Either a number to define the width of the container or an object with `width` and `height` fields.
             * @member Ext.container.Container
             */
            if (owner.anchorSize) {
                if (typeof owner.anchorSize == 'number') {
                    anchorWidth = owner.anchorSize;
                } else {
                    anchorWidth = owner.anchorSize.width;
                    anchorHeight = owner.anchorSize.height;
                }
            } else {
                anchorWidth = owner.initialConfig.width;
                anchorHeight = owner.initialConfig.height;
            }

            if (anchor) {
                // cache all anchor values
                anchorsArray = anchor.split(' ');
                item.anchorSpec = {
                    right: me.parseAnchor(anchorsArray[0], item.initialConfig.width, anchorWidth),
                    bottom: me.parseAnchor(anchorsArray[1], item.initialConfig.height, anchorHeight)
                };
            }
        },

        sizePolicy: {
            '': {
                setsWidth: 0,
                setsHeight: 0
            },
            b: {
                setsWidth: 0,
                setsHeight: 1
            },
            r: {
                '': {
                    setsWidth: 1,
                    setsHeight: 0
                },
                b: {
                    setsWidth: 1,
                    setsHeight: 1

                }
            }
        },

        getItemSizePolicy: function (item) {
            var anchorSpec = item.anchorSpec,
                key = '',
                policy = this.sizePolicy,
                sizeModel;

            if (anchorSpec) {
                sizeModel = this.owner.getSizeModel();
                if (anchorSpec.right && !sizeModel.width.shrinkWrap) {
                    policy = policy.r;
                }
                if (anchorSpec.bottom && !sizeModel.height.shrinkWrap) {
                    key = 'b';
                }
            }

            return policy[key];
        }
    });