    /**
     * @class Ext.layout.ToolbarLayout
     * @extends Ext.layout.ContainerLayout
     * Layout manager used by Ext.Toolbar. This is highly specialised for use by Toolbars and would not
     * usually be used by any other class.
     */
    Ext.layout.ToolbarLayout = Ext.extend(Ext.layout.ContainerLayout, {
        monitorResize : true,

        type: 'toolbar',

        /**
         * @property triggerWidth
         * @type Number
         * The width allocated for the menu trigger at the extreme right end of the Toolbar
         */
        triggerWidth: 18,

        /**
         * @property noItemsMenuText
         * @type String
         * HTML fragment to render into the toolbar overflow menu if there are no items to display
         */
        noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',

        /**
         * @private
         * @property lastOverflow
         * @type Boolean
         * Used internally to record whether the last layout caused an overflow or not
         */
        lastOverflow: false,

        /**
         * @private
         * @property tableHTML
         * @type String
         * String used to build the HTML injected to support the Toolbar's layout. The align property is
         * injected into this string inside the td.x-toolbar-left element during onLayout.
         */
        tableHTML: [
            '<table cellspacing="0" class="x-toolbar-ct">',
                '<tbody>',
                    '<tr>',
                        '<td class="x-toolbar-left" align="{0}">',
                            '<table cellspacing="0">',
                                '<tbody>',
                                    '<tr class="x-toolbar-left-row"></tr>',
                                '</tbody>',
                            '</table>',
                        '</td>',
                        '<td class="x-toolbar-right" align="right">',
                            '<table cellspacing="0" class="x-toolbar-right-ct">',
                                '<tbody>',
                                    '<tr>',
                                        '<td>',
                                            '<table cellspacing="0">',
                                                '<tbody>',
                                                    '<tr class="x-toolbar-right-row"></tr>',
                                                '</tbody>',
                                            '</table>',
                                        '</td>',
                                        '<td>',
                                            '<table cellspacing="0">',
                                                '<tbody>',
                                                    '<tr class="x-toolbar-extras-row"></tr>',
                                                '</tbody>',
                                            '</table>',
                                        '</td>',
                                    '</tr>',
                                '</tbody>',
                            '</table>',
                        '</td>',
                    '</tr>',
                '</tbody>',
            '</table>'
        ].join(""),

        /**
         * @private
         * Create the wrapping Toolbar HTML and render/move all the items into the correct places
         */
        onLayout : function(ct, target) {
            //render the Toolbar <table> HTML if it's not already present
            if (!this.leftTr) {
                var align = ct.buttonAlign == 'center' ? 'center' : 'left';

                target.addClass('x-toolbar-layout-ct');
                target.insertHtml('beforeEnd', String.format(this.tableHTML, align));

                this.leftTr   = target.child('tr.x-toolbar-left-row', true);
                this.rightTr  = target.child('tr.x-toolbar-right-row', true);
                this.extrasTr = target.child('tr.x-toolbar-extras-row', true);

                if (this.hiddenItem == undefined) {
                    /**
                     * @property hiddenItems
                     * @type Array
                     * Holds all items that are currently hidden due to there not being enough space to render them
                     * These items will appear on the expand menu.
                     */
                    this.hiddenItems = [];
                }
            }

            var side     = ct.buttonAlign == 'right' ? this.rightTr : this.leftTr,
                items    = ct.items.items,
                position = 0;

            //render each item if not already rendered, place it into the correct (left or right) target
            for (var i = 0, len = items.length, c; i < len; i++, position++) {
                c = items[i];

                if (c.isFill) {
                    side   = this.rightTr;
                    position = -1;
                } else if (!c.rendered) {
                    c.render(this.insertCell(c, side, position));
                    this.configureItem(c);
                } else {
                    if (!c.xtbHidden && !this.isValidParent(c, side.childNodes[position])) {
                        var td = this.insertCell(c, side, position);
                        td.appendChild(c.getPositionEl().dom);
                        c.container = Ext.get(td);
                    }
                }
            }

            //strip extra empty cells
            this.cleanup(this.leftTr);
            this.cleanup(this.rightTr);
            this.cleanup(this.extrasTr);
            this.fitToSize(target);
        },

        /**
         * @private
         * Removes any empty nodes from the given element
         * @param {Ext.Element} el The element to clean up
         */
        cleanup : function(el) {
            var cn = el.childNodes, i, c;

            for (i = cn.length-1; i >= 0 && (c = cn[i]); i--) {
                if (!c.firstChild) {
                    el.removeChild(c);
                }
            }
        },

        /**
         * @private
         * Inserts the given Toolbar item into the given element
         * @param {Ext.Component} c The component to add
         * @param {Ext.Element} target The target to add the component to
         * @param {Number} position The position to add the component at
         */
        insertCell : function(c, target, position) {
            var td = document.createElement('td');
            td.className = 'x-toolbar-cell';

            target.insertBefore(td, target.childNodes[position] || null);

            return td;
        },

        /**
         * @private
         * Hides an item because it will not fit in the available width. The item will be unhidden again
         * if the Toolbar is resized to be large enough to show it
         * @param {Ext.Component} item The item to hide
         */
        hideItem : function(item) {
            this.hiddenItems.push(item);

            item.xtbHidden = true;
            item.xtbWidth = item.getPositionEl().dom.parentNode.offsetWidth;
            item.hide();
        },

        /**
         * @private
         * Unhides an item that was previously hidden due to there not being enough space left on the Toolbar
         * @param {Ext.Component} item The item to show
         */
        unhideItem : function(item) {
            item.show();
            item.xtbHidden = false;
            this.hiddenItems.remove(item);
        },

        /**
         * @private
         * Returns the width of the given toolbar item. If the item is currently hidden because there
         * is not enough room to render it, its previous width is returned
         * @param {Ext.Component} c The component to measure
         * @return {Number} The width of the item
         */
        getItemWidth : function(c) {
            return c.hidden ? (c.xtbWidth || 0) : c.getPositionEl().dom.parentNode.offsetWidth;
        },

        /**
         * @private
         * Called at the end of onLayout. At this point the Toolbar has already been resized, so we need
         * to fit the items into the available width. We add up the width required by all of the items in
         * the toolbar - if we don't have enough space we hide the extra items and render the expand menu
         * trigger.
         * @param {Ext.Element} target The Element the Toolbar is currently laid out within
         */
        fitToSize : function(target) {
            if (this.container.enableOverflow === false) {
                return;
            }

            var width       = target.dom.clientWidth,
                tableWidth  = target.dom.firstChild.offsetWidth,
                clipWidth   = width - this.triggerWidth,
                lastWidth   = this.lastWidth || 0,

                hiddenItems = this.hiddenItems,
                hasHiddens  = hiddenItems.length != 0,
                isLarger    = width >= lastWidth;

            this.lastWidth  = width;

            if (tableWidth > width || (hasHiddens && isLarger)) {
                var items     = this.container.items.items,
                    len       = items.length,
                    loopWidth = 0,
                    item;

                for (var i = 0; i < len; i++) {
                    item = items[i];

                    if (!item.isFill) {
                        loopWidth += this.getItemWidth(item);
                        if (loopWidth > clipWidth) {
                            if (!(item.hidden || item.xtbHidden)) {
                                this.hideItem(item);
                            }
                        } else if (item.xtbHidden) {
                            this.unhideItem(item);
                        }
                    }
                }
            }

            //test for number of hidden items again here because they may have changed above
            hasHiddens = hiddenItems.length != 0;

            if (hasHiddens) {
                this.initMore();

                if (!this.lastOverflow) {
                    this.container.fireEvent('overflowchange', this.container, true);
                    this.lastOverflow = true;
                }
            } else if (this.more) {
                this.clearMenu();
                this.more.destroy();
                delete this.more;

                if (this.lastOverflow) {
                    this.container.fireEvent('overflowchange', this.container, false);
                    this.lastOverflow = false;
                }
            }
        },

        /**
         * @private
         * Returns a menu config for a given component. This config is used to create a menu item
         * to be added to the expander menu
         * @param {Ext.Component} component The component to create the config for
         * @param {Boolean} hideOnClick Passed through to the menu item
         */
        createMenuConfig : function(component, hideOnClick){
            var config = Ext.apply({}, component.initialConfig),
                group  = component.toggleGroup;

            Ext.copyTo(config, component, [
                'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
            ]);

            Ext.apply(config, {
                text       : component.overflowText || component.text,
                hideOnClick: hideOnClick
            });

            if (group || component.enableToggle) {
                Ext.apply(config, {
                    group  : group,
                    checked: component.pressed,
                    listeners: {
                        checkchange: function(item, checked){
                            component.toggle(checked);
                        }
                    }
                });
            }

            delete config.ownerCt;
            delete config.xtype;
            delete config.id;

            return config;
        },

        /**
         * @private
         * Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
         * @param {Ext.menu.Menu} menu The menu to add to
         * @param {Ext.Component} component The component to add
         */
        addComponentToMenu : function(menu, component) {
            if (component instanceof Ext.Toolbar.Separator) {
                menu.add('-');

            } else if (Ext.isFunction(component.isXType)) {
                if (component.isXType('splitbutton')) {
                    menu.add(this.createMenuConfig(component, true));

                } else if (component.isXType('button')) {
                    menu.add(this.createMenuConfig(component, !component.menu));

                } else if (component.isXType('buttongroup')) {
                    component.items.each(function(item){
                         this.addComponentToMenu(menu, item);
                    }, this);
                }
            }
        },

        /**
         * @private
         * Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
         * splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
         */
        clearMenu : function(){
            var menu = this.moreMenu;
            if (menu && menu.items) {
                menu.items.each(function(item){
                    delete item.menu;
                });
            }
        },

        /**
         * @private
         * Called before the expand menu is shown, this rebuilds the menu since it was last shown because
         * it is possible that the items hidden due to space limitations on the Toolbar have changed since.
         * @param {Ext.menu.Menu} m The menu
         */
        beforeMoreShow : function(menu) {
            var items = this.container.items.items,
                len   = items.length,
                item,
                prev;

            var needsSep = function(group, item){
                return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
            };

            this.clearMenu();
            menu.removeAll();
            for (var i = 0; i < len; i++) {
                item = items[i];
                if (item.xtbHidden) {
                    if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
                        menu.add('-');
                    }
                    this.addComponentToMenu(menu, item);
                    prev = item;
                }
            }

            // put something so the menu isn't empty if no compatible items found
            if (menu.items.length < 1) {
                menu.add(this.noItemsMenuText);
            }
        },

        /**
         * @private
         * Creates the expand trigger and menu, adding them to the <tr> at the extreme right of the
         * Toolbar table
         */
        initMore : function(){
            if (!this.more) {
                /**
                 * @private
                 * @property moreMenu
                 * @type Ext.menu.Menu
                 * The expand menu - holds items for every Toolbar item that cannot be shown
                 * because the Toolbar is currently not wide enough.
                 */
                this.moreMenu = new Ext.menu.Menu({
                    ownerCt : this.container,
                    listeners: {
                        beforeshow: this.beforeMoreShow,
                        scope: this
                    }
                });

                /**
                 * @private
                 * @property more
                 * @type Ext.Button
                 * The expand button which triggers the overflow menu to be shown
                 */
                this.more = new Ext.Button({
                    iconCls: 'x-toolbar-more-icon',
                    cls    : 'x-toolbar-more',
                    menu   : this.moreMenu,
                    ownerCt: this.container
                });

                var td = this.insertCell(this.more, this.extrasTr, 100);
                this.more.render(td);
            }
        },

        destroy : function(){
            Ext.destroy(this.more, this.moreMenu);
            delete this.leftTr;
            delete this.rightTr;
            delete this.extrasTr;
            Ext.layout.ToolbarLayout.superclass.destroy.call(this);
        }
    });

    Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;