MDL-15505
[moodle-linuxchix.git] / lib / yui / tabview / tabview-debug.js
blob068d438d6a25678b177f956db003bdc287721c52
1 /*
2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.3.0
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          * @config 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          * @config 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          * @config 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          * @config 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     };
468  * Fires before the activeTab is changed.
469  * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
470  * <p>If handler returns false, the change will be cancelled, and the value will not
471  * be set.</p>
472  * <p><strong>Event fields:</strong><br>
473  * <code>&lt;String&gt; type</code> beforeActiveTabChange<br>
474  * <code>&lt;<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>&gt;
475  * prevValue</code> the currently active tab<br>
476  * <code>&lt;<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>&gt;
477  * newValue</code> the tab to be made active</p>
478  * <p><strong>Usage:</strong><br>
479  * <code>var handler = function(e) {var previous = e.prevValue};<br>
480  * myTabs.addListener('beforeActiveTabChange', handler);</code></p>
481  * @event beforeActiveTabChange
482  */
483     
485  * Fires after the activeTab is changed.
486  * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
487  * <p><strong>Event fields:</strong><br>
488  * <code>&lt;String&gt; type</code> activeTabChange<br>
489  * <code>&lt;<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>&gt;
490  * prevValue</code> the formerly active tab<br>
491  * <code>&lt;<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>&gt;
492  * newValue</code> the new active tab</p>
493  * <p><strong>Usage:</strong><br>
494  * <code>var handler = function(e) {var previous = e.prevValue};<br>
495  * myTabs.addListener('activeTabChange', handler);</code></p>
496  * @event activeTabChange
497  */
500  * Fires before the orientation is changed.
501  * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
502  * <p>If handler returns false, the change will be cancelled, and the value will not
503  * be set.</p>
504  * <p><strong>Event fields:</strong><br>
505  * <code>&lt;String&gt; type</code> beforeOrientationChange<br>
506  * <code>&lt;String&gt;
507  * prevValue</code> the current orientation<br>
508  * <code>&lt;String&gt;
509  * newValue</code> the new orientation to be applied</p>
510  * <p><strong>Usage:</strong><br>
511  * <code>var handler = function(e) {var previous = e.prevValue};<br>
512  * myTabs.addListener('beforeOrientationChange', handler);</code></p>
513  * @event beforeOrientationChange
514  */
515     
517  * Fires after the orientation is changed.
518  * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
519  * <p><strong>Event fields:</strong><br>
520  * <code>&lt;String&gt; type</code> orientationChange<br>
521  * <code>&lt;String&gt;
522  * prevValue</code> the former orientation<br>
523  * <code>&lt;String&gt;
524  * newValue</code> the new orientation</p>
525  * <p><strong>Usage:</strong><br>
526  * <code>var handler = function(e) {var previous = e.prevValue};<br>
527  * myTabs.addListener('orientationChange', handler);</code></p>
528  * @event orientationChange
529  */
530 })();
532 (function() {
533     var Dom = YAHOO.util.Dom,
534         Event = YAHOO.util.Event;
535     
536     /**
537      * A representation of a Tab's label and content.
538      * @namespace YAHOO.widget
539      * @class Tab
540      * @extends YAHOO.util.Element
541      * @constructor
542      * @param element {HTMLElement | String} (optional) The html element that 
543      * represents the TabView. An element will be created if none provided.
544      * @param {Object} properties A key map of initial properties
545      */
546     var Tab = function(el, attr) {
547         attr = attr || {};
548         if (arguments.length == 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
549             attr = el;
550             el = attr.element;
551         }
553         if (!el && !attr.element) {
554             el = _createTabElement.call(this, attr);
555         }
557         this.loadHandler =  {
558             success: function(o) {
559                 this.set('content', o.responseText);
560             },
561             failure: function(o) {
562                 YAHOO.log('loading failed: ' + o.statusText,
563                         'error', 'Tab');
564             }
565         };
566         
567         Tab.superclass.constructor.call(this, el, attr);
568         
569         this.DOM_EVENTS = {}; // delegating to tabView
570     };
572     YAHOO.extend(Tab, YAHOO.util.Element);
573     var proto = Tab.prototype;
574     
575     /**
576      * The default tag name for a Tab's inner element.
577      * @property LABEL_INNER_TAGNAME
578      * @type String
579      * @default "em"
580      */
581     proto.LABEL_TAGNAME = 'em';
582     
583     /**
584      * The class name applied to active tabs.
585      * @property ACTIVE_CLASSNAME
586      * @type String
587      * @default "on"
588      */
589     proto.ACTIVE_CLASSNAME = 'selected';
590     
591     /**
592      * The class name applied to disabled tabs.
593      * @property DISABLED_CLASSNAME
594      * @type String
595      * @default "disabled"
596      */
597     proto.DISABLED_CLASSNAME = 'disabled';
598     
599     /**
600      * The class name applied to dynamic tabs while loading.
601      * @property LOADING_CLASSNAME
602      * @type String
603      * @default "disabled"
604      */
605     proto.LOADING_CLASSNAME = 'loading';
607     /**
608      * Provides a reference to the connection request object when data is
609      * loaded dynamically.
610      * @property dataConnection
611      * @type Object
612      */
613     proto.dataConnection = null;
614     
615     /**
616      * Object containing success and failure callbacks for loading data.
617      * @property loadHandler
618      * @type object
619      */
620     proto.loadHandler = null;
622     proto._loading = false;
623     
624     /**
625      * Provides a readable name for the tab.
626      * @method toString
627      * @return String
628      */
629     proto.toString = function() {
630         var el = this.get('element');
631         var id = el.id || el.tagName;
632         return "Tab " + id; 
633     };
634     
635     /**
636      * setAttributeConfigs TabView specific properties.
637      * @method initAttributes
638      * @param {Object} attr Hash of initial attributes
639      */
640     proto.initAttributes = function(attr) {
641         attr = attr || {};
642         Tab.superclass.initAttributes.call(this, attr);
643         
644         var el = this.get('element');
645         
646         /**
647          * The event that triggers the tab's activation.
648          * @config activationEvent
649          * @type String
650          */
651         this.setAttributeConfig('activationEvent', {
652             value: attr.activationEvent || 'click'
653         });        
655         /**
656          * The element that contains the tab's label.
657          * @config labelEl
658          * @type HTMLElement
659          */
660         this.setAttributeConfig('labelEl', {
661             value: attr.labelEl || _getlabelEl.call(this),
662             method: function(value) {
663                 var current = this.get('labelEl');
665                 if (current) {
666                     if (current == value) {
667                         return false; // already set
668                     }
669                     
670                     this.replaceChild(value, current);
671                 } else if (el.firstChild) { // ensure label is firstChild by default
672                     this.insertBefore(value, el.firstChild);
673                 } else {
674                     this.appendChild(value);
675                 }  
676             } 
677         });
679         /**
680          * The tab's label text (or innerHTML).
681          * @config label
682          * @type String
683          */
684         this.setAttributeConfig('label', {
685             value: attr.label || _getLabel.call(this),
686             method: function(value) {
687                 var labelEl = this.get('labelEl');
688                 if (!labelEl) { // create if needed
689                     this.set('labelEl', _createlabelEl.call(this));
690                 }
691                 
692                 _setLabel.call(this, value);
693             }
694         });
695         
696         /**
697          * The HTMLElement that contains the tab's content.
698          * @config contentEl
699          * @type HTMLElement
700          */
701         this.setAttributeConfig('contentEl', {
702             value: attr.contentEl || document.createElement('div'),
703             method: function(value) {
704                 var current = this.get('contentEl');
706                 if (current) {
707                     if (current == value) {
708                         return false; // already set
709                     }
710                     this.replaceChild(value, current);
711                 }
712             }
713         });
714         
715         /**
716          * The tab's content.
717          * @config content
718          * @type String
719          */
720         this.setAttributeConfig('content', {
721             value: attr.content,
722             method: function(value) {
723                 this.get('contentEl').innerHTML = value;
724             }
725         });
727         var _dataLoaded = false;
728         
729         /**
730          * The tab's data source, used for loading content dynamically.
731          * @config dataSrc
732          * @type String
733          */
734         this.setAttributeConfig('dataSrc', {
735             value: attr.dataSrc
736         });
737         
738         /**
739          * Whether or not content should be reloaded for every view.
740          * @config cacheData
741          * @type Boolean
742          * @default false
743          */
744         this.setAttributeConfig('cacheData', {
745             value: attr.cacheData || false,
746             validator: YAHOO.lang.isBoolean
747         });
748         
749         /**
750          * The method to use for the data request.
751          * @config loadMethod
752          * @type String
753          * @default "GET"
754          */
755         this.setAttributeConfig('loadMethod', {
756             value: attr.loadMethod || 'GET',
757             validator: YAHOO.lang.isString
758         });
760         /**
761          * Whether or not any data has been loaded from the server.
762          * @config dataLoaded
763          * @type Boolean
764          */        
765         this.setAttributeConfig('dataLoaded', {
766             value: false,
767             validator: YAHOO.lang.isBoolean,
768             writeOnce: true
769         });
770         
771         /**
772          * Number if milliseconds before aborting and calling failure handler.
773          * @config dataTimeout
774          * @type Number
775          * @default null
776          */
777         this.setAttributeConfig('dataTimeout', {
778             value: attr.dataTimeout || null,
779             validator: YAHOO.lang.isNumber
780         });
781         
782         /**
783          * Whether or not the tab is currently active.
784          * If a dataSrc is set for the tab, the content will be loaded from
785          * the given source.
786          * @config active
787          * @type Boolean
788          */
789         this.setAttributeConfig('active', {
790             value: attr.active || this.hasClass(this.ACTIVE_CLASSNAME),
791             method: function(value) {
792                 if (value === true) {
793                     this.addClass(this.ACTIVE_CLASSNAME);
794                     this.set('title', 'active');
795                 } else {
796                     this.removeClass(this.ACTIVE_CLASSNAME);
797                     this.set('title', '');
798                 }
799             },
800             validator: function(value) {
801                 return YAHOO.lang.isBoolean(value) && !this.get('disabled') ;
802             }
803         });
804         
805         /**
806          * Whether or not the tab is disabled.
807          * @config disabled
808          * @type Boolean
809          */
810         this.setAttributeConfig('disabled', {
811             value: attr.disabled || this.hasClass(this.DISABLED_CLASSNAME),
812             method: function(value) {
813                 if (value === true) {
814                     Dom.addClass(this.get('element'), this.DISABLED_CLASSNAME);
815                 } else {
816                     Dom.removeClass(this.get('element'), this.DISABLED_CLASSNAME);
817                 }
818             },
819             validator: YAHOO.lang.isBoolean
820         });
821         
822         /**
823          * The href of the tab's anchor element.
824          * @config href
825          * @type String
826          * @default '#'
827          */
828         this.setAttributeConfig('href', {
829             value: attr.href ||
830                     this.getElementsByTagName('a')[0].getAttribute('href', 2) || '#',
831             method: function(value) {
832                 this.getElementsByTagName('a')[0].href = value;
833             },
834             validator: YAHOO.lang.isString
835         });
836         
837         /**
838          * The Whether or not the tab's content is visible.
839          * @config contentVisible
840          * @type Boolean
841          * @default false
842          */
843         this.setAttributeConfig('contentVisible', {
844             value: attr.contentVisible,
845             method: function(value) {
846                 if (value) {
847                     this.get('contentEl').style.display = 'block';
848                     
849                     if ( this.get('dataSrc') ) {
850                      // load dynamic content unless already loading or loaded and caching
851                         if ( !this._loading && !(this.get('dataLoaded') && this.get('cacheData')) ) {
852                             _dataConnect.call(this);
853                         }
854                     }
855                 } else {
856                     this.get('contentEl').style.display = 'none';
857                 }
858             },
859             validator: YAHOO.lang.isBoolean
860         });
861     };
862     
863     var _createTabElement = function(attr) {
864         var el = document.createElement('li');
865         var a = document.createElement('a');
866         
867         a.href = attr.href || '#';
868         
869         el.appendChild(a);
870         
871         var label = attr.label || null;
872         var labelEl = attr.labelEl || null;
873         
874         if (labelEl) { // user supplied labelEl
875             if (!label) { // user supplied label
876                 label = _getLabel.call(this, labelEl);
877             }
878         } else {
879             labelEl = _createlabelEl.call(this);
880         }
881         
882         a.appendChild(labelEl);
883         
884         return el;
885     };
886     
887     var _getlabelEl = function() {
888         return this.getElementsByTagName(this.LABEL_TAGNAME)[0];
889     };
890     
891     var _createlabelEl = function() {
892         var el = document.createElement(this.LABEL_TAGNAME);
893         return el;
894     };
895     
896     var _setLabel = function(label) {
897         var el = this.get('labelEl');
898         el.innerHTML = label;
899     };
900     
901     var _getLabel = function() {
902         var label,
903             el = this.get('labelEl');
904             
905             if (!el) {
906                 return undefined;
907             }
908         
909         return el.innerHTML;
910     };
911     
912     var _dataConnect = function() {
913         if (!YAHOO.util.Connect) {
914             YAHOO.log('YAHOO.util.Connect dependency not met',
915                     'error', 'Tab');
916             return false;
917         }
919         Dom.addClass(this.get('contentEl').parentNode, this.LOADING_CLASSNAME);
920         this._loading = true; 
921         this.dataConnection = YAHOO.util.Connect.asyncRequest(
922             this.get('loadMethod'),
923             this.get('dataSrc'), 
924             {
925                 success: function(o) {
926                     this.loadHandler.success.call(this, o);
927                     this.set('dataLoaded', true);
928                     this.dataConnection = null;
929                     Dom.removeClass(this.get('contentEl').parentNode,
930                             this.LOADING_CLASSNAME);
931                     this._loading = false;
932                 },
933                 failure: function(o) {
934                     this.loadHandler.failure.call(this, o);
935                     this.dataConnection = null;
936                     Dom.removeClass(this.get('contentEl').parentNode,
937                             this.LOADING_CLASSNAME);
938                     this._loading = false;
939                 },
940                 scope: this,
941                 timeout: this.get('dataTimeout')
942             }
943         );
944     };
945     
946     YAHOO.widget.Tab = Tab;
947     
948     /**
949      * Fires before the active state is changed.
950      * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
951      * <p>If handler returns false, the change will be cancelled, and the value will not
952      * be set.</p>
953      * <p><strong>Event fields:</strong><br>
954      * <code>&lt;String&gt; type</code> beforeActiveChange<br>
955      * <code>&lt;Boolean&gt;
956      * prevValue</code> the current value<br>
957      * <code>&lt;Boolean&gt;
958      * newValue</code> the new value</p>
959      * <p><strong>Usage:</strong><br>
960      * <code>var handler = function(e) {var previous = e.prevValue};<br>
961      * myTabs.addListener('beforeActiveChange', handler);</code></p>
962      * @event beforeActiveChange
963      */
964         
965     /**
966      * Fires after the active state is changed.
967      * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
968      * <p><strong>Event fields:</strong><br>
969      * <code>&lt;String&gt; type</code> activeChange<br>
970      * <code>&lt;Boolean&gt;
971      * prevValue</code> the previous value<br>
972      * <code>&lt;Boolean&gt;
973      * newValue</code> the updated value</p>
974      * <p><strong>Usage:</strong><br>
975      * <code>var handler = function(e) {var previous = e.prevValue};<br>
976      * myTabs.addListener('activeChange', handler);</code></p>
977      * @event activeChange
978      */
979      
980     /**
981      * Fires before the tab label is changed.
982      * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
983      * <p>If handler returns false, the change will be cancelled, and the value will not
984      * be set.</p>
985      * <p><strong>Event fields:</strong><br>
986      * <code>&lt;String&gt; type</code> beforeLabelChange<br>
987      * <code>&lt;String&gt;
988      * prevValue</code> the current value<br>
989      * <code>&lt;String&gt;
990      * newValue</code> the new value</p>
991      * <p><strong>Usage:</strong><br>
992      * <code>var handler = function(e) {var previous = e.prevValue};<br>
993      * myTabs.addListener('beforeLabelChange', handler);</code></p>
994      * @event beforeLabelChange
995      */
996         
997     /**
998      * Fires after the tab label is changed.
999      * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1000      * <p><strong>Event fields:</strong><br>
1001      * <code>&lt;String&gt; type</code> labelChange<br>
1002      * <code>&lt;String&gt;
1003      * prevValue</code> the previous value<br>
1004      * <code>&lt;String&gt;
1005      * newValue</code> the updated value</p>
1006      * <p><strong>Usage:</strong><br>
1007      * <code>var handler = function(e) {var previous = e.prevValue};<br>
1008      * myTabs.addListener('labelChange', handler);</code></p>
1009      * @event labelChange
1010      */
1011      
1012     /**
1013      * Fires before the tab content is changed.
1014      * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1015      * <p>If handler returns false, the change will be cancelled, and the value will not
1016      * be set.</p>
1017      * <p><strong>Event fields:</strong><br>
1018      * <code>&lt;String&gt; type</code> beforeContentChange<br>
1019      * <code>&lt;String&gt;
1020      * prevValue</code> the current value<br>
1021      * <code>&lt;String&gt;
1022      * newValue</code> the new value</p>
1023      * <p><strong>Usage:</strong><br>
1024      * <code>var handler = function(e) {var previous = e.prevValue};<br>
1025      * myTabs.addListener('beforeContentChange', handler);</code></p>
1026      * @event beforeContentChange
1027      */
1028         
1029     /**
1030      * Fires after the tab content is changed.
1031      * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1032      * <p><strong>Event fields:</strong><br>
1033      * <code>&lt;String&gt; type</code> contentChange<br>
1034      * <code>&lt;String&gt;
1035      * prevValue</code> the previous value<br>
1036      * <code>&lt;Boolean&gt;
1037      * newValue</code> the updated value</p>
1038      * <p><strong>Usage:</strong><br>
1039      * <code>var handler = function(e) {var previous = e.prevValue};<br>
1040      * myTabs.addListener('contentChange', handler);</code></p>
1041      * @event contentChange
1042      */
1043 })();
1045 YAHOO.register("tabview", YAHOO.widget.TabView, {version: "2.3.0", build: "442"});