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