/**
 * @class Ext.TabPanel
 *

A basic tab container. TabPanels can be used exactly like a standard {@link Ext.Panel}
 * for layout purposes, but also have special support for containing child Components
 * (
{@link Ext.Container#items items}) that are managed using a
 * {@link Ext.layout.CardLayout CardLayout layout manager}, and displayed as separate tabs.


 *
 *
Note: By default, a tab's close tool destroys the child tab Component
 * and all its descendants. This makes the child tab Component, and all its descendants
unusable. To enable
 * re-use of a tab, configure the TabPanel with
{@link #autoDestroy autoDestroy: false}.
 *
 *

TabPanel header/footer elements


 *

TabPanels use their {@link Ext.Panel#header header} or {@link Ext.Panel#footer footer} element
 * (depending on the {@link #tabPosition} configuration) to accommodate the tab selector buttons.
 * This means that a TabPanel will not display any configured title, and will not display any
 * configured header {@link Ext.Panel#tools tools}.


 *

To display a header, embed the TabPanel in a {@link Ext.Panel Panel} which uses
 *
{@link Ext.Container#layout layout:'fit'}.


 *
 *

Tab Events


 *

There is no actual tab class — each tab is simply a {@link Ext.BoxComponent Component}
 * such as a {@link Ext.Panel Panel}. However, when rendered in a TabPanel, each child Component
 * can fire additional events that only exist for tabs and are not available from other Components.
 * These events are:


 *

 *

Creating TabPanels from Code


 *

TabPanels can be created and rendered completely in code, as in this example:


 *

var tabs = new Ext.TabPanel({
    renderTo: Ext.getBody(),
    activeTab: 0,
    items: [{
        title: 'Tab 1',
        html: 'A simple tab'
    },{
        title: 'Tab 2',
        html: 'Another one'
    }]
});

 *

Creating TabPanels from Existing Markup


 *

TabPanels can also be rendered from pre-existing markup in a couple of ways.


 *

 *
 * @extends Ext.Panel
 * @constructor
 * @param {Object} config The configuration options
 * @xtype tabpanel
 */

Ext.TabPanel = Ext.extend(Ext.Panel,  {
   
/**
     * @cfg {Boolean} layoutOnTabChange
     * Set to true to force a layout of the active tab when the tab is changed. Defaults to false.
     * See {@link Ext.layout.CardLayout}.
{@link Ext.layout.CardLayout#layoutOnCardChange layoutOnCardChange}.
     */

   
/**
     * @cfg {String} tabCls
This config option is used on child Components of ths TabPanel. A CSS
     * class name applied to the tab strip item representing the child Component, allowing special
     * styling to be applied.
     */

   
/**
     * @cfg {Boolean} monitorResize True to automatically monitor window resize events and rerender the layout on
     * browser resize (defaults to true).
     */

    monitorResize
: true,
   
/**
     * @cfg {Boolean} deferredRender
     *

true by default to defer the rendering of child {@link Ext.Container#items items}
     * to the browsers DOM until a tab is activated.
false will render all contained
     *
{@link Ext.Container#items items} as soon as the {@link Ext.layout.CardLayout layout}
     * is rendered. If there is a significant amount of content or a lot of heavy controls being
     * rendered into panels that are not displayed by default, setting this to
true might
     * improve performance.


     *

The deferredRender property is internally passed to the layout manager for
     * TabPanels ({@link Ext.layout.CardLayout}) as its {@link Ext.layout.CardLayout#deferredRender}
     * configuration value.


     *

Note: leaving deferredRender as true means that the content
     * within an unactivated tab will not be available. For example, this means that if the TabPanel
     * is within a {@link Ext.form.FormPanel form}, then until a tab is activated, any Fields within
     * unactivated tabs will not be rendered, and will therefore not be submitted and will not be
     * available to either {@link Ext.form.BasicForm#getValues getValues} or
     * {@link Ext.form.BasicForm#setValues setValues}.


     */

    deferredRender
: true,
   
/**
     * @cfg {Number} tabWidth The initial width in pixels of each new tab (defaults to 120).
     */

    tabWidth
: 120,
   
/**
     * @cfg {Number} minTabWidth The minimum width in pixels for each tab when {@link #resizeTabs} = true (defaults to 30).
     */

    minTabWidth
: 30,
   
/**
     * @cfg {Boolean} resizeTabs True to automatically resize each tab so that the tabs will completely fill the
     * tab strip (defaults to false).  Setting this to true may cause specific widths that might be set per tab to
     * be overridden in order to fit them all into view (although {@link #minTabWidth} will always be honored).
     */

    resizeTabs
: false,
   
/**
     * @cfg {Boolean} enableTabScroll True to enable scrolling to tabs that may be invisible due to overflowing the
     * overall TabPanel width. Only available with tabPosition:'top' (defaults to false).
     */

    enableTabScroll
: false,
   
/**
     * @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed
     * (defaults to
100, or if {@link #resizeTabs} = true, the calculated tab width).  Only
     * applies when
{@link #enableTabScroll} = true.
     */

    scrollIncrement
: 0,
   
/**
     * @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a tab scroll button is
     * continuously pressed (defaults to
400).
     */

    scrollRepeatInterval
: 400,
   
/**
     * @cfg {Float} scrollDuration The number of milliseconds that each scroll animation should last (defaults
     * to
.35). Only applies when {@link #animScroll} = true.
     */

    scrollDuration
: 0.35,
   
/**
     * @cfg {Boolean} animScroll True to animate tab scrolling so that hidden tabs slide smoothly into view (defaults
     * to
true).  Only applies when {@link #enableTabScroll} = true.
     */

    animScroll
: true,
   
/**
     * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to
'top').
     * The only other supported value is
'bottom'.  Note: tab scrolling is only supported for
     *
tabPosition: 'top'.
     */

    tabPosition
: 'top',
   
/**
     * @cfg {String} baseCls The base CSS class applied to the panel (defaults to
'x-tab-panel').
     */

    baseCls
: 'x-tab-panel',
   
/**
     * @cfg {Boolean} autoTabs
     *

true to query the DOM for any divs with a class of 'x-tab' to be automatically converted
     * to tabs and added to this panel (defaults to
false).  Note that the query will be executed within
     * the scope of the container element only (so that multiple tab panels from markup can be supported via this
     * method).


     *

This method is only possible when the markup is structured correctly as a container with nested divs
     * containing the class
'x-tab'. To create TabPanels without these limitations, or to pull tab content
     * from other elements on the page, see the example at the top of the class for generating tabs from markup.


     *

There are a couple of things to note when using this method:

Example usage:


     *

var tabs = new Ext.TabPanel({
    applyTo: 'my-tabs',
    activeTab: 0,
    deferredRender: false,
    autoTabs: true
});

// This markup will be converted to a TabPanel from the code above
<div id="my-tabs">
    <div class="x-tab" title="Tab 1">A simple tab</div>
    <div class="x-tab" title="Tab 2">Another one</div>
</div>

     */

    autoTabs
: false,
   
/**
     * @cfg {String} autoTabSelector The CSS selector used to search for tabs in existing markup when
     *
{@link #autoTabs} = true (defaults to 'div.x-tab').  This can be any valid selector
     * supported by {@link Ext.DomQuery#select}. Note that the query will be executed within the scope of this
     * tab panel only (so that multiple tab panels from markup can be supported on a page).
     */

    autoTabSelector
: 'div.x-tab',
   
/**
     * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially
     * activated on render (defaults to none).
     */

    activeTab
: null,
   
/**
     * @cfg {Number} tabMargin The number of pixels of space to calculate into the sizing and scrolling of
     * tabs. If you change the margin in CSS, you will need to update this value so calculations are correct
     * with either
{@link #resizeTabs} or scrolling tabs. (defaults to 2)
     */

    tabMargin
: 2,
   
/**
     * @cfg {Boolean} plain true to render the tab strip without a background container image
     * (defaults to
false).
     */

    plain
: false,
   
/**
     * @cfg {Number} wheelIncrement For scrolling tabs, the number of pixels to increment on mouse wheel
     * scrolling (defaults to
20).
     */

    wheelIncrement
: 20,

   
/*
     * This is a protected property used when concatenating tab ids to the TabPanel id for internal uniqueness.
     * It does not generally need to be changed, but can be if external code also uses an id scheme that can
     * potentially clash with this one.
     */

    idDelimiter
: '__',

   
// private
    itemCls
: 'x-tab-item',

   
// private config overrides
    elements
: 'body',
    headerAsText
: false,
    frame
: false,
    hideBorders
:true,

   
// private
    initComponent
: function(){
       
this.frame = false;
       
Ext.TabPanel.superclass.initComponent.call(this);
       
this.addEvents(
           
/**
             * @event beforetabchange
             * Fires before the active tab changes. Handlers can
return false to cancel the tab change.
             * @param {TabPanel} this
             * @param {Panel} newTab The tab being activated
             * @param {Panel} currentTab The current active tab
             */

           
'beforetabchange',
           
/**
             * @event tabchange
             * Fires after the active tab has changed.
             * @param {TabPanel} this
             * @param {Panel} tab The new active tab
             */

           
'tabchange',
           
/**
             * @event contextmenu
             * Relays the contextmenu event from a tab selector element in the tab strip.
             * @param {TabPanel} this
             * @param {Panel} tab The target tab
             * @param {EventObject} e
             */

           
'contextmenu'
       
);
       
/**
         * @cfg {Object} layoutConfig
         * TabPanel implicitly uses {@link Ext.layout.CardLayout} as its layout manager.
         *
layoutConfig may be used to configure this layout manager.
         *
{@link #deferredRender} and {@link #layoutOnTabChange}
         * configured on the TabPanel will be applied as configs to the layout manager.
         */

       
this.setLayout(new Ext.layout.CardLayout(Ext.apply({
            layoutOnCardChange
: this.layoutOnTabChange,
            deferredRender
: this.deferredRender
       
}, this.layoutConfig)));

       
if(this.tabPosition == 'top'){
           
this.elements += ',header';
           
this.stripTarget = 'header';
       
}else {
           
this.elements += ',footer';
           
this.stripTarget = 'footer';
       
}
       
if(!this.stack){
           
this.stack = Ext.TabPanel.AccessStack();
       
}
       
this.initItems();
   
},

   
// private
    onRender
: function(ct, position){
       
Ext.TabPanel.superclass.onRender.call(this, ct, position);

       
if(this.plain){
           
var pos = this.tabPosition == 'top' ? 'header' : 'footer';
           
this[pos].addClass('x-tab-panel-'+pos+'-plain');
       
}

       
var st = this[this.stripTarget];

       
this.stripWrap = st.createChild({cls:'x-tab-strip-wrap', cn:{
            tag
:'ul', cls:'x-tab-strip x-tab-strip-'+this.tabPosition}});

       
var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
       
this.stripSpacer = st.createChild({cls:'x-tab-strip-spacer'}, beforeEl);
       
this.strip = new Ext.Element(this.stripWrap.dom.firstChild);

       
this.edge = this.strip.createChild({tag:'li', cls:'x-tab-edge'});
       
this.strip.createChild({cls:'x-clear'});

       
this.body.addClass('x-tab-panel-body-'+this.tabPosition);

       
/**
         * @cfg {Template/XTemplate} itemTpl

(Optional) A {@link Ext.Template Template} or
         * {@link Ext.XTemplate XTemplate} which may be provided to process the data object returned from
         *
{@link #getTemplateArgs} to produce a clickable selector element in the tab strip.


         *

The main element created should be a <li> element. In order for a click event on
         * a selector element to be connected to its item, it must take its
id from the TabPanel's
         * native
{@link #getTemplateArgs}.


         *

The child element which contains the title text must be marked by the CSS class
         *
x-tab-strip-inner.


         *

To enable closability, the created element should contain an element marked by the CSS class
         *
x-tab-strip-close.


         *

If a custom itemTpl is supplied, it is the developer's responsibility to create CSS
         * style rules to create the desired appearance.


         * Below is an example of how to create customized tab selector items:

new Ext.TabPanel({
    renderTo: document.body,
    minTabWidth: 115,
    tabWidth: 135,
    enableTabScroll: true,
    width: 600,
    height: 250,
    defaults: {autoScroll:true},
    itemTpl: new Ext.XTemplate(
    '<li class="{cls}" id="{id}" style="overflow:hidden">',
         '<tpl if="closable">',
            '<a class="x-tab-strip-close" onclick="return false;"></a>',
         '</tpl>',
         '<a class="x-tab-right" href="#" onclick="return false;" style="padding-left:6px">',
            '<em class="x-tab-left">',
                '<span class="x-tab-strip-inner">',
                    '<img src="{src}" style="float:left;margin:3px 3px 0 0">',
                    '<span style="margin-left:20px" class="x-tab-strip-text {iconCls}">{text} {extra}</span>',
                '</span>',
            '</em>',
        '</a>',
    '</li>'
    ),
    getTemplateArgs: function(item) {
//      Call the native method to collect the base data. Like the ID!
        var result = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);

//      Add stuff used in our template
        return Ext.apply(result, {
            closable: item.closable,
            src: item.iconSrc,
            extra: item.extraText || ''
        });
    },
    items: [{
        title: 'New Tab 1',
        iconSrc: '../shared/icons/fam/grid.png',
        html: 'Tab Body 1',
        closable: true
    }, {
        title: 'New Tab 2',
        iconSrc: '../shared/icons/fam/grid.png',
        html: 'Tab Body 2',
        extraText: 'Extra stuff in the tab button'
    }]
});

         */

       
if(!this.itemTpl){
           
var tt = new Ext.Template(
                 
'
  • ',
                     
    '',
                     
    '{text}',
                     
    '
  • '
               
    );
                tt
    .disableFormats = true;
                tt
    .compile();
               
    Ext.TabPanel.prototype.itemTpl = tt;
           
    }

           
    this.items.each(this.initTab, this);
       
    },

       
    // private
        afterRender
    : function(){
           
    Ext.TabPanel.superclass.afterRender.call(this);
           
    if(this.autoTabs){
               
    this.readTabs(false);
           
    }
           
    if(this.activeTab !== undefined){
               
    var item = Ext.isObject(this.activeTab) ? this.activeTab : this.items.get(this.activeTab);
               
    delete this.activeTab;
               
    this.setActiveTab(item);
           
    }
       
    },

       
    // private
        initEvents
    : function(){
           
    Ext.TabPanel.superclass.initEvents.call(this);
           
    this.on('add', this.onAdd, this, {target: this});
           
    this.on('remove', this.onRemove, this, {target: this});

           
    this.mon(this.strip, 'mousedown', this.onStripMouseDown, this);
           
    this.mon(this.strip, 'contextmenu', this.onStripContextMenu, this);
           
    if(this.enableTabScroll){
               
    this.mon(this.strip, 'mousewheel', this.onWheel, this);
           
    }
       
    },

       
    // private
        findTargets
    : function(e){
           
    var item = null;
           
    var itemEl = e.getTarget('li', this.strip);
           
    if(itemEl){
                item
    = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
               
    if(item.disabled){
                   
    return {
                        close
    : null,
                        item
    : null,
                        el
    : null
                   
    };
               
    }
           
    }
           
    return {
                close
    : e.getTarget('.x-tab-strip-close', this.strip),
                item
    : item,
                el
    : itemEl
           
    };
       
    },

       
    // private
        onStripMouseDown
    : function(e){
           
    if(e.button !== 0){
               
    return;
           
    }
            e
    .preventDefault();
           
    var t = this.findTargets(e);
           
    if(t.close){
               
    if (t.item.fireEvent('beforeclose', t.item) !== false) {
                    t
    .item.fireEvent('close', t.item);
                   
    this.remove(t.item);
               
    }
               
    return;
           
    }
           
    if(t.item && t.item != this.activeTab){
               
    this.setActiveTab(t.item);
           
    }
       
    },

       
    // private
        onStripContextMenu
    : function(e){
            e
    .preventDefault();
           
    var t = this.findTargets(e);
           
    if(t.item){
               
    this.fireEvent('contextmenu', this, t.item, e);
           
    }
       
    },

       
    /**
         * True to scan the markup in this tab panel for
    {@link #autoTabs} using the
         *
    {@link #autoTabSelector}
         * @param {Boolean} removeExisting True to remove existing tabs
         */

        readTabs
    : function(removeExisting){
           
    if(removeExisting === true){
               
    this.items.each(function(item){
                   
    this.remove(item);
               
    }, this);
           
    }
           
    var tabs = this.el.query(this.autoTabSelector);
           
    for(var i = 0, len = tabs.length; i < len; i++){
               
    var tab = tabs[i];
               
    var title = tab.getAttribute('title');
                tab
    .removeAttribute('title');
               
    this.add({
                    title
    : title,
                    contentEl
    : tab
               
    });
           
    }
       
    },

       
    // private
        initTab
    : function(item, index){
           
    var before = this.strip.dom.childNodes[index];
           
    var p = this.getTemplateArgs(item);
           
    var el = before ?
                     
    this.itemTpl.insertBefore(before, p) :
                     
    this.itemTpl.append(this.strip, p);

           
    Ext.fly(el).addClassOnOver('x-tab-strip-over');

           
    if(item.tabTip){
               
    Ext.fly(el).child('span.x-tab-strip-text', true).qtip = item.tabTip;
           
    }
            item
    .tabEl = el;

            item
    .on('disable', this.onItemDisabled, this);
            item
    .on('enable', this.onItemEnabled, this);
            item
    .on('titlechange', this.onItemTitleChanged, this);
            item
    .on('iconchange', this.onItemIconChanged, this);
            item
    .on('beforeshow', this.onBeforeShowItem, this);
       
    },

       
    /**
         *

    Provides template arguments for rendering a tab selector item in the tab strip.


         *

    This method returns an object hash containing properties used by the TabPanel's {@link #itemTpl}
         * to create a formatted, clickable tab selector element. The properties which must be returned
         * are:


         * @param {BoxComponent} item The {@link Ext.BoxComponent BoxComponent} for which to create a selector element in the tab strip.
         * @return {Object} An object hash containing the properties required to render the selector element.
         */

        getTemplateArgs
    : function(item) {
           
    var cls = item.closable ? 'x-tab-strip-closable' : '';
           
    if(item.disabled){
                cls
    += ' x-item-disabled';
           
    }
           
    if(item.iconCls){
                cls
    += ' x-tab-with-icon';
           
    }
           
    if(item.tabCls){
                cls
    += ' ' + item.tabCls;
           
    }

           
    return {
                id
    : this.id + this.idDelimiter + item.getItemId(),
                text
    : item.title,
                cls
    : cls,
                iconCls
    : item.iconCls || ''
           
    };
       
    },

       
    // private
        onAdd
    : function(tp, item, index){
           
    this.initTab(item, index);
           
    if(this.items.getCount() == 1){
               
    this.syncSize();
           
    }
           
    this.delegateUpdates();
       
    },

       
    // private
        onBeforeAdd
    : function(item){
           
    var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
           
    if(existing){
               
    this.setActiveTab(item);
               
    return false;
           
    }
           
    Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
           
    var es = item.elements;
            item
    .elements = es ? es.replace(',header', '') : es;
            item
    .border = (item.border === true);
       
    },

       
    // private
        onRemove
    : function(tp, item){
           
    Ext.destroy(Ext.get(this.getTabEl(item)));
           
    this.stack.remove(item);
            item
    .un('disable', this.onItemDisabled, this);
            item
    .un('enable', this.onItemEnabled, this);
            item
    .un('titlechange', this.onItemTitleChanged, this);
            item
    .un('iconchange', this.onItemIconChanged, this);
            item
    .un('beforeshow', this.onBeforeShowItem, this);
           
    if(item == this.activeTab){
               
    var next = this.stack.next();
               
    if(next){
                   
    this.setActiveTab(next);
               
    }else if(this.items.getCount() > 0){
                   
    this.setActiveTab(0);
               
    }else{
                   
    this.activeTab = null;
               
    }
           
    }
           
    this.delegateUpdates();
       
    },

       
    // private
        onBeforeShowItem
    : function(item){
           
    if(item != this.activeTab){
               
    this.setActiveTab(item);
               
    return false;
           
    }
       
    },

       
    // private
        onItemDisabled
    : function(item){
           
    var el = this.getTabEl(item);
           
    if(el){
               
    Ext.fly(el).addClass('x-item-disabled');
           
    }
           
    this.stack.remove(item);
       
    },

       
    // private
        onItemEnabled
    : function(item){
           
    var el = this.getTabEl(item);
           
    if(el){
               
    Ext.fly(el).removeClass('x-item-disabled');
           
    }
       
    },

       
    // private
        onItemTitleChanged
    : function(item){
           
    var el = this.getTabEl(item);
           
    if(el){
               
    Ext.fly(el).child('span.x-tab-strip-text', true).innerHTML = item.title;
           
    }
       
    },

       
    //private
        onItemIconChanged
    : function(item, iconCls, oldCls){
           
    var el = this.getTabEl(item);
           
    if(el){
               
    Ext.fly(el).child('span.x-tab-strip-text').replaceClass(oldCls, iconCls);
           
    }
       
    },

       
    /**
         * Gets the DOM element for the tab strip item which activates the child panel with the specified
         * ID. Access this to change the visual treatment of the item, for example by changing the CSS class name.
         * @param {Panel/Number/String} tab The tab component, or the tab's index, or the tabs id or itemId.
         * @return {HTMLElement} The DOM node
         */

        getTabEl
    : function(item){
           
    return document.getElementById(this.id + this.idDelimiter + this.getComponent(item).getItemId());
       
    },

       
    // private
        onResize
    : function(){
           
    Ext.TabPanel.superclass.onResize.apply(this, arguments);
           
    this.delegateUpdates();
       
    },

       
    /**
         * Suspends any internal calculations or scrolling while doing a bulk operation. See {@link #endUpdate}
         */

        beginUpdate
    : function(){
           
    this.suspendUpdates = true;
       
    },

       
    /**
         * Resumes calculations and scrolling at the end of a bulk operation. See {@link #beginUpdate}
         */

        endUpdate
    : function(){
           
    this.suspendUpdates = false;
           
    this.delegateUpdates();
       
    },

       
    /**
         * Hides the tab strip item for the passed tab
         * @param {Number/String/Panel} item The tab index, id or item
         */

        hideTabStripItem
    : function(item){
            item
    = this.getComponent(item);
           
    var el = this.getTabEl(item);
           
    if(el){
                el
    .style.display = 'none';
               
    this.delegateUpdates();
           
    }
           
    this.stack.remove(item);
       
    },

       
    /**
         * Unhides the tab strip item for the passed tab
         * @param {Number/String/Panel} item The tab index, id or item
         */

        unhideTabStripItem
    : function(item){
            item
    = this.getComponent(item);
           
    var el = this.getTabEl(item);
           
    if(el){
                el
    .style.display = '';
               
    this.delegateUpdates();
           
    }
       
    },

       
    // private
        delegateUpdates
    : function(){
           
    if(this.suspendUpdates){
               
    return;
           
    }
           
    if(this.resizeTabs && this.rendered){
               
    this.autoSizeTabs();
           
    }
           
    if(this.enableTabScroll && this.rendered){
               
    this.autoScrollTabs();
           
    }
       
    },

       
    // private
        autoSizeTabs
    : function(){
           
    var count = this.items.length;
           
    var ce = this.tabPosition != 'bottom' ? 'header' : 'footer';
           
    var ow = this[ce].dom.offsetWidth;
           
    var aw = this[ce].dom.clientWidth;

           
    if(!this.resizeTabs || count < 1 || !aw){ // !aw for display:none
               
    return;
           
    }

           
    var each = Math.max(Math.min(Math.floor((aw-4) / count) - this.tabMargin, this.tabWidth), this.minTabWidth); // -4 for float errors in IE
           
    this.lastTabWidth = each;
           
    var lis = this.strip.query("li:not([className^=x-tab-edge])");
           
    for(var i = 0, len = lis.length; i < len; i++) {
               
    var li = lis[i];
               
    var inner = Ext.fly(li).child('.x-tab-strip-inner', true);
               
    var tw = li.offsetWidth;
               
    var iw = inner.offsetWidth;
                inner
    .style.width = (each - (tw-iw)) + 'px';
           
    }
       
    },

       
    // private
        adjustBodyWidth
    : function(w){
           
    if(this.header){
               
    this.header.setWidth(w);
           
    }
           
    if(this.footer){
               
    this.footer.setWidth(w);
           
    }
           
    return w;
       
    },

       
    /**
         * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
         * can
    return false to cancel the tab change.
         * @param {String/Number} item
         * The id or tab Panel to activate. This parameter may be any of the following:
         *

         *

    For additional information see {@link Ext.util.MixedCollection#get}.
         */

        setActiveTab
    : function(item){
            item
    = this.getComponent(item);
           
    if(!item || this.fireEvent('beforetabchange', this, item, this.activeTab) === false){
               
    return;
           
    }
           
    if(!this.rendered){
               
    this.activeTab = item;
               
    return;
           
    }
           
    if(this.activeTab != item){
               
    if(this.activeTab){
                   
    var oldEl = this.getTabEl(this.activeTab);
                   
    if(oldEl){
                       
    Ext.fly(oldEl).removeClass('x-tab-strip-active');
                   
    }
                   
    this.activeTab.fireEvent('deactivate', this.activeTab);
               
    }
               
    var el = this.getTabEl(item);
               
    Ext.fly(el).addClass('x-tab-strip-active');
               
    this.activeTab = item;
               
    this.stack.add(item);

               
    this.layout.setActiveItem(item);
               
    if(this.scrolling){
                   
    this.scrollToTab(item, this.animScroll);
               
    }

                item
    .fireEvent('activate', item);
               
    this.fireEvent('tabchange', this, item);
           
    }
       
    },

       

    /**
         * Gets the currently active tab.
         * @return {Panel} The active tab
         */

        getActiveTab
    : function(){
           
    return this.activeTab || null;
       
    },

       
    /**
         * Gets the specified tab by id.
         * @param {String} id The tab id
         * @return {Panel} The tab
         */

        getItem
    : function(item){
           
    return this.getComponent(item);
       
    },

       
    // private
        autoScrollTabs
    : function(){
           
    this.pos = this.tabPosition=='bottom' ? this.footer : this.header;
           
    var count = this.items.length;
           
    var ow = this.pos.dom.offsetWidth;
           
    var tw = this.pos.dom.clientWidth;

           
    var wrap = this.stripWrap;
           
    var wd = wrap.dom;
           
    var cw = wd.offsetWidth;
           
    var pos = this.getScrollPos();
           
    var l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos;

           
    if(!this.enableTabScroll || count < 1 || cw < 20){ // 20 to prevent display:none issues
               
    return;
           
    }
           
    if(l <= tw){
                wd
    .scrollLeft = 0;
                wrap
    .setWidth(tw);
               
    if(this.scrolling){
                   
    this.scrolling = false;
                   
    this.pos.removeClass('x-tab-scrolling');
                   
    this.scrollLeft.hide();
                   
    this.scrollRight.hide();
                   
    // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
                   
    if(Ext.isAir || Ext.isWebKit){
                        wd
    .style.marginLeft = '';
                        wd
    .style.marginRight = '';
                   
    }
               
    }
           
    }else{
               
    if(!this.scrolling){
                   
    this.pos.addClass('x-tab-scrolling');
                   
    // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
                   
    if(Ext.isAir || Ext.isWebKit){
                        wd
    .style.marginLeft = '18px';
                        wd
    .style.marginRight = '18px';
                   
    }
               
    }
                tw
    -= wrap.getMargins('lr');
                wrap
    .setWidth(tw > 20 ? tw : 20);
               
    if(!this.scrolling){
                   
    if(!this.scrollLeft){
                       
    this.createScrollers();
                   
    }else{
                       
    this.scrollLeft.show();
                       
    this.scrollRight.show();
                   
    }
               
    }
               
    this.scrolling = true;
               
    if(pos > (l-tw)){ // ensure it stays within bounds
                    wd
    .scrollLeft = l-tw;
               
    }else{ // otherwise, make sure the active tab is still visible
                   
    this.scrollToTab(this.activeTab, false);
               
    }
               
    this.updateScrollButtons();
           
    }
       
    },

       
    // private
        createScrollers
    : function(){
           
    this.pos.addClass('x-tab-scrolling-' + this.tabPosition);
           
    var h = this.stripWrap.dom.offsetHeight;

           
    // left
           
    var sl = this.pos.insertFirst({
                cls
    :'x-tab-scroller-left'
           
    });
            sl
    .setHeight(h);
            sl
    .addClassOnOver('x-tab-scroller-left-over');
           
    this.leftRepeater = new Ext.util.ClickRepeater(sl, {
                interval
    : this.scrollRepeatInterval,
                handler
    : this.onScrollLeft,
                scope
    : this
           
    });
           
    this.scrollLeft = sl;

           
    // right
           
    var sr = this.pos.insertFirst({
                cls
    :'x-tab-scroller-right'
           
    });
            sr
    .setHeight(h);
            sr
    .addClassOnOver('x-tab-scroller-right-over');
           
    this.rightRepeater = new Ext.util.ClickRepeater(sr, {
                interval
    : this.scrollRepeatInterval,
                handler
    : this.onScrollRight,
                scope
    : this
           
    });
           
    this.scrollRight = sr;
       
    },

       
    // private
        getScrollWidth
    : function(){
           
    return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos();
       
    },

       
    // private
        getScrollPos
    : function(){
           
    return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0;
       
    },

       
    // private
        getScrollArea
    : function(){
           
    return parseInt(this.stripWrap.dom.clientWidth, 10) || 0;
       
    },

       
    // private
        getScrollAnim
    : function(){
           
    return {duration:this.scrollDuration, callback: this.updateScrollButtons, scope: this};
       
    },

       
    // private
        getScrollIncrement
    : function(){
           
    return this.scrollIncrement || (this.resizeTabs ? this.lastTabWidth+2 : 100);
       
    },

       
    /**
         * Scrolls to a particular tab if tab scrolling is enabled
         * @param {Panel} item The item to scroll to
         * @param {Boolean} animate True to enable animations
         */


        scrollToTab
    : function(item, animate){
           
    if(!item){ return; }
           
    var el = this.getTabEl(item);
           
    var pos = this.getScrollPos(), area = this.getScrollArea();
           
    var left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos;
           
    var right = left + el.offsetWidth;
           
    if(left < pos){
               
    this.scrollTo(left, animate);
           
    }else if(right > (pos + area)){
               
    this.scrollTo(right - area, animate);
           
    }
       
    },

       
    // private
        scrollTo
    : function(pos, animate){
           
    this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false);
           
    if(!animate){
               
    this.updateScrollButtons();
           
    }
       
    },

        onWheel
    : function(e){
           
    var d = e.getWheelDelta()*this.wheelIncrement*-1;
            e
    .stopEvent();

           
    var pos = this.getScrollPos();
           
    var newpos = pos + d;
           
    var sw = this.getScrollWidth()-this.getScrollArea();

           
    var s = Math.max(0, Math.min(sw, newpos));
           
    if(s != pos){
               
    this.scrollTo(s, false);
           
    }
       
    },

       
    // private
        onScrollRight
    : function(){
           
    var sw = this.getScrollWidth()-this.getScrollArea();
           
    var pos = this.getScrollPos();
           
    var s = Math.min(sw, pos + this.getScrollIncrement());
           
    if(s != pos){
               
    this.scrollTo(s, this.animScroll);
           
    }
       
    },

       
    // private
        onScrollLeft
    : function(){
           
    var pos = this.getScrollPos();
           
    var s = Math.max(0, pos - this.getScrollIncrement());
           
    if(s != pos){
               
    this.scrollTo(s, this.animScroll);
           
    }
       
    },

       
    // private
        updateScrollButtons
    : function(){
           
    var pos = this.getScrollPos();
           
    this.scrollLeft[pos === 0 ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
           
    this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
       
    },

       
    // private
        beforeDestroy
    : function() {
           
    if(this.items){
               
    this.items.each(function(item){
                   
    if(item && item.tabEl){
                       
    Ext.get(item.tabEl).removeAllListeners();
                        item
    .tabEl = null;
                   
    }
               
    }, this);
           
    }
           
    if(this.strip){
               
    this.strip.removeAllListeners();
           
    }
           
    Ext.TabPanel.superclass.beforeDestroy.apply(this);
       
    }

       
    /**
         * @cfg {Boolean} collapsible
         * @hide
         */

       
    /**
         * @cfg {String} header
         * @hide
         */

       
    /**
         * @cfg {Boolean} headerAsText
         * @hide
         */

       
    /**
         * @property header
         * @hide
         */

       
    /**
         * @property title
         * @hide
         */

       
    /**
         * @cfg {Array} tools
         * @hide
         */

       
    /**
         * @cfg {Array} toolTemplate
         * @hide
         */

       
    /**
         * @cfg {Boolean} hideCollapseTool
         * @hide
         */

       
    /**
         * @cfg {Boolean} titleCollapse
         * @hide
         */

       
    /**
         * @cfg {Boolean} collapsed
         * @hide
         */

       
    /**
         * @cfg {String} layout
         * @hide
         */

       
    /**
         * @cfg {Boolean} preventBodyReset
         * @hide
         */

    });
    Ext.reg('tabpanel', Ext.TabPanel);

    /**
     * See {@link #setActiveTab}. Sets the specified tab as the active tab. This method fires
     * the {@link #beforetabchange} event which can
    return false to cancel the tab change.
     * @param {String/Panel} tab The id or tab Panel to activate
     * @method activate
     */

    Ext.TabPanel.prototype.activate = Ext.TabPanel.prototype.setActiveTab;

    // private utility class used by TabPanel
    Ext.TabPanel.AccessStack = function(){
       
    var items = [];
       
    return {
            add
    : function(item){
                items
    .push(item);
               
    if(items.length > 10){
                    items
    .shift();
               
    }
           
    },

            remove
    : function(item){
               
    var s = [];
               
    for(var i = 0, len = items.length; i < len; i++) {
                   
    if(items[i] != item){
                        s
    .push(items[i]);
                   
    }
               
    }
                items
    = s;
           
    },

            next
    : function(){
               
    return items.pop();
           
    }
       
    };
    };