Merge commit 'catalyst/MOODLE_19_STABLE' into mdl19-linuxchix
[moodle-linuxchix.git] / lib / yui / tabview / tabview.js
blob6248e5658ca87ae494ab9feb1c49d0092ad9ec4f
1 /*
2 Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.5.2
6 */
7 (function() {
9     /**
10      * The tabview module provides a widget for managing content bound to tabs.
11      * @module tabview
12      * @requires yahoo, dom, event, element
13      *
14      */
15     /**
16      * A widget to control tabbed views.
17      * @namespace YAHOO.widget
18      * @class TabView
19      * @extends YAHOO.util.Element
20      * @constructor
21      * @param {HTMLElement | String | Object} el(optional) The html 
22      * element that represents the TabView, or the attribute object to use. 
23      * An element will be created if none provided.
24      * @param {Object} attr (optional) A key map of the tabView's 
25      * initial attributes.  Ignored if first arg is attributes object.
26      */
27     YAHOO.widget.TabView = function(el, attr) {
28         attr = attr || {};
29         if (arguments.length == 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
30             attr = el; // treat first arg as attr object
31             el = attr.element || null;
32         }
33         
34         if (!el && !attr.element) { // create if we dont have one
35             el = _createTabViewElement.call(this, attr);
36         }
37         YAHOO.widget.TabView.superclass.constructor.call(this, el, attr); 
38     };
40     YAHOO.extend(YAHOO.widget.TabView, YAHOO.util.Element);
41     
42     var proto = YAHOO.widget.TabView.prototype;
43     var Dom = YAHOO.util.Dom;
44     var Event = YAHOO.util.Event;
45     var Tab = YAHOO.widget.Tab;
46     
47     
48     /**
49      * The className to add when building from scratch. 
50      * @property CLASSNAME
51      * @default "navset"
52      */
53     proto.CLASSNAME = 'yui-navset';
54     
55     /**
56      * The className of the HTMLElement containing the TabView's tab elements
57      * to look for when building from existing markup, or to add when building
58      * from scratch. 
59      * All childNodes of the tab container are treated as Tabs when building
60      * from existing markup.
61      * @property TAB_PARENT_CLASSNAME
62      * @default "nav"
63      */
64     proto.TAB_PARENT_CLASSNAME = 'yui-nav';
65     
66     /**
67      * The className of the HTMLElement containing the TabView's label elements
68      * to look for when building from existing markup, or to add when building
69      * from scratch. 
70      * All childNodes of the content container are treated as content elements when
71      * building from existing markup.
72      * @property CONTENT_PARENT_CLASSNAME
73      * @default "nav-content"
74      */
75     proto.CONTENT_PARENT_CLASSNAME = 'yui-content';
76     
77     proto._tabParent = null;
78     proto._contentParent = null; 
79     
80     /**
81      * Adds a Tab to the TabView instance.  
82      * If no index is specified, the tab is added to the end of the tab list.
83      * @method addTab
84      * @param {YAHOO.widget.Tab} tab A Tab instance to add.
85      * @param {Integer} index The position to add the tab. 
86      * @return void
87      */
88     proto.addTab = function(tab, index) {
89         var tabs = this.get('tabs');
90         if (!tabs) { // not ready yet
91             this._queue[this._queue.length] = ['addTab', arguments];
92             return false;
93         }
94         
95         index = (index === undefined) ? tabs.length : index;
96         
97         var before = this.getTab(index);
98         
99         var self = this;
100         var el = this.get('element');
101         var tabParent = this._tabParent;
102         var contentParent = this._contentParent;
104         var tabElement = tab.get('element');
105         var contentEl = tab.get('contentEl');
107         if ( before ) {
108             tabParent.insertBefore(tabElement, before.get('element'));
109         } else {
110             tabParent.appendChild(tabElement);
111         }
113         if ( contentEl && !Dom.isAncestor(contentParent, contentEl) ) {
114             contentParent.appendChild(contentEl);
115         }
116         
117         if ( !tab.get('active') ) {
118             tab.set('contentVisible', false, true); /* hide if not active */
119         } else {
120             this.set('activeTab', tab, true);
121             
122         }
124         var activate = function(e) {
125             YAHOO.util.Event.preventDefault(e);
126             var silent = false;
128             if (this == self.get('activeTab')) {
129                 silent = true; // dont fire activeTabChange if already active
130             }
131             self.set('activeTab', this, silent);
132         };
133         
134         tab.addListener( tab.get('activationEvent'), activate);
135         
136         tab.addListener('activationEventChange', function(e) {
137             if (e.prevValue != e.newValue) {
138                 tab.removeListener(e.prevValue, activate);
139                 tab.addListener(e.newValue, activate);
140             }
141         });
142         
143         tabs.splice(index, 0, tab);
144     };
146     /**
147      * Routes childNode events.
148      * @method DOMEventHandler
149      * @param {event} e The Dom event that is being handled.
150      * @return void
151      */
152     proto.DOMEventHandler = function(e) {
153         var el = this.get('element');
154         var target = YAHOO.util.Event.getTarget(e);
155         var tabParent = this._tabParent;
156         
157         if (Dom.isAncestor(tabParent, target) ) {
158             var tabEl;
159             var tab = null;
160             var contentEl;
161             var tabs = this.get('tabs');
163             for (var i = 0, len = tabs.length; i < len; i++) {
164                 tabEl = tabs[i].get('element');
165                 contentEl = tabs[i].get('contentEl');
167                 if ( target == tabEl || Dom.isAncestor(tabEl, target) ) {
168                     tab = tabs[i];
169                     break; // note break
170                 }
171             } 
172             
173             if (tab) {
174                 tab.fireEvent(e.type, e);
175             }
176         }
177     };
178     
179     /**
180      * Returns the Tab instance at the specified index.
181      * @method getTab
182      * @param {Integer} index The position of the Tab.
183      * @return YAHOO.widget.Tab
184      */
185     proto.getTab = function(index) {
186         return this.get('tabs')[index];
187     };
188     
189     /**
190      * Returns the index of given tab.
191      * @method getTabIndex
192      * @param {YAHOO.widget.Tab} tab The tab whose index will be returned.
193      * @return int
194      */
195     proto.getTabIndex = function(tab) {
196         var index = null;
197         var tabs = this.get('tabs');
198         for (var i = 0, len = tabs.length; i < len; ++i) {
199             if (tab == tabs[i]) {
200                 index = i;
201                 break;
202             }
203         }
204         
205         return index;
206     };
207     
208     /**
209      * Removes the specified Tab from the TabView.
210      * @method removeTab
211      * @param {YAHOO.widget.Tab} item The Tab instance to be removed.
212      * @return void
213      */
214     proto.removeTab = function(tab) {
215         var tabCount = this.get('tabs').length;
217         var index = this.getTabIndex(tab);
218         var nextIndex = index + 1;
219         if ( tab == this.get('activeTab') ) { // select next tab
220             if (tabCount > 1) {
221                 if (index + 1 == tabCount) {
222                     this.set('activeIndex', index - 1);
223                 } else {
224                     this.set('activeIndex', index + 1);
225                 }
226             }
227         }
228         
229         this._tabParent.removeChild( tab.get('element') );
230         this._contentParent.removeChild( tab.get('contentEl') );
231         this._configs.tabs.value.splice(index, 1);
232         
233     };
234     
235     /**
236      * Provides a readable name for the TabView instance.
237      * @method toString
238      * @return String
239      */
240     proto.toString = function() {
241         var name = this.get('id') || this.get('tagName');
242         return "TabView " + name; 
243     };
244     
245     /**
246      * The transiton to use when switching between tabs.
247      * @method contentTransition
248      */
249     proto.contentTransition = function(newTab, oldTab) {
250         newTab.set('contentVisible', true);
251         oldTab.set('contentVisible', false);
252     };
253     
254     /**
255      * setAttributeConfigs TabView specific properties.
256      * @method initAttributes
257      * @param {Object} attr Hash of initial attributes
258      */
259     proto.initAttributes = function(attr) {
260         YAHOO.widget.TabView.superclass.initAttributes.call(this, attr);
261         
262         if (!attr.orientation) {
263             attr.orientation = 'top';
264         }
265         
266         var el = this.get('element');
268         if (!YAHOO.util.Dom.hasClass(el, this.CLASSNAME)) {
269             YAHOO.util.Dom.addClass(el, this.CLASSNAME);        
270         }
271         
272         /**
273          * The Tabs belonging to the TabView instance.
274          * @attribute tabs
275          * @type Array
276          */
277         this.setAttributeConfig('tabs', {
278             value: [],
279             readOnly: true
280         });
282         /**
283          * The container of the tabView's label elements.
284          * @property _tabParent
285          * @private
286          * @type HTMLElement
287          */
288         this._tabParent = 
289                 this.getElementsByClassName(this.TAB_PARENT_CLASSNAME,
290                         'ul' )[0] || _createTabParent.call(this);
291             
292         /**
293          * The container of the tabView's content elements.
294          * @property _contentParent
295          * @type HTMLElement
296          * @private
297          */
298         this._contentParent = 
299                 this.getElementsByClassName(this.CONTENT_PARENT_CLASSNAME,
300                         'div')[0] ||  _createContentParent.call(this);
301         
302         /**
303          * How the Tabs should be oriented relative to the TabView.
304          * @attribute orientation
305          * @type String
306          * @default "top"
307          */
308         this.setAttributeConfig('orientation', {
309             value: attr.orientation,
310             method: function(value) {
311                 var current = this.get('orientation');
312                 this.addClass('yui-navset-' + value);
313                 
314                 if (current != value) {
315                     this.removeClass('yui-navset-' + current);
316                 }
317                 
318                 switch(value) {
319                     case 'bottom':
320                     this.appendChild(this._tabParent);
321                     break;
322                 }
323             }
324         });
325         
326         /**
327          * The index of the tab currently active.
328          * @attribute activeIndex
329          * @type Int
330          */
331         this.setAttributeConfig('activeIndex', {
332             value: attr.activeIndex,
333             method: function(value) {
334                 this.set('activeTab', this.getTab(value));
335             },
336             validator: function(value) {
337                 return !this.getTab(value).get('disabled'); // cannot activate if disabled
338             }
339         });
340         
341         /**
342          * The tab currently active.
343          * @attribute activeTab
344          * @type YAHOO.widget.Tab
345          */
346         this.setAttributeConfig('activeTab', {
347             value: attr.activeTab,
348             method: function(tab) {
349                 var activeTab = this.get('activeTab');
350                 
351                 if (tab) {  
352                     tab.set('active', true);
353                     this._configs['activeIndex'].value = this.getTabIndex(tab); // keep in sync
354                 }
355                 
356                 if (activeTab && activeTab != tab) {
357                     activeTab.set('active', false);
358                 }
359                 
360                 if (activeTab && tab != activeTab) { // no transition if only 1
361                     this.contentTransition(tab, activeTab);
362                 } else if (tab) {
363                     tab.set('contentVisible', true);
364                 }
365             },
366             validator: function(value) {
367                 return !value.get('disabled'); // cannot activate if disabled
368             }
369         });
371         if ( this._tabParent ) {
372             _initTabs.call(this);
373         }
374         
375         // Due to delegation we add all DOM_EVENTS to the TabView container
376         // but IE will leak when unsupported events are added, so remove these
377         this.DOM_EVENTS.submit = false;
378         this.DOM_EVENTS.focus = false;
379         this.DOM_EVENTS.blur = false;
381         for (var type in this.DOM_EVENTS) {
382             if ( YAHOO.lang.hasOwnProperty(this.DOM_EVENTS, type) ) {
383                 this.addListener.call(this, type, this.DOMEventHandler);
384             }
385         }
386     };
387     
388     /**
389      * Creates Tab instances from a collection of HTMLElements.
390      * @method initTabs
391      * @private
392      * @return void
393      */
394     var _initTabs = function() {
395         var tab,
396             attr,
397             contentEl;
398             
399         var el = this.get('element');   
400         var tabs = _getChildNodes(this._tabParent);
401         var contentElements = _getChildNodes(this._contentParent);
403         for (var i = 0, len = tabs.length; i < len; ++i) {
404             attr = {};
405             
406             if (contentElements[i]) {
407                 attr.contentEl = contentElements[i];
408             }
410             tab = new YAHOO.widget.Tab(tabs[i], attr);
411             this.addTab(tab);
412             
413             if (tab.hasClass(tab.ACTIVE_CLASSNAME) ) {
414                 this._configs.activeTab.value = tab; // dont invoke method
415                 this._configs.activeIndex.value = this.getTabIndex(tab);
416             }
417         }
418     };
419     
420     var _createTabViewElement = function(attr) {
421         var el = document.createElement('div');
423         if ( this.CLASSNAME ) {
424             el.className = this.CLASSNAME;
425         }
426         
427         return el;
428     };
429     
430     var _createTabParent = function(attr) {
431         var el = document.createElement('ul');
433         if ( this.TAB_PARENT_CLASSNAME ) {
434             el.className = this.TAB_PARENT_CLASSNAME;
435         }
436         
437         this.get('element').appendChild(el);
438         
439         return el;
440     };
441     
442     var _createContentParent = function(attr) {
443         var el = document.createElement('div');
445         if ( this.CONTENT_PARENT_CLASSNAME ) {
446             el.className = this.CONTENT_PARENT_CLASSNAME;
447         }
448         
449         this.get('element').appendChild(el);
450         
451         return el;
452     };
453     
454     var _getChildNodes = function(el) {
455         var nodes = [];
456         var childNodes = el.childNodes;
457         
458         for (var i = 0, len = childNodes.length; i < len; ++i) {
459             if (childNodes[i].nodeType == 1) {
460                 nodes[nodes.length] = childNodes[i];
461             }
462         }
463         
464         return nodes;
465     };
466 })();
468 (function() {
469     var Dom = YAHOO.util.Dom,
470         Event = YAHOO.util.Event;
471     
472     /**
473      * A representation of a Tab's label and content.
474      * @namespace YAHOO.widget
475      * @class Tab
476      * @extends YAHOO.util.Element
477      * @constructor
478      * @param element {HTMLElement | String} (optional) The html element that 
479      * represents the TabView. An element will be created if none provided.
480      * @param {Object} properties A key map of initial properties
481      */
482     var Tab = function(el, attr) {
483         attr = attr || {};
484         if (arguments.length == 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
485             attr = el;
486             el = attr.element;
487         }
489         if (!el && !attr.element) {
490             el = _createTabElement.call(this, attr);
491         }
493         this.loadHandler =  {
494             success: function(o) {
495                 this.set('content', o.responseText);
496             },
497             failure: function(o) {
498             }
499         };
500         
501         Tab.superclass.constructor.call(this, el, attr);
502         
503         this.DOM_EVENTS = {}; // delegating to tabView
504     };
506     YAHOO.extend(Tab, YAHOO.util.Element);
507     var proto = Tab.prototype;
508     
509     /**
510      * The default tag name for a Tab's inner element.
511      * @property LABEL_INNER_TAGNAME
512      * @type String
513      * @default "em"
514      */
515     proto.LABEL_TAGNAME = 'em';
516     
517     /**
518      * The class name applied to active tabs.
519      * @property ACTIVE_CLASSNAME
520      * @type String
521      * @default "selected"
522      */
523     proto.ACTIVE_CLASSNAME = 'selected';
524     
525     /**
526      * The title applied to active tabs.
527      * @property ACTIVE_TITLE
528      * @type String
529      * @default "active"
530      */
531     proto.ACTIVE_TITLE = 'active';
533     /**
534      * The class name applied to disabled tabs.
535      * @property DISABLED_CLASSNAME
536      * @type String
537      * @default "disabled"
538      */
539     proto.DISABLED_CLASSNAME = 'disabled';
540     
541     /**
542      * The class name applied to dynamic tabs while loading.
543      * @property LOADING_CLASSNAME
544      * @type String
545      * @default "disabled"
546      */
547     proto.LOADING_CLASSNAME = 'loading';
549     /**
550      * Provides a reference to the connection request object when data is
551      * loaded dynamically.
552      * @property dataConnection
553      * @type Object
554      */
555     proto.dataConnection = null;
556     
557     /**
558      * Object containing success and failure callbacks for loading data.
559      * @property loadHandler
560      * @type object
561      */
562     proto.loadHandler = null;
564     proto._loading = false;
565     
566     /**
567      * Provides a readable name for the tab.
568      * @method toString
569      * @return String
570      */
571     proto.toString = function() {
572         var el = this.get('element');
573         var id = el.id || el.tagName;
574         return "Tab " + id; 
575     };
576     
577     /**
578      * setAttributeConfigs TabView specific properties.
579      * @method initAttributes
580      * @param {Object} attr Hash of initial attributes
581      */
582     proto.initAttributes = function(attr) {
583         attr = attr || {};
584         Tab.superclass.initAttributes.call(this, attr);
585         
586         var el = this.get('element');
587         
588         /**
589          * The event that triggers the tab's activation.
590          * @attribute activationEvent
591          * @type String
592          */
593         this.setAttributeConfig('activationEvent', {
594             value: attr.activationEvent || 'click'
595         });        
597         /**
598          * The element that contains the tab's label.
599          * @attribute labelEl
600          * @type HTMLElement
601          */
602         this.setAttributeConfig('labelEl', {
603             value: attr.labelEl || _getlabelEl.call(this),
604             method: function(value) {
605                 var current = this.get('labelEl');
607                 if (current) {
608                     if (current == value) {
609                         return false; // already set
610                     }
611                     
612                     this.replaceChild(value, current);
613                 } else if (el.firstChild) { // ensure label is firstChild by default
614                     this.insertBefore(value, el.firstChild);
615                 } else {
616                     this.appendChild(value);
617                 }  
618             } 
619         });
621         /**
622          * The tab's label text (or innerHTML).
623          * @attribute label
624          * @type String
625          */
626         this.setAttributeConfig('label', {
627             value: attr.label || _getLabel.call(this),
628             method: function(value) {
629                 var labelEl = this.get('labelEl');
630                 if (!labelEl) { // create if needed
631                     this.set('labelEl', _createlabelEl.call(this));
632                 }
633                 
634                 _setLabel.call(this, value);
635             }
636         });
637         
638         /**
639          * The HTMLElement that contains the tab's content.
640          * @attribute contentEl
641          * @type HTMLElement
642          */
643         this.setAttributeConfig('contentEl', {
644             value: attr.contentEl || document.createElement('div'),
645             method: function(value) {
646                 var current = this.get('contentEl');
648                 if (current) {
649                     if (current == value) {
650                         return false; // already set
651                     }
652                     this.replaceChild(value, current);
653                 }
654             }
655         });
656         
657         /**
658          * The tab's content.
659          * @attribute content
660          * @type String
661          */
662         this.setAttributeConfig('content', {
663             value: attr.content,
664             method: function(value) {
665                 this.get('contentEl').innerHTML = value;
666             }
667         });
669         var _dataLoaded = false;
670         
671         /**
672          * The tab's data source, used for loading content dynamically.
673          * @attribute dataSrc
674          * @type String
675          */
676         this.setAttributeConfig('dataSrc', {
677             value: attr.dataSrc
678         });
679         
680         /**
681          * Whether or not content should be reloaded for every view.
682          * @attribute cacheData
683          * @type Boolean
684          * @default false
685          */
686         this.setAttributeConfig('cacheData', {
687             value: attr.cacheData || false,
688             validator: YAHOO.lang.isBoolean
689         });
690         
691         /**
692          * The method to use for the data request.
693          * @attribute loadMethod
694          * @type String
695          * @default "GET"
696          */
697         this.setAttributeConfig('loadMethod', {
698             value: attr.loadMethod || 'GET',
699             validator: YAHOO.lang.isString
700         });
702         /**
703          * Whether or not any data has been loaded from the server.
704          * @attribute dataLoaded
705          * @type Boolean
706          */        
707         this.setAttributeConfig('dataLoaded', {
708             value: false,
709             validator: YAHOO.lang.isBoolean,
710             writeOnce: true
711         });
712         
713         /**
714          * Number if milliseconds before aborting and calling failure handler.
715          * @attribute dataTimeout
716          * @type Number
717          * @default null
718          */
719         this.setAttributeConfig('dataTimeout', {
720             value: attr.dataTimeout || null,
721             validator: YAHOO.lang.isNumber
722         });
723         
724         /**
725          * Whether or not the tab is currently active.
726          * If a dataSrc is set for the tab, the content will be loaded from
727          * the given source.
728          * @attribute active
729          * @type Boolean
730          */
731         this.setAttributeConfig('active', {
732             value: attr.active || this.hasClass(this.ACTIVE_CLASSNAME),
733             method: function(value) {
734                 if (value === true) {
735                     this.addClass(this.ACTIVE_CLASSNAME);
736                     this.set('title', this.ACTIVE_TITLE);
737                 } else {
738                     this.removeClass(this.ACTIVE_CLASSNAME);
739                     this.set('title', '');
740                 }
741             },
742             validator: function(value) {
743                 return YAHOO.lang.isBoolean(value) && !this.get('disabled') ;
744             }
745         });
746         
747         /**
748          * Whether or not the tab is disabled.
749          * @attribute disabled
750          * @type Boolean
751          */
752         this.setAttributeConfig('disabled', {
753             value: attr.disabled || this.hasClass(this.DISABLED_CLASSNAME),
754             method: function(value) {
755                 if (value === true) {
756                     Dom.addClass(this.get('element'), this.DISABLED_CLASSNAME);
757                 } else {
758                     Dom.removeClass(this.get('element'), this.DISABLED_CLASSNAME);
759                 }
760             },
761             validator: YAHOO.lang.isBoolean
762         });
763         
764         /**
765          * The href of the tab's anchor element.
766          * @attribute href
767          * @type String
768          * @default '#'
769          */
770         this.setAttributeConfig('href', {
771             value: attr.href ||
772                     this.getElementsByTagName('a')[0].getAttribute('href', 2) || '#',
773             method: function(value) {
774                 this.getElementsByTagName('a')[0].href = value;
775             },
776             validator: YAHOO.lang.isString
777         });
778         
779         /**
780          * The Whether or not the tab's content is visible.
781          * @attribute contentVisible
782          * @type Boolean
783          * @default false
784          */
785         this.setAttributeConfig('contentVisible', {
786             value: attr.contentVisible,
787             method: function(value) {
788                 if (value) {
789                     this.get('contentEl').style.display = 'block';
790                     
791                     if ( this.get('dataSrc') ) {
792                      // load dynamic content unless already loading or loaded and caching
793                         if ( !this._loading && !(this.get('dataLoaded') && this.get('cacheData')) ) {
794                             _dataConnect.call(this);
795                         }
796                     }
797                 } else {
798                     this.get('contentEl').style.display = 'none';
799                 }
800             },
801             validator: YAHOO.lang.isBoolean
802         });
803     };
804     
805     var _createTabElement = function(attr) {
806         var el = document.createElement('li');
807         var a = document.createElement('a');
808         
809         a.href = attr.href || '#';
810         
811         el.appendChild(a);
812         
813         var label = attr.label || null;
814         var labelEl = attr.labelEl || null;
815         
816         if (labelEl) { // user supplied labelEl
817             if (!label) { // user supplied label
818                 label = _getLabel.call(this, labelEl);
819             }
820         } else {
821             labelEl = _createlabelEl.call(this);
822         }
823         
824         a.appendChild(labelEl);
825         
826         return el;
827     };
828     
829     var _getlabelEl = function() {
830         return this.getElementsByTagName(this.LABEL_TAGNAME)[0];
831     };
832     
833     var _createlabelEl = function() {
834         var el = document.createElement(this.LABEL_TAGNAME);
835         return el;
836     };
837     
838     var _setLabel = function(label) {
839         var el = this.get('labelEl');
840         el.innerHTML = label;
841     };
842     
843     var _getLabel = function() {
844         var label,
845             el = this.get('labelEl');
846             
847             if (!el) {
848                 return undefined;
849             }
850         
851         return el.innerHTML;
852     };
853     
854     var _dataConnect = function() {
855         if (!YAHOO.util.Connect) {
856             return false;
857         }
859         Dom.addClass(this.get('contentEl').parentNode, this.LOADING_CLASSNAME);
860         this._loading = true; 
861         this.dataConnection = YAHOO.util.Connect.asyncRequest(
862             this.get('loadMethod'),
863             this.get('dataSrc'), 
864             {
865                 success: function(o) {
866                     this.loadHandler.success.call(this, o);
867                     this.set('dataLoaded', true);
868                     this.dataConnection = null;
869                     Dom.removeClass(this.get('contentEl').parentNode,
870                             this.LOADING_CLASSNAME);
871                     this._loading = false;
872                 },
873                 failure: function(o) {
874                     this.loadHandler.failure.call(this, o);
875                     this.dataConnection = null;
876                     Dom.removeClass(this.get('contentEl').parentNode,
877                             this.LOADING_CLASSNAME);
878                     this._loading = false;
879                 },
880                 scope: this,
881                 timeout: this.get('dataTimeout')
882             }
883         );
884     };
885     
886     YAHOO.widget.Tab = Tab;
887 })();
889 YAHOO.register("tabview", YAHOO.widget.TabView, {version: "2.5.2", build: "1076"});