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