~mortenoh/+junk/dhis2-detailed-import-export

« back to all changes in this revision

Viewing changes to gis/dhis-gis-geostat/mfbase/ext/source/widgets/TabPanel.js

  • Committer: larshelge at gmail
  • Date: 2009-03-03 16:46:36 UTC
  • Revision ID: larshelge@gmail.com-20090303164636-2sjlrquo7ib1gf7r
Initial check-in

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Ext JS Library 2.2
 
3
 * Copyright(c) 2006-2008, 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>
 
34
Ext.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>
 
101
var 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>
 
122
var 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
 */
 
140
Ext.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 {Boolean} monitorResize True to automatically monitor window resize events and rerender the layout on
 
146
     * browser resize (defaults to true).
 
147
     */
 
148
    monitorResize : true,
 
149
    /**
 
150
     * @cfg {Boolean} deferredRender Internally, the TabPanel uses a {@link Ext.layout.CardLayout} to manage its tabs.
 
151
     * This property will be passed on to the layout as its {@link Ext.layout.CardLayout#deferredRender} config value,
 
152
     * determining whether or not each tab is rendered only when first accessed (defaults to true).
 
153
     */
 
154
    deferredRender : true,
 
155
    /**
 
156
     * @cfg {Number} tabWidth The initial width in pixels of each new tab (defaults to 120).
 
157
     */
 
158
    tabWidth: 120,
 
159
    /**
 
160
     * @cfg {Number} minTabWidth The minimum width in pixels for each tab when {@link #resizeTabs} = true (defaults to 30).
 
161
     */
 
162
    minTabWidth: 30,
 
163
    /**
 
164
     * @cfg {Boolean} resizeTabs True to automatically resize each tab so that the tabs will completely fill the
 
165
     * tab strip (defaults to false).  Setting this to true may cause specific widths that might be set per tab to
 
166
     * be overridden in order to fit them all into view (although {@link #minTabWidth} will always be honored).
 
167
     */
 
168
    resizeTabs:false,
 
169
    /**
 
170
     * @cfg {Boolean} enableTabScroll True to enable scrolling to tabs that may be invisible due to overflowing the
 
171
     * overall TabPanel width. Only available with tabPosition:'top' (defaults to false).
 
172
     */
 
173
    enableTabScroll: false,
 
174
    /**
 
175
     * @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed (defaults
 
176
     * to 100, or if {@link #resizeTabs} = true, the calculated tab width).  Only applies when {@link #enableTabScroll} = true.
 
177
     */
 
178
    scrollIncrement : 0,
 
179
    /**
 
180
     * @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a tab scroll button is
 
181
     * continuously pressed (defaults to 400).
 
182
     */
 
183
    scrollRepeatInterval : 400,
 
184
    /**
 
185
     * @cfg {Float} scrollDuration The number of milliseconds that each scroll animation should last (defaults to .35).
 
186
     * Only applies when {@link #animScroll} = true.
 
187
     */
 
188
    scrollDuration : .35,
 
189
    /**
 
190
     * @cfg {Boolean} animScroll True to animate tab scrolling so that hidden tabs slide smoothly into view (defaults
 
191
     * to true).  Only applies when {@link #enableTabScroll} = true.
 
192
     */
 
193
    animScroll : true,
 
194
    /**
 
195
     * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to 'top').  The only
 
196
     * other supported value is 'bottom'.  Note that tab scrolling is only supported for position 'top'.
 
197
     */
 
198
    tabPosition: 'top',
 
199
    /**
 
200
     * @cfg {String} baseCls The base CSS class applied to the panel (defaults to 'x-tab-panel').
 
201
     */
 
202
    baseCls: 'x-tab-panel',
 
203
    /**
 
204
     * @cfg {Boolean} autoTabs
 
205
     * <p>True to query the DOM for any divs with a class of 'x-tab' to be automatically converted
 
206
     * to tabs and added to this panel (defaults to false).  Note that the query will be executed within the scope of
 
207
     * the container element only (so that multiple tab panels from markup can be supported via this method).</p>
 
208
     * <p>This method is only possible when the markup is structured correctly as a container with nested
 
209
     * divs containing the class 'x-tab'. To create TabPanels without these limitations, or to pull tab content from
 
210
     * other elements on the page, see the example at the top of the class for generating tabs from markup.</p>
 
211
     * <p>There are a couple of things to note when using this method:<ul>
 
212
     * <li>When using the autoTabs config (as opposed to passing individual tab configs in the TabPanel's
 
213
     * {@link #items} collection), you must use {@link #applyTo} to correctly use the specified id as the tab container.
 
214
     * The autoTabs method <em>replaces</em> existing content with the TabPanel components.</li>
 
215
     * <li>Make sure that you set {@link #deferredRender} to false so that the content elements for each tab will be
 
216
     * rendered into the TabPanel immediately upon page load, otherwise they will not be transformed until each tab
 
217
     * is activated and will be visible outside the TabPanel.</li>
 
218
     * </ul>Example usage:</p>
 
219
     * <pre><code>
 
220
var tabs = new Ext.TabPanel({
 
221
    applyTo: 'my-tabs',
 
222
    activeTab: 0,
 
223
    deferredRender: false,
 
224
    autoTabs: true
 
225
});
 
226
 
 
227
// This markup will be converted to a TabPanel from the code above
 
228
&lt;div id="my-tabs">
 
229
    &lt;div class="x-tab" title="Tab 1">A simple tab&lt;/div>
 
230
    &lt;div class="x-tab" title="Tab 2">Another one&lt;/div>
 
231
&lt;/div>
 
232
</code></pre>
 
233
     */
 
234
    autoTabs : false,
 
235
    /**
 
236
     * @cfg {String} autoTabSelector The CSS selector used to search for tabs in existing markup when {@link #autoTabs}
 
237
     * = true (defaults to 'div.x-tab').  This can be any valid selector supported by {@link Ext.DomQuery#select}.
 
238
     * Note that the query will be executed within the scope of this tab panel only (so that multiple tab panels from
 
239
     * markup can be supported on a page).
 
240
     */
 
241
    autoTabSelector:'div.x-tab',
 
242
    /**
 
243
     * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially
 
244
     * activated on render (defaults to none).
 
245
     */
 
246
    activeTab : null,
 
247
    /**
 
248
     * @cfg {Number} tabMargin The number of pixels of space to calculate into the sizing and scrolling of tabs. If you
 
249
     * change the margin in CSS, you will need to update this value so calculations are correct with either resizeTabs
 
250
     * or scrolling tabs. (defaults to 2)
 
251
     */
 
252
    tabMargin : 2,
 
253
    /**
 
254
     * @cfg {Boolean} plain True to render the tab strip without a background container image (defaults to false).
 
255
     */
 
256
    plain: false,
 
257
    /**
 
258
     * @cfg {Number} wheelIncrement For scrolling tabs, the number of pixels to increment on mouse wheel scrolling (defaults to 20).
 
259
     */
 
260
    wheelIncrement : 20,
 
261
 
 
262
    /*
 
263
     * This is a protected property used when concatenating tab ids to the TabPanel id for internal uniqueness.
 
264
     * It does not generally need to be changed, but can be if external code also uses an id scheme that can
 
265
     * potentially clash with this one.
 
266
     */
 
267
    idDelimiter : '__',
 
268
 
 
269
    // private
 
270
    itemCls : 'x-tab-item',
 
271
 
 
272
    // private config overrides
 
273
    elements: 'body',
 
274
    headerAsText: false,
 
275
    frame: false,
 
276
    hideBorders:true,
 
277
 
 
278
    // private
 
279
    initComponent : function(){
 
280
        this.frame = false;
 
281
        Ext.TabPanel.superclass.initComponent.call(this);
 
282
        this.addEvents(
 
283
            /**
 
284
             * @event beforetabchange
 
285
             * Fires before the active tab changes. Handlers can return false to cancel the tab change.
 
286
             * @param {TabPanel} this
 
287
             * @param {Panel} newTab The tab being activated
 
288
             * @param {Panel} currentTab The current active tab
 
289
             */
 
290
            'beforetabchange',
 
291
            /**
 
292
             * @event tabchange
 
293
             * Fires after the active tab has changed.
 
294
             * @param {TabPanel} this
 
295
             * @param {Panel} tab The new active tab
 
296
             */
 
297
            'tabchange',
 
298
            /**
 
299
             * @event contextmenu
 
300
             * Fires when the original browser contextmenu event originated from a tab element.
 
301
             * @param {TabPanel} this
 
302
             * @param {Panel} tab The target tab
 
303
             * @param {EventObject} e
 
304
             */
 
305
            'contextmenu'
 
306
        );
 
307
        this.setLayout(new Ext.layout.CardLayout({
 
308
            deferredRender: this.deferredRender
 
309
        }));
 
310
        if(this.tabPosition == 'top'){
 
311
            this.elements += ',header';
 
312
            this.stripTarget = 'header';
 
313
        }else {
 
314
            this.elements += ',footer';
 
315
            this.stripTarget = 'footer';
 
316
        }
 
317
        if(!this.stack){
 
318
            this.stack = Ext.TabPanel.AccessStack();
 
319
        }
 
320
        this.initItems();
 
321
    },
 
322
 
 
323
    // private
 
324
    render : function(){
 
325
        Ext.TabPanel.superclass.render.apply(this, arguments);
 
326
        if(this.activeTab !== undefined){
 
327
            var item = this.activeTab;
 
328
            delete this.activeTab;
 
329
            this.setActiveTab(item);
 
330
        }
 
331
    },
 
332
 
 
333
    // private
 
334
    onRender : function(ct, position){
 
335
        Ext.TabPanel.superclass.onRender.call(this, ct, position);
 
336
 
 
337
        if(this.plain){
 
338
            var pos = this.tabPosition == 'top' ? 'header' : 'footer';
 
339
            this[pos].addClass('x-tab-panel-'+pos+'-plain');
 
340
        }
 
341
 
 
342
        var st = this[this.stripTarget];
 
343
 
 
344
        this.stripWrap = st.createChild({cls:'x-tab-strip-wrap', cn:{
 
345
            tag:'ul', cls:'x-tab-strip x-tab-strip-'+this.tabPosition}});
 
346
 
 
347
        var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
 
348
        this.stripSpacer = st.createChild({cls:'x-tab-strip-spacer'}, beforeEl);
 
349
        this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
 
350
        
 
351
        this.edge = this.strip.createChild({tag:'li', cls:'x-tab-edge'});
 
352
        this.strip.createChild({cls:'x-clear'});
 
353
 
 
354
        this.body.addClass('x-tab-panel-body-'+this.tabPosition);
 
355
 
 
356
        if(!this.itemTpl){
 
357
            var tt = new Ext.Template(
 
358
                 '<li class="{cls}" id="{id}"><a class="x-tab-strip-close" onclick="return false;"></a>',
 
359
                 '<a class="x-tab-right" href="#" onclick="return false;"><em class="x-tab-left">',
 
360
                 '<span class="x-tab-strip-inner"><span class="x-tab-strip-text {iconCls}">{text}</span></span>',
 
361
                 '</em></a></li>'
 
362
            );
 
363
            tt.disableFormats = true;
 
364
            tt.compile();
 
365
            Ext.TabPanel.prototype.itemTpl = tt;
 
366
        }
 
367
 
 
368
        this.items.each(this.initTab, this);
 
369
    },
 
370
 
 
371
    // private
 
372
    afterRender : function(){
 
373
        Ext.TabPanel.superclass.afterRender.call(this);
 
374
        if(this.autoTabs){
 
375
            this.readTabs(false);
 
376
        }
 
377
    },
 
378
 
 
379
    // private
 
380
    initEvents : function(){
 
381
        Ext.TabPanel.superclass.initEvents.call(this);
 
382
        this.on('add', this.onAdd, this);
 
383
        this.on('remove', this.onRemove, this);
 
384
 
 
385
        this.strip.on('mousedown', this.onStripMouseDown, this);
 
386
        this.strip.on('contextmenu', this.onStripContextMenu, this);
 
387
        if(this.enableTabScroll){
 
388
            this.strip.on('mousewheel', this.onWheel, this);
 
389
        }
 
390
    },
 
391
 
 
392
    // private
 
393
    findTargets : function(e){
 
394
        var item = null;
 
395
        var itemEl = e.getTarget('li', this.strip);
 
396
        if(itemEl){
 
397
            item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
 
398
            if(item.disabled){
 
399
                return {
 
400
                    close : null,
 
401
                    item : null,
 
402
                    el : null
 
403
                };
 
404
            }
 
405
        }
 
406
        return {
 
407
            close : e.getTarget('.x-tab-strip-close', this.strip),
 
408
            item : item,
 
409
            el : itemEl
 
410
        };
 
411
    },
 
412
 
 
413
    // private
 
414
    onStripMouseDown : function(e){
 
415
        if(e.button != 0){
 
416
            return;
 
417
        }
 
418
        e.preventDefault();
 
419
        var t = this.findTargets(e);
 
420
        if(t.close){
 
421
            this.remove(t.item);
 
422
            return;
 
423
        }
 
424
        if(t.item && t.item != this.activeTab){
 
425
            this.setActiveTab(t.item);
 
426
        }
 
427
    },
 
428
 
 
429
    // private
 
430
    onStripContextMenu : function(e){
 
431
        e.preventDefault();
 
432
        var t = this.findTargets(e);
 
433
        if(t.item){
 
434
            this.fireEvent('contextmenu', this, t.item, e);
 
435
        }
 
436
    },
 
437
 
 
438
    /**
 
439
     * True to scan the markup in this tab panel for autoTabs using the autoTabSelector
 
440
     * @param {Boolean} removeExisting True to remove existing tabs
 
441
     */
 
442
    readTabs : function(removeExisting){
 
443
        if(removeExisting === true){
 
444
            this.items.each(function(item){
 
445
                this.remove(item);
 
446
            }, this);
 
447
        }
 
448
        var tabs = this.el.query(this.autoTabSelector);
 
449
        for(var i = 0, len = tabs.length; i < len; i++){
 
450
            var tab = tabs[i];
 
451
            var title = tab.getAttribute('title');
 
452
            tab.removeAttribute('title');
 
453
            this.add({
 
454
                title: title,
 
455
                el: tab
 
456
            });
 
457
        }
 
458
    },
 
459
 
 
460
    // private
 
461
    initTab : function(item, index){
 
462
        var before = this.strip.dom.childNodes[index];
 
463
        var cls = item.closable ? 'x-tab-strip-closable' : '';
 
464
        if(item.disabled){
 
465
            cls += ' x-item-disabled';
 
466
        }
 
467
        if(item.iconCls){
 
468
            cls += ' x-tab-with-icon';
 
469
        }
 
470
        if(item.tabCls){
 
471
            cls += ' ' + item.tabCls;
 
472
        }
 
473
        
 
474
        var p = {
 
475
            id: this.id + this.idDelimiter + item.getItemId(),
 
476
            text: item.title,
 
477
            cls: cls,
 
478
            iconCls: item.iconCls || ''
 
479
        };
 
480
        var el = before ?
 
481
                 this.itemTpl.insertBefore(before, p) :
 
482
                 this.itemTpl.append(this.strip, p);
 
483
 
 
484
        Ext.fly(el).addClassOnOver('x-tab-strip-over');
 
485
 
 
486
        if(item.tabTip){
 
487
            Ext.fly(el).child('span.x-tab-strip-text', true).qtip = item.tabTip;
 
488
        }
 
489
        item.on('disable', this.onItemDisabled, this);
 
490
        item.on('enable', this.onItemEnabled, this);
 
491
        item.on('titlechange', this.onItemTitleChanged, this);
 
492
        item.on('beforeshow', this.onBeforeShowItem, this);
 
493
    },
 
494
 
 
495
    // private
 
496
    onAdd : function(tp, item, index){
 
497
        this.initTab(item, index);
 
498
        if(this.items.getCount() == 1){
 
499
            this.syncSize();
 
500
        }
 
501
        this.delegateUpdates();
 
502
    },
 
503
 
 
504
    // private
 
505
    onBeforeAdd : function(item){
 
506
        var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
 
507
        if(existing){
 
508
            this.setActiveTab(item);
 
509
            return false;
 
510
        }
 
511
        Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
 
512
        var es = item.elements;
 
513
        item.elements = es ? es.replace(',header', '') : es;
 
514
        item.border = (item.border === true);
 
515
    },
 
516
 
 
517
    // private
 
518
    onRemove : function(tp, item){
 
519
        Ext.removeNode(this.getTabEl(item));
 
520
        this.stack.remove(item);
 
521
        item.un('disable', this.onItemDisabled, this);
 
522
        item.un('enable', this.onItemEnabled, this);
 
523
        item.un('titlechange', this.onItemTitleChanged, this);
 
524
        item.un('beforeshow', this.onBeforeShowItem, this);
 
525
        if(item == this.activeTab){
 
526
            var next = this.stack.next();
 
527
            if(next){
 
528
                this.setActiveTab(next);
 
529
            }else{
 
530
                this.setActiveTab(0);
 
531
            }
 
532
        }
 
533
        this.delegateUpdates();
 
534
    },
 
535
 
 
536
    // private
 
537
    onBeforeShowItem : function(item){
 
538
        if(item != this.activeTab){
 
539
            this.setActiveTab(item);
 
540
            return false;
 
541
        }
 
542
    },
 
543
 
 
544
    // private
 
545
    onItemDisabled : function(item){
 
546
        var el = this.getTabEl(item);
 
547
        if(el){
 
548
            Ext.fly(el).addClass('x-item-disabled');
 
549
        }
 
550
        this.stack.remove(item);
 
551
    },
 
552
 
 
553
    // private
 
554
    onItemEnabled : function(item){
 
555
        var el = this.getTabEl(item);
 
556
        if(el){
 
557
            Ext.fly(el).removeClass('x-item-disabled');
 
558
        }
 
559
    },
 
560
 
 
561
    // private
 
562
    onItemTitleChanged : function(item){
 
563
        var el = this.getTabEl(item);
 
564
        if(el){
 
565
            Ext.fly(el).child('span.x-tab-strip-text', true).innerHTML = item.title;
 
566
        }
 
567
    },
 
568
 
 
569
    /**
 
570
     * Gets the DOM element for tab strip item which activates the
 
571
     * child panel with the specified ID. Access this to change the visual treatment of the
 
572
     * item, for example by changing the CSS class name.
 
573
     * @param {Panel} tab The tab
 
574
     * @return {HTMLElement} The DOM node
 
575
     */
 
576
    getTabEl : function(item){
 
577
        var itemId = (typeof item === 'number')?this.items.items[item].getItemId() : item.getItemId();
 
578
        return document.getElementById(this.id+this.idDelimiter+itemId);
 
579
    },
 
580
 
 
581
    // private
 
582
    onResize : function(){
 
583
        Ext.TabPanel.superclass.onResize.apply(this, arguments);
 
584
        this.delegateUpdates();
 
585
    },
 
586
 
 
587
    /**
 
588
     * Suspends any internal calculations or scrolling while doing a bulk operation. See {@link #endUpdate}
 
589
     */
 
590
    beginUpdate : function(){
 
591
        this.suspendUpdates = true;
 
592
    },
 
593
 
 
594
    /**
 
595
     * Resumes calculations and scrolling at the end of a bulk operation. See {@link #beginUpdate}
 
596
     */
 
597
    endUpdate : function(){
 
598
        this.suspendUpdates = false;
 
599
        this.delegateUpdates();
 
600
    },
 
601
 
 
602
    /**
 
603
     * Hides the tab strip item for the passed tab
 
604
     * @param {Number/String/Panel} item The tab index, id or item
 
605
     */
 
606
    hideTabStripItem : function(item){
 
607
        item = this.getComponent(item);
 
608
        var el = this.getTabEl(item);
 
609
        if(el){
 
610
            el.style.display = 'none';
 
611
            this.delegateUpdates();
 
612
        }
 
613
        this.stack.remove(item);
 
614
    },
 
615
 
 
616
    /**
 
617
     * Unhides the tab strip item for the passed tab
 
618
     * @param {Number/String/Panel} item The tab index, id or item
 
619
     */
 
620
    unhideTabStripItem : function(item){
 
621
        item = this.getComponent(item);
 
622
        var el = this.getTabEl(item);
 
623
        if(el){
 
624
            el.style.display = '';
 
625
            this.delegateUpdates();
 
626
        }
 
627
    },
 
628
 
 
629
    // private
 
630
    delegateUpdates : function(){
 
631
        if(this.suspendUpdates){
 
632
            return;
 
633
        }
 
634
        if(this.resizeTabs && this.rendered){
 
635
            this.autoSizeTabs();
 
636
        }
 
637
        if(this.enableTabScroll && this.rendered){
 
638
            this.autoScrollTabs();
 
639
        }
 
640
    },
 
641
 
 
642
    // private
 
643
    autoSizeTabs : function(){
 
644
        var count = this.items.length;
 
645
        var ce = this.tabPosition != 'bottom' ? 'header' : 'footer';
 
646
        var ow = this[ce].dom.offsetWidth;
 
647
        var aw = this[ce].dom.clientWidth;
 
648
 
 
649
        if(!this.resizeTabs || count < 1 || !aw){ // !aw for display:none
 
650
            return;
 
651
        }
 
652
 
 
653
        var each = Math.max(Math.min(Math.floor((aw-4) / count) - this.tabMargin, this.tabWidth), this.minTabWidth); // -4 for float errors in IE
 
654
        this.lastTabWidth = each;
 
655
        var lis = this.stripWrap.dom.getElementsByTagName('li');
 
656
        for(var i = 0, len = lis.length-1; i < len; i++) { // -1 for the "edge" li
 
657
            var li = lis[i];
 
658
            var inner = li.childNodes[1].firstChild.firstChild;
 
659
            var tw = li.offsetWidth;
 
660
            var iw = inner.offsetWidth;
 
661
            inner.style.width = (each - (tw-iw)) + 'px';
 
662
        }
 
663
    },
 
664
 
 
665
    // private
 
666
    adjustBodyWidth : function(w){
 
667
        if(this.header){
 
668
            this.header.setWidth(w);
 
669
        }
 
670
        if(this.footer){
 
671
            this.footer.setWidth(w);
 
672
        }
 
673
        return w;
 
674
    },
 
675
 
 
676
    /**
 
677
     * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
 
678
     * can return false to cancel the tab change.
 
679
     * @param {String/Panel} tab The id or tab Panel to activate
 
680
     */
 
681
    setActiveTab : function(item){
 
682
        item = this.getComponent(item);
 
683
        if(!item || this.fireEvent('beforetabchange', this, item, this.activeTab) === false){
 
684
            return;
 
685
        }
 
686
        if(!this.rendered){
 
687
            this.activeTab = item;
 
688
            return;
 
689
        }
 
690
        if(this.activeTab != item){
 
691
            if(this.activeTab){
 
692
                var oldEl = this.getTabEl(this.activeTab);
 
693
                if(oldEl){
 
694
                    Ext.fly(oldEl).removeClass('x-tab-strip-active');
 
695
                }
 
696
                this.activeTab.fireEvent('deactivate', this.activeTab);
 
697
            }
 
698
            var el = this.getTabEl(item);
 
699
            Ext.fly(el).addClass('x-tab-strip-active');
 
700
            this.activeTab = item;
 
701
            this.stack.add(item);
 
702
 
 
703
            this.layout.setActiveItem(item);
 
704
            if(this.layoutOnTabChange && item.doLayout){
 
705
                item.doLayout();
 
706
            }
 
707
            if(this.scrolling){
 
708
                this.scrollToTab(item, this.animScroll);
 
709
            }
 
710
 
 
711
            item.fireEvent('activate', item);
 
712
            this.fireEvent('tabchange', this, item);
 
713
        }
 
714
    },
 
715
 
 
716
    /**
 
717
     * Gets the currently active tab.
 
718
     * @return {Panel} The active tab
 
719
     */
 
720
    getActiveTab : function(){
 
721
        return this.activeTab || null;
 
722
    },
 
723
 
 
724
    /**
 
725
     * Gets the specified tab by id.
 
726
     * @param {String} id The tab id
 
727
     * @return {Panel} The tab
 
728
     */
 
729
    getItem : function(item){
 
730
        return this.getComponent(item);
 
731
    },
 
732
 
 
733
    // private
 
734
    autoScrollTabs : function(){
 
735
        var count = this.items.length;
 
736
        var ow = this.header.dom.offsetWidth;
 
737
        var tw = this.header.dom.clientWidth;
 
738
 
 
739
        var wrap = this.stripWrap;
 
740
        var wd = wrap.dom;
 
741
        var cw = wd.offsetWidth;
 
742
        var pos = this.getScrollPos();
 
743
        var l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos;
 
744
 
 
745
        if(!this.enableTabScroll || count < 1 || cw < 20){ // 20 to prevent display:none issues
 
746
            return;
 
747
        }
 
748
        if(l <= tw){
 
749
            wd.scrollLeft = 0;
 
750
            wrap.setWidth(tw);
 
751
            if(this.scrolling){
 
752
                this.scrolling = false;
 
753
                this.header.removeClass('x-tab-scrolling');
 
754
                this.scrollLeft.hide();
 
755
                this.scrollRight.hide();
 
756
                if(Ext.isAir){
 
757
                    wd.style.marginLeft = '';
 
758
                    wd.style.marginRight = '';
 
759
                }
 
760
            }
 
761
        }else{
 
762
            if(!this.scrolling){
 
763
                this.header.addClass('x-tab-scrolling');
 
764
                if(Ext.isAir){
 
765
                    wd.style.marginLeft = '18px';
 
766
                    wd.style.marginRight = '18px';
 
767
                }
 
768
            }
 
769
            tw -= wrap.getMargins('lr');
 
770
            wrap.setWidth(tw > 20 ? tw : 20);
 
771
            if(!this.scrolling){
 
772
                if(!this.scrollLeft){
 
773
                    this.createScrollers();
 
774
                }else{
 
775
                    this.scrollLeft.show();
 
776
                    this.scrollRight.show();
 
777
                }
 
778
            }
 
779
            this.scrolling = true;
 
780
            if(pos > (l-tw)){ // ensure it stays within bounds
 
781
                wd.scrollLeft = l-tw;
 
782
            }else{ // otherwise, make sure the active tab is still visible
 
783
                this.scrollToTab(this.activeTab, false);
 
784
            }
 
785
            this.updateScrollButtons();
 
786
        }
 
787
    },
 
788
 
 
789
    // private
 
790
    createScrollers : function(){
 
791
        var h = this.stripWrap.dom.offsetHeight;
 
792
 
 
793
        // left
 
794
        var sl = this.header.insertFirst({
 
795
            cls:'x-tab-scroller-left'
 
796
        });
 
797
        sl.setHeight(h);
 
798
        sl.addClassOnOver('x-tab-scroller-left-over');
 
799
        this.leftRepeater = new Ext.util.ClickRepeater(sl, {
 
800
            interval : this.scrollRepeatInterval,
 
801
            handler: this.onScrollLeft,
 
802
            scope: this
 
803
        });
 
804
        this.scrollLeft = sl;
 
805
 
 
806
        // right
 
807
        var sr = this.header.insertFirst({
 
808
            cls:'x-tab-scroller-right'
 
809
        });
 
810
        sr.setHeight(h);
 
811
        sr.addClassOnOver('x-tab-scroller-right-over');
 
812
        this.rightRepeater = new Ext.util.ClickRepeater(sr, {
 
813
            interval : this.scrollRepeatInterval,
 
814
            handler: this.onScrollRight,
 
815
            scope: this
 
816
        });
 
817
        this.scrollRight = sr;
 
818
    },
 
819
 
 
820
    // private
 
821
    getScrollWidth : function(){
 
822
        return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos();
 
823
    },
 
824
 
 
825
    // private
 
826
    getScrollPos : function(){
 
827
        return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0;
 
828
    },
 
829
 
 
830
    // private
 
831
    getScrollArea : function(){
 
832
        return parseInt(this.stripWrap.dom.clientWidth, 10) || 0;
 
833
    },
 
834
 
 
835
    // private
 
836
    getScrollAnim : function(){
 
837
        return {duration:this.scrollDuration, callback: this.updateScrollButtons, scope: this};
 
838
    },
 
839
 
 
840
    // private
 
841
    getScrollIncrement : function(){
 
842
        return this.scrollIncrement || (this.resizeTabs ? this.lastTabWidth+2 : 100);
 
843
    },
 
844
 
 
845
    /**
 
846
     * Scrolls to a particular tab if tab scrolling is enabled
 
847
     * @param {Panel} item The item to scroll to
 
848
     * @param {Boolean} animate True to enable animations
 
849
     */
 
850
 
 
851
    scrollToTab : function(item, animate){
 
852
        if(!item){ return; }
 
853
        var el = this.getTabEl(item);
 
854
        var pos = this.getScrollPos(), area = this.getScrollArea();
 
855
        var left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos;
 
856
        var right = left + el.offsetWidth;
 
857
        if(left < pos){
 
858
            this.scrollTo(left, animate);
 
859
        }else if(right > (pos + area)){
 
860
            this.scrollTo(right - area, animate);
 
861
        }
 
862
    },
 
863
 
 
864
    // private
 
865
    scrollTo : function(pos, animate){
 
866
        this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false);
 
867
        if(!animate){
 
868
            this.updateScrollButtons();
 
869
        }
 
870
    },
 
871
 
 
872
    onWheel : function(e){
 
873
        var d = e.getWheelDelta()*this.wheelIncrement*-1;
 
874
        e.stopEvent();
 
875
 
 
876
        var pos = this.getScrollPos();
 
877
        var newpos = pos + d;
 
878
        var sw = this.getScrollWidth()-this.getScrollArea();
 
879
 
 
880
        var s = Math.max(0, Math.min(sw, newpos));
 
881
        if(s != pos){
 
882
            this.scrollTo(s, false);
 
883
        }
 
884
    },
 
885
 
 
886
    // private
 
887
    onScrollRight : function(){
 
888
        var sw = this.getScrollWidth()-this.getScrollArea();
 
889
        var pos = this.getScrollPos();
 
890
        var s = Math.min(sw, pos + this.getScrollIncrement());
 
891
        if(s != pos){
 
892
            this.scrollTo(s, this.animScroll);
 
893
        }
 
894
    },
 
895
 
 
896
    // private
 
897
    onScrollLeft : function(){
 
898
        var pos = this.getScrollPos();
 
899
        var s = Math.max(0, pos - this.getScrollIncrement());
 
900
        if(s != pos){
 
901
            this.scrollTo(s, this.animScroll);
 
902
        }
 
903
    },
 
904
 
 
905
    // private
 
906
    updateScrollButtons : function(){
 
907
        var pos = this.getScrollPos();
 
908
        this.scrollLeft[pos == 0 ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
 
909
        this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
 
910
    }
 
911
 
 
912
    /**
 
913
     * @cfg {Boolean} collapsible
 
914
     * @hide
 
915
     */
 
916
    /**
 
917
     * @cfg {String} header
 
918
     * @hide
 
919
     */
 
920
    /**
 
921
     * @cfg {Boolean} headerAsText
 
922
     * @hide
 
923
     */
 
924
    /**
 
925
     * @property header
 
926
     * @hide
 
927
     */
 
928
    /**
 
929
     * @property title
 
930
     * @hide
 
931
     */
 
932
    /**
 
933
     * @cfg {Array} tools
 
934
     * @hide
 
935
     */
 
936
    /**
 
937
     * @cfg {Boolean} hideCollapseTool
 
938
     * @hide
 
939
     */
 
940
    /**
 
941
     * @cfg {Boolean} titleCollapse
 
942
     * @hide
 
943
     */
 
944
    /**
 
945
     * @cfg {Boolean} collapsed
 
946
     * @hide
 
947
     */
 
948
    /**
 
949
     * @cfg {String} layout
 
950
     * @hide
 
951
     */
 
952
    /**
 
953
     * @cfg {Object} layoutConfig
 
954
     * @hide
 
955
     */
 
956
 
 
957
});
 
958
Ext.reg('tabpanel', Ext.TabPanel);
 
959
 
 
960
/**
 
961
 * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
 
962
 * can return false to cancel the tab change.
 
963
 * @param {String/Panel} tab The id or tab Panel to activate
 
964
 * @method activate
 
965
 */
 
966
Ext.TabPanel.prototype.activate = Ext.TabPanel.prototype.setActiveTab;
 
967
 
 
968
// private utility class used by TabPanel
 
969
Ext.TabPanel.AccessStack = function(){
 
970
    var items = [];
 
971
    return {
 
972
        add : function(item){
 
973
            items.push(item);
 
974
            if(items.length > 10){
 
975
                items.shift();
 
976
            }
 
977
        },
 
978
 
 
979
        remove : function(item){
 
980
            var s = [];
 
981
            for(var i = 0, len = items.length; i < len; i++) {
 
982
                if(items[i] != item){
 
983
                    s.push(items[i]);
 
984
                }
 
985
            }
 
986
            items = s;
 
987
        },
 
988
 
 
989
        next : function(){
 
990
            return items.pop();
 
991
        }
 
992
    };
 
993
};
 
994