source: trunk/web/addons/job_monarch/lib/extjs/source/widgets/TabPanel.js @ 619

Last change on this file since 619 was 619, checked in by ramonb, 15 years ago

lib/:

  • added new AJAX dependancies: ExtJS, pChart, Lightbox2
File size: 36.1 KB
Line 
1/*
2 * Ext JS Library 2.2.1
3 * Copyright(c) 2006-2009, Ext JS, LLC.
4 * licensing@extjs.com
5 *
6 * http://extjs.com/license
7 */
8
9/**
10 * @class Ext.TabPanel
11 * <p>A basic tab container. TabPanels can be used exactly like a standard {@link Ext.Panel} for layout
12 * purposes, but also have special support for containing child Components that are managed using a CardLayout
13 * layout manager, and displayed as seperate tabs.</p>
14 * <p>There is no actual tab class &mdash; each tab is simply an {@link Ext.BoxComponent Component} such
15 * as a {@link Ext.Panel Panel}. However, when rendered in a TabPanel, each child Component can fire
16 * additional events that only exist for tabs and are not available from other Component. These are:</p>
17 * <ul>
18 * <li><b>activate</b>: Fires when this Component becomes the active tab.
19 * <div class="mdetail-params">
20 *      <strong style="font-weight: normal;">Listeners will be called with the following arguments:</strong>
21 *      <ul><li><code>tab</code> : Panel<div class="sub-desc">The tab that was activated</div></li></ul>
22 *  </div></li>
23 * <li><b>deactivate</b>: Fires when the Component that was the active tab becomes deactivated.
24 * <div class="mdetail-params">
25 *      <strong style="font-weight: normal;">Listeners will be called with the following arguments:</strong>
26 *      <ul><li><code>tab</code> : Panel<div class="sub-desc">The tab that was deactivated</div></li></ul>
27 *  </div></li>
28 * </ul>
29 * <p>To add Components to a TabPanel which are generated dynamically on the server, it is necessary to
30 * create a server script to generate the Javascript to create the Component required.</p>
31 * For example, to add a GridPanel to a TabPanel where the GridPanel is generated by the server
32 * based on certain parameters, you would need to execute an Ajax request to invoke your the script,
33 * and process the response object to add it to the TabPanel:</p><pre><code>
34Ext.Ajax.request({
35    url: 'gen-invoice-grid.php',
36    params: {
37        startDate = Ext.getCmp('start-date').getValue(),
38        endDate = Ext.getCmp('end-date').getValue()
39    },
40    success: function(xhr) {
41        var newComponent = eval(xhr.responseText);
42        myTabPanel.add(newComponent);
43        myTabPanel.setActiveTab(newComponent);
44    },
45    failure: function() {
46        Ext.Msg.alert("Grid create failed", "Server communication failure");
47    }
48});
49</code></pre>
50 * <p>The server script would need to return an executable Javascript statement which, when processed
51 * using <tt>eval()</tt> will return either a config object with an {@link Ext.Component#xtype xtype},
52 * or an instantiated Component. For example:</p><pre><code>
53(function() {
54    function formatDate(value){
55        return value ? value.dateFormat('M d, Y') : '';
56    };
57
58    var store = new Ext.data.Store({
59        url: 'get-invoice-data.php',
60        baseParams: {
61            startDate: '01/01/2008',
62            endDate: '01/31/2008'
63        },
64        reader: new Ext.data.JsonReader({
65            record: 'transaction',
66            id: 'id',
67            totalRecords: 'total'
68        }, [
69           'customer',
70           'invNo',
71           {name: 'date', type: 'date', dateFormat: 'm/d/Y'},
72           {name: 'value', type: 'float'}
73        ])
74    });
75
76    var grid = new Ext.grid.GridPanel({
77        title: 'Invoice Report',
78        bbar: new Ext.PagingToolbar(store),
79        store: store,
80        columns: [
81            {header: "Customer", width: 250, dataIndex: 'customer', sortable: true},
82            {header: "Invoice Number", width: 120, dataIndex: 'invNo', sortable: true},
83            {header: "Invoice Date", width: 100, dataIndex: 'date', renderer: formatDate, sortable: true},
84            {header: "Value", width: 120, dataIndex: 'value', renderer: 'usMoney', sortable: true}
85        ],
86    });
87    store.load();
88    return grid;
89})();
90</code></pre>
91 * <p>Since that code is <i>generated</i> by a server script, the <tt>baseParams</tt> for the Store
92 * can be configured into the Store. The metadata to allow generation of the Record layout, and the
93 * ColumnModel is also known on the server, so these can be generated into the code.</p>
94 * <p>When that code fragment is passed through the <tt>eval</tt> function in the success handler
95 * of the Ajax request, the code is executed by the Javascript processor, and the anonymous function
96 * runs, and returns the grid.</p>
97 * <p>There are several other methods available for creating TabPanels. The output of the following
98 * examples should produce exactly the same appearance. The tabs can be created and rendered completely
99 * in code, as in this example:</p>
100 * <pre><code>
101var tabs = new Ext.TabPanel({
102    renderTo: Ext.getBody(),
103    activeTab: 0,
104    items: [{
105        title: 'Tab 1',
106        html: 'A simple tab'
107    },{
108        title: 'Tab 2',
109        html: 'Another one'
110    }]
111});
112</code></pre>
113  * <p>TabPanels can also be rendered from pre-existing markup in a couple of ways.  See the {@link #autoTabs} example for
114  * rendering entirely from markup that is already structured correctly as a TabPanel (a container div with
115  * one or more nested tab divs with class 'x-tab'). You can also render from markup that is not strictly
116  * structured by simply specifying by id which elements should be the container and the tabs. Using this method,
117  * tab content can be pulled from different elements within the page by id regardless of page structure.  Note
118  * that the tab divs in this example contain the class 'x-hide-display' so that they can be rendered deferred
119  * without displaying outside the tabs. You could alternately set {@link #deferredRender} to false to render all
120  * content tabs on page load. For example:
121  * <pre><code>
122var tabs = new Ext.TabPanel({
123    renderTo: 'my-tabs',
124    activeTab: 0,
125    items:[
126        {contentEl:'tab1', title:'Tab 1'},
127        {contentEl:'tab2', title:'Tab 2'}
128    ]
129});
130
131// Note that the tabs do not have to be nested within the container (although they can be)
132&lt;div id="my-tabs">&lt;/div>
133&lt;div id="tab1" class="x-hide-display">A simple tab&lt;/div>
134&lt;div id="tab2" class="x-hide-display">Another one&lt;/div>
135</code></pre>
136 * @extends Ext.Panel
137 * @constructor
138 * @param {Object} config The configuration options
139 */
140Ext.TabPanel = Ext.extend(Ext.Panel,  {
141    /**
142     * @cfg {Boolean} layoutOnTabChange Set to true to do a layout of tab items as tabs are changed.
143     */
144    /**
145     * @cfg {String} tabCls <b>This config option is used on <u>child Components</u> of ths TabPanel.</b> A CSS
146     * class name applied to the tab strip item representing the child Component, allowing special
147     * styling to be applied.
148     */
149    /**
150     * @cfg {Boolean} monitorResize True to automatically monitor window resize events and rerender the layout on
151     * browser resize (defaults to true).
152     */
153    monitorResize : true,
154    /**
155     * @cfg {Boolean} deferredRender Internally, the TabPanel uses a {@link Ext.layout.CardLayout} to manage its tabs.
156     * This property will be passed on to the layout as its {@link Ext.layout.CardLayout#deferredRender} config value,
157     * determining whether or not each tab is rendered only when first accessed (defaults to true).
158     * <p>Be aware that leaving deferredRender as <b><tt>true</tt></b> means that, if the TabPanel is within
159     * a {@link Ext.form.FormPanel form}, then until a tab is activated, any Fields within that tab will not
160     * be rendered, and will therefore not be submitted and will not be available to either
161     * {@link Ext.form.BasicForm#getValues getValues} or {@link Ext.form.BasicForm#setValues setValues}.</p>
162     */
163    deferredRender : true,
164    /**
165     * @cfg {Number} tabWidth The initial width in pixels of each new tab (defaults to 120).
166     */
167    tabWidth: 120,
168    /**
169     * @cfg {Number} minTabWidth The minimum width in pixels for each tab when {@link #resizeTabs} = true (defaults to 30).
170     */
171    minTabWidth: 30,
172    /**
173     * @cfg {Boolean} resizeTabs True to automatically resize each tab so that the tabs will completely fill the
174     * tab strip (defaults to false).  Setting this to true may cause specific widths that might be set per tab to
175     * be overridden in order to fit them all into view (although {@link #minTabWidth} will always be honored).
176     */
177    resizeTabs:false,
178    /**
179     * @cfg {Boolean} enableTabScroll True to enable scrolling to tabs that may be invisible due to overflowing the
180     * overall TabPanel width. Only available with tabPosition:'top' (defaults to false).
181     */
182    enableTabScroll: false,
183    /**
184     * @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed (defaults
185     * to 100, or if {@link #resizeTabs} = true, the calculated tab width).  Only applies when {@link #enableTabScroll} = true.
186     */
187    scrollIncrement : 0,
188    /**
189     * @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a tab scroll button is
190     * continuously pressed (defaults to 400).
191     */
192    scrollRepeatInterval : 400,
193    /**
194     * @cfg {Float} scrollDuration The number of milliseconds that each scroll animation should last (defaults to .35).
195     * Only applies when {@link #animScroll} = true.
196     */
197    scrollDuration : .35,
198    /**
199     * @cfg {Boolean} animScroll True to animate tab scrolling so that hidden tabs slide smoothly into view (defaults
200     * to true).  Only applies when {@link #enableTabScroll} = true.
201     */
202    animScroll : true,
203    /**
204     * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to 'top').  The only
205     * other supported value is 'bottom'.  Note that tab scrolling is only supported for position 'top'.
206     */
207    tabPosition: 'top',
208    /**
209     * @cfg {String} baseCls The base CSS class applied to the panel (defaults to 'x-tab-panel').
210     */
211    baseCls: 'x-tab-panel',
212    /**
213     * @cfg {Boolean} autoTabs
214     * <p>True to query the DOM for any divs with a class of 'x-tab' to be automatically converted
215     * to tabs and added to this panel (defaults to false).  Note that the query will be executed within the scope of
216     * the container element only (so that multiple tab panels from markup can be supported via this method).</p>
217     * <p>This method is only possible when the markup is structured correctly as a container with nested
218     * divs containing the class 'x-tab'. To create TabPanels without these limitations, or to pull tab content from
219     * other elements on the page, see the example at the top of the class for generating tabs from markup.</p>
220     * <p>There are a couple of things to note when using this method:<ul>
221     * <li>When using the autoTabs config (as opposed to passing individual tab configs in the TabPanel's
222     * {@link #items} collection), you must use {@link #applyTo} to correctly use the specified id as the tab container.
223     * The autoTabs method <em>replaces</em> existing content with the TabPanel components.</li>
224     * <li>Make sure that you set {@link #deferredRender} to false so that the content elements for each tab will be
225     * rendered into the TabPanel immediately upon page load, otherwise they will not be transformed until each tab
226     * is activated and will be visible outside the TabPanel.</li>
227     * </ul>Example usage:</p>
228     * <pre><code>
229var tabs = new Ext.TabPanel({
230    applyTo: 'my-tabs',
231    activeTab: 0,
232    deferredRender: false,
233    autoTabs: true
234});
235
236// This markup will be converted to a TabPanel from the code above
237&lt;div id="my-tabs">
238    &lt;div class="x-tab" title="Tab 1">A simple tab&lt;/div>
239    &lt;div class="x-tab" title="Tab 2">Another one&lt;/div>
240&lt;/div>
241</code></pre>
242     */
243    autoTabs : false,
244    /**
245     * @cfg {String} autoTabSelector The CSS selector used to search for tabs in existing markup when {@link #autoTabs}
246     * = true (defaults to 'div.x-tab').  This can be any valid selector supported by {@link Ext.DomQuery#select}.
247     * Note that the query will be executed within the scope of this tab panel only (so that multiple tab panels from
248     * markup can be supported on a page).
249     */
250    autoTabSelector:'div.x-tab',
251    /**
252     * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially
253     * activated on render (defaults to none).
254     */
255    activeTab : null,
256    /**
257     * @cfg {Number} tabMargin The number of pixels of space to calculate into the sizing and scrolling of tabs. If you
258     * change the margin in CSS, you will need to update this value so calculations are correct with either resizeTabs
259     * or scrolling tabs. (defaults to 2)
260     */
261    tabMargin : 2,
262    /**
263     * @cfg {Boolean} plain True to render the tab strip without a background container image (defaults to false).
264     */
265    plain: false,
266    /**
267     * @cfg {Number} wheelIncrement For scrolling tabs, the number of pixels to increment on mouse wheel scrolling (defaults to 20).
268     */
269    wheelIncrement : 20,
270
271    /*
272     * This is a protected property used when concatenating tab ids to the TabPanel id for internal uniqueness.
273     * It does not generally need to be changed, but can be if external code also uses an id scheme that can
274     * potentially clash with this one.
275     */
276    idDelimiter : '__',
277
278    // private
279    itemCls : 'x-tab-item',
280
281    // private config overrides
282    elements: 'body',
283    headerAsText: false,
284    frame: false,
285    hideBorders:true,
286
287    // private
288    initComponent : function(){
289        this.frame = false;
290        Ext.TabPanel.superclass.initComponent.call(this);
291        this.addEvents(
292            /**
293             * @event beforetabchange
294             * Fires before the active tab changes. Handlers can return false to cancel the tab change.
295             * @param {TabPanel} this
296             * @param {Panel} newTab The tab being activated
297             * @param {Panel} currentTab The current active tab
298             */
299            'beforetabchange',
300            /**
301             * @event tabchange
302             * Fires after the active tab has changed.
303             * @param {TabPanel} this
304             * @param {Panel} tab The new active tab
305             */
306            'tabchange',
307            /**
308             * @event contextmenu
309             * Relays the contextmenu event from a tab selector element in the tab strip.
310             * @param {TabPanel} this
311             * @param {Panel} tab The target tab
312             * @param {EventObject} e
313             */
314            'contextmenu'
315        );
316        this.setLayout(new Ext.layout.CardLayout({
317            deferredRender: this.deferredRender
318        }));
319        if(this.tabPosition == 'top'){
320            this.elements += ',header';
321            this.stripTarget = 'header';
322        }else {
323            this.elements += ',footer';
324            this.stripTarget = 'footer';
325        }
326        if(!this.stack){
327            this.stack = Ext.TabPanel.AccessStack();
328        }
329        this.initItems();
330    },
331
332    // private
333    render : function(){
334        Ext.TabPanel.superclass.render.apply(this, arguments);
335        if(this.activeTab !== undefined){
336            var item = this.activeTab;
337            delete this.activeTab;
338            this.setActiveTab(item);
339        }
340    },
341
342    // private
343    onRender : function(ct, position){
344        Ext.TabPanel.superclass.onRender.call(this, ct, position);
345
346        if(this.plain){
347            var pos = this.tabPosition == 'top' ? 'header' : 'footer';
348            this[pos].addClass('x-tab-panel-'+pos+'-plain');
349        }
350
351        var st = this[this.stripTarget];
352
353        this.stripWrap = st.createChild({cls:'x-tab-strip-wrap', cn:{
354            tag:'ul', cls:'x-tab-strip x-tab-strip-'+this.tabPosition}});
355
356        var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
357        this.stripSpacer = st.createChild({cls:'x-tab-strip-spacer'}, beforeEl);
358        this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
359
360        this.edge = this.strip.createChild({tag:'li', cls:'x-tab-edge'});
361        this.strip.createChild({cls:'x-clear'});
362
363        this.body.addClass('x-tab-panel-body-'+this.tabPosition);
364
365        if(!this.itemTpl){
366            var tt = new Ext.Template(
367                 '<li class="{cls}" id="{id}"><a class="x-tab-strip-close" onclick="return false;"></a>',
368                 '<a class="x-tab-right" href="#" onclick="return false;"><em class="x-tab-left">',
369                 '<span class="x-tab-strip-inner"><span class="x-tab-strip-text {iconCls}">{text}</span></span>',
370                 '</em></a></li>'
371            );
372            tt.disableFormats = true;
373            tt.compile();
374            Ext.TabPanel.prototype.itemTpl = tt;
375        }
376
377        this.items.each(this.initTab, this);
378    },
379
380    // private
381    afterRender : function(){
382        Ext.TabPanel.superclass.afterRender.call(this);
383        if(this.autoTabs){
384            this.readTabs(false);
385        }
386    },
387
388    // private
389    initEvents : function(){
390        Ext.TabPanel.superclass.initEvents.call(this);
391        this.on('add', this.onAdd, this);
392        this.on('remove', this.onRemove, this);
393
394        this.strip.on('mousedown', this.onStripMouseDown, this);
395        this.strip.on('contextmenu', this.onStripContextMenu, this);
396        if(this.enableTabScroll){
397            this.strip.on('mousewheel', this.onWheel, this);
398        }
399    },
400
401    // private
402    findTargets : function(e){
403        var item = null;
404        var itemEl = e.getTarget('li', this.strip);
405        if(itemEl){
406            item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
407            if(item.disabled){
408                return {
409                    close : null,
410                    item : null,
411                    el : null
412                };
413            }
414        }
415        return {
416            close : e.getTarget('.x-tab-strip-close', this.strip),
417            item : item,
418            el : itemEl
419        };
420    },
421
422    // private
423    onStripMouseDown : function(e){
424        if(e.button != 0){
425            return;
426        }
427        e.preventDefault();
428        var t = this.findTargets(e);
429        if(t.close){
430            this.remove(t.item);
431            return;
432        }
433        if(t.item && t.item != this.activeTab){
434            this.setActiveTab(t.item);
435        }
436    },
437
438    // private
439    onStripContextMenu : function(e){
440        e.preventDefault();
441        var t = this.findTargets(e);
442        if(t.item){
443            this.fireEvent('contextmenu', this, t.item, e);
444        }
445    },
446
447    /**
448     * True to scan the markup in this tab panel for autoTabs using the autoTabSelector
449     * @param {Boolean} removeExisting True to remove existing tabs
450     */
451    readTabs : function(removeExisting){
452        if(removeExisting === true){
453            this.items.each(function(item){
454                this.remove(item);
455            }, this);
456        }
457        var tabs = this.el.query(this.autoTabSelector);
458        for(var i = 0, len = tabs.length; i < len; i++){
459            var tab = tabs[i];
460            var title = tab.getAttribute('title');
461            tab.removeAttribute('title');
462            this.add({
463                title: title,
464                el: tab
465            });
466        }
467    },
468
469    // private
470    initTab : function(item, index){
471        var before = this.strip.dom.childNodes[index];
472        var cls = item.closable ? 'x-tab-strip-closable' : '';
473        if(item.disabled){
474            cls += ' x-item-disabled';
475        }
476        if(item.iconCls){
477            cls += ' x-tab-with-icon';
478        }
479        if(item.tabCls){
480            cls += ' ' + item.tabCls;
481        }
482
483        var p = {
484            id: this.id + this.idDelimiter + item.getItemId(),
485            text: item.title,
486            cls: cls,
487            iconCls: item.iconCls || ''
488        };
489        var el = before ?
490                 this.itemTpl.insertBefore(before, p) :
491                 this.itemTpl.append(this.strip, p);
492
493        Ext.fly(el).addClassOnOver('x-tab-strip-over');
494
495        if(item.tabTip){
496            Ext.fly(el).child('span.x-tab-strip-text', true).qtip = item.tabTip;
497        }
498        item.tabEl = el;
499
500        item.on('disable', this.onItemDisabled, this);
501        item.on('enable', this.onItemEnabled, this);
502        item.on('titlechange', this.onItemTitleChanged, this);
503        item.on('iconchange', this.onItemIconChanged, this);
504        item.on('beforeshow', this.onBeforeShowItem, this);
505    },
506
507    // private
508    onAdd : function(tp, item, index){
509        this.initTab(item, index);
510        if(this.items.getCount() == 1){
511            this.syncSize();
512        }
513        this.delegateUpdates();
514    },
515
516    // private
517    onBeforeAdd : function(item){
518        var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
519        if(existing){
520            this.setActiveTab(item);
521            return false;
522        }
523        Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
524        var es = item.elements;
525        item.elements = es ? es.replace(',header', '') : es;
526        item.border = (item.border === true);
527    },
528
529    // private
530    onRemove : function(tp, item){
531        Ext.destroy(Ext.get(this.getTabEl(item)));
532        this.stack.remove(item);
533        item.un('disable', this.onItemDisabled, this);
534        item.un('enable', this.onItemEnabled, this);
535        item.un('titlechange', this.onItemTitleChanged, this);
536        item.un('iconchange', this.onItemIconChanged, this);
537        item.un('beforeshow', this.onBeforeShowItem, this);
538        if(item == this.activeTab){
539            var next = this.stack.next();
540            if(next){
541                this.setActiveTab(next);
542            }else if(this.items.getCount() > 0){
543                this.setActiveTab(0);
544            }else{
545                this.activeTab = null;
546            }
547        }
548        this.delegateUpdates();
549    },
550
551    // private
552    onBeforeShowItem : function(item){
553        if(item != this.activeTab){
554            this.setActiveTab(item);
555            return false;
556        }
557    },
558
559    // private
560    onItemDisabled : function(item){
561        var el = this.getTabEl(item);
562        if(el){
563            Ext.fly(el).addClass('x-item-disabled');
564        }
565        this.stack.remove(item);
566    },
567
568    // private
569    onItemEnabled : function(item){
570        var el = this.getTabEl(item);
571        if(el){
572            Ext.fly(el).removeClass('x-item-disabled');
573        }
574    },
575
576    // private
577    onItemTitleChanged : function(item){
578        var el = this.getTabEl(item);
579        if(el){
580            Ext.fly(el).child('span.x-tab-strip-text', true).innerHTML = item.title;
581        }
582    },
583   
584    //private
585    onItemIconChanged: function(item, iconCls, oldCls){
586        var el = this.getTabEl(item);
587        if(el){
588            Ext.fly(el).child('span.x-tab-strip-text').replaceClass(oldCls, iconCls);
589        }
590    },
591
592    /**
593     * Gets the DOM element for tab strip item which activates the
594     * child panel with the specified ID. Access this to change the visual treatment of the
595     * item, for example by changing the CSS class name.
596     * @param {Panel/Number} tab The tab component, or the tab's index
597     * @return {HTMLElement} The DOM node
598     */
599    getTabEl : function(item){
600        var itemId = (typeof item === 'number')?this.items.items[item].getItemId() : item.getItemId();
601        return document.getElementById(this.id+this.idDelimiter+itemId);
602    },
603
604    // private
605    onResize : function(){
606        Ext.TabPanel.superclass.onResize.apply(this, arguments);
607        this.delegateUpdates();
608    },
609
610    /**
611     * Suspends any internal calculations or scrolling while doing a bulk operation. See {@link #endUpdate}
612     */
613    beginUpdate : function(){
614        this.suspendUpdates = true;
615    },
616
617    /**
618     * Resumes calculations and scrolling at the end of a bulk operation. See {@link #beginUpdate}
619     */
620    endUpdate : function(){
621        this.suspendUpdates = false;
622        this.delegateUpdates();
623    },
624
625    /**
626     * Hides the tab strip item for the passed tab
627     * @param {Number/String/Panel} item The tab index, id or item
628     */
629    hideTabStripItem : function(item){
630        item = this.getComponent(item);
631        var el = this.getTabEl(item);
632        if(el){
633            el.style.display = 'none';
634            this.delegateUpdates();
635        }
636        this.stack.remove(item);
637    },
638
639    /**
640     * Unhides the tab strip item for the passed tab
641     * @param {Number/String/Panel} item The tab index, id or item
642     */
643    unhideTabStripItem : function(item){
644        item = this.getComponent(item);
645        var el = this.getTabEl(item);
646        if(el){
647            el.style.display = '';
648            this.delegateUpdates();
649        }
650    },
651
652    // private
653    delegateUpdates : function(){
654        if(this.suspendUpdates){
655            return;
656        }
657        if(this.resizeTabs && this.rendered){
658            this.autoSizeTabs();
659        }
660        if(this.enableTabScroll && this.rendered){
661            this.autoScrollTabs();
662        }
663    },
664
665    // private
666    autoSizeTabs : function(){
667        var count = this.items.length;
668        var ce = this.tabPosition != 'bottom' ? 'header' : 'footer';
669        var ow = this[ce].dom.offsetWidth;
670        var aw = this[ce].dom.clientWidth;
671
672        if(!this.resizeTabs || count < 1 || !aw){ // !aw for display:none
673            return;
674        }
675
676        var each = Math.max(Math.min(Math.floor((aw-4) / count) - this.tabMargin, this.tabWidth), this.minTabWidth); // -4 for float errors in IE
677        this.lastTabWidth = each;
678        var lis = this.stripWrap.dom.getElementsByTagName('li');
679        for(var i = 0, len = lis.length-1; i < len; i++) { // -1 for the "edge" li
680            var li = lis[i];
681            var inner = li.childNodes[1].firstChild.firstChild;
682            var tw = li.offsetWidth;
683            var iw = inner.offsetWidth;
684            inner.style.width = (each - (tw-iw)) + 'px';
685        }
686    },
687
688    // private
689    adjustBodyWidth : function(w){
690        if(this.header){
691            this.header.setWidth(w);
692        }
693        if(this.footer){
694            this.footer.setWidth(w);
695        }
696        return w;
697    },
698
699    /**
700     * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
701     * can return false to cancel the tab change.
702     * @param {String/Panel} tab The id or tab Panel to activate
703     */
704    setActiveTab : function(item){
705        item = this.getComponent(item);
706        if(!item || this.fireEvent('beforetabchange', this, item, this.activeTab) === false){
707            return;
708        }
709        if(!this.rendered){
710            this.activeTab = item;
711            return;
712        }
713        if(this.activeTab != item){
714            if(this.activeTab){
715                var oldEl = this.getTabEl(this.activeTab);
716                if(oldEl){
717                    Ext.fly(oldEl).removeClass('x-tab-strip-active');
718                }
719                this.activeTab.fireEvent('deactivate', this.activeTab);
720            }
721            var el = this.getTabEl(item);
722            Ext.fly(el).addClass('x-tab-strip-active');
723            this.activeTab = item;
724            this.stack.add(item);
725
726            this.layout.setActiveItem(item);
727            if(this.layoutOnTabChange && item.doLayout){
728                item.doLayout();
729            }
730            if(this.scrolling){
731                this.scrollToTab(item, this.animScroll);
732            }
733
734            item.fireEvent('activate', item);
735            this.fireEvent('tabchange', this, item);
736        }
737    },
738
739    /**
740     * Gets the currently active tab.
741     * @return {Panel} The active tab
742     */
743    getActiveTab : function(){
744        return this.activeTab || null;
745    },
746
747    /**
748     * Gets the specified tab by id.
749     * @param {String} id The tab id
750     * @return {Panel} The tab
751     */
752    getItem : function(item){
753        return this.getComponent(item);
754    },
755
756    // private
757    autoScrollTabs : function(){
758        this.pos = this.tabPosition=='bottom' ? this.footer : this.header;
759        var count = this.items.length;
760        var ow = this.pos.dom.offsetWidth;
761        var tw = this.pos.dom.clientWidth;
762
763        var wrap = this.stripWrap;
764        var wd = wrap.dom;
765        var cw = wd.offsetWidth;
766        var pos = this.getScrollPos();
767        var l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos;
768
769        if(!this.enableTabScroll || count < 1 || cw < 20){ // 20 to prevent display:none issues
770            return;
771        }
772        if(l <= tw){
773            wd.scrollLeft = 0;
774            wrap.setWidth(tw);
775            if(this.scrolling){
776                this.scrolling = false;
777                this.pos.removeClass('x-tab-scrolling');
778                this.scrollLeft.hide();
779                this.scrollRight.hide();
780                if(Ext.isAir || Ext.isSafari){
781                    wd.style.marginLeft = '';
782                    wd.style.marginRight = '';
783                }
784            }
785        }else{
786            if(!this.scrolling){
787                this.pos.addClass('x-tab-scrolling');
788                if(Ext.isAir || Ext.isSafari){
789                    wd.style.marginLeft = '18px';
790                    wd.style.marginRight = '18px';
791                }
792            }
793            tw -= wrap.getMargins('lr');
794            wrap.setWidth(tw > 20 ? tw : 20);
795            if(!this.scrolling){
796                if(!this.scrollLeft){
797                    this.createScrollers();
798                }else{
799                    this.scrollLeft.show();
800                    this.scrollRight.show();
801                }
802            }
803            this.scrolling = true;
804            if(pos > (l-tw)){ // ensure it stays within bounds
805                wd.scrollLeft = l-tw;
806            }else{ // otherwise, make sure the active tab is still visible
807                this.scrollToTab(this.activeTab, false);
808            }
809            this.updateScrollButtons();
810        }
811    },
812
813    // private
814    createScrollers : function(){
815        this.pos.addClass('x-tab-scrolling-' + this.tabPosition);
816        var h = this.stripWrap.dom.offsetHeight;
817
818        // left
819        var sl = this.pos.insertFirst({
820            cls:'x-tab-scroller-left'
821        });
822        sl.setHeight(h);
823        sl.addClassOnOver('x-tab-scroller-left-over');
824        this.leftRepeater = new Ext.util.ClickRepeater(sl, {
825            interval : this.scrollRepeatInterval,
826            handler: this.onScrollLeft,
827            scope: this
828        });
829        this.scrollLeft = sl;
830
831        // right
832        var sr = this.pos.insertFirst({
833            cls:'x-tab-scroller-right'
834        });
835        sr.setHeight(h);
836        sr.addClassOnOver('x-tab-scroller-right-over');
837        this.rightRepeater = new Ext.util.ClickRepeater(sr, {
838            interval : this.scrollRepeatInterval,
839            handler: this.onScrollRight,
840            scope: this
841        });
842        this.scrollRight = sr;
843    },
844
845    // private
846    getScrollWidth : function(){
847        return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos();
848    },
849
850    // private
851    getScrollPos : function(){
852        return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0;
853    },
854
855    // private
856    getScrollArea : function(){
857        return parseInt(this.stripWrap.dom.clientWidth, 10) || 0;
858    },
859
860    // private
861    getScrollAnim : function(){
862        return {duration:this.scrollDuration, callback: this.updateScrollButtons, scope: this};
863    },
864
865    // private
866    getScrollIncrement : function(){
867        return this.scrollIncrement || (this.resizeTabs ? this.lastTabWidth+2 : 100);
868    },
869
870    /**
871     * Scrolls to a particular tab if tab scrolling is enabled
872     * @param {Panel} item The item to scroll to
873     * @param {Boolean} animate True to enable animations
874     */
875
876    scrollToTab : function(item, animate){
877        if(!item){ return; }
878        var el = this.getTabEl(item);
879        var pos = this.getScrollPos(), area = this.getScrollArea();
880        var left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos;
881        var right = left + el.offsetWidth;
882        if(left < pos){
883            this.scrollTo(left, animate);
884        }else if(right > (pos + area)){
885            this.scrollTo(right - area, animate);
886        }
887    },
888
889    // private
890    scrollTo : function(pos, animate){
891        this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false);
892        if(!animate){
893            this.updateScrollButtons();
894        }
895    },
896
897    onWheel : function(e){
898        var d = e.getWheelDelta()*this.wheelIncrement*-1;
899        e.stopEvent();
900
901        var pos = this.getScrollPos();
902        var newpos = pos + d;
903        var sw = this.getScrollWidth()-this.getScrollArea();
904
905        var s = Math.max(0, Math.min(sw, newpos));
906        if(s != pos){
907            this.scrollTo(s, false);
908        }
909    },
910
911    // private
912    onScrollRight : function(){
913        var sw = this.getScrollWidth()-this.getScrollArea();
914        var pos = this.getScrollPos();
915        var s = Math.min(sw, pos + this.getScrollIncrement());
916        if(s != pos){
917            this.scrollTo(s, this.animScroll);
918        }
919    },
920
921    // private
922    onScrollLeft : function(){
923        var pos = this.getScrollPos();
924        var s = Math.max(0, pos - this.getScrollIncrement());
925        if(s != pos){
926            this.scrollTo(s, this.animScroll);
927        }
928    },
929
930    // private
931    updateScrollButtons : function(){
932        var pos = this.getScrollPos();
933        this.scrollLeft[pos == 0 ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
934        this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
935    },
936
937    // private
938    beforeDestroy : function() {
939        if(this.items){
940            this.items.each(function(item){
941                if(item && item.tabEl){
942                    Ext.get(item.tabEl).removeAllListeners();
943                    item.tabEl = null;
944                }
945            }, this);
946        }
947        if(this.strip){
948            this.strip.removeAllListeners();
949        }
950        Ext.TabPanel.superclass.beforeDestroy.apply(this);
951    }
952
953    /**
954     * @cfg {Boolean} collapsible
955     * @hide
956     */
957    /**
958     * @cfg {String} header
959     * @hide
960     */
961    /**
962     * @cfg {Boolean} headerAsText
963     * @hide
964     */
965    /**
966     * @property header
967     * @hide
968     */
969    /**
970     * @property title
971     * @hide
972     */
973    /**
974     * @cfg {Array} tools
975     * @hide
976     */
977    /**
978     * @cfg {Boolean} hideCollapseTool
979     * @hide
980     */
981    /**
982     * @cfg {Boolean} titleCollapse
983     * @hide
984     */
985    /**
986     * @cfg {Boolean} collapsed
987     * @hide
988     */
989    /**
990     * @cfg {String} layout
991     * @hide
992     */
993    /**
994     * @cfg {Object} layoutConfig
995     * @hide
996     */
997
998});
999Ext.reg('tabpanel', Ext.TabPanel);
1000
1001/**
1002 * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
1003 * can return false to cancel the tab change.
1004 * @param {String/Panel} tab The id or tab Panel to activate
1005 * @method activate
1006 */
1007Ext.TabPanel.prototype.activate = Ext.TabPanel.prototype.setActiveTab;
1008
1009// private utility class used by TabPanel
1010Ext.TabPanel.AccessStack = function(){
1011    var items = [];
1012    return {
1013        add : function(item){
1014            items.push(item);
1015            if(items.length > 10){
1016                items.shift();
1017            }
1018        },
1019
1020        remove : function(item){
1021            var s = [];
1022            for(var i = 0, len = items.length; i < len; i++) {
1023                if(items[i] != item){
1024                    s.push(items[i]);
1025                }
1026            }
1027            items = s;
1028        },
1029
1030        next : function(){
1031            return items.pop();
1032        }
1033    };
1034};
1035
Note: See TracBrowser for help on using the repository browser.