2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
10 * The tabview module provides a widget for managing content bound to tabs.
12 * @requires yahoo, dom, event, element
16 * A widget to control tabbed views.
17 * @namespace YAHOO.widget
19 * @extends YAHOO.util.Element
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.
27 YAHOO.widget.TabView = function(el, 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;
34 if (!el && !attr.element) { // create if we dont have one
35 el = _createTabViewElement.call(this, attr);
37 YAHOO.widget.TabView.superclass.constructor.call(this, el, attr);
40 YAHOO.extend(YAHOO.widget.TabView, YAHOO.util.Element);
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;
49 * The className to add when building from scratch.
53 proto.CLASSNAME = 'yui-navset';
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
59 * All childNodes of the tab container are treated as Tabs when building
60 * from existing markup.
61 * @property TAB_PARENT_CLASSNAME
64 proto.TAB_PARENT_CLASSNAME = 'yui-nav';
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
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"
75 proto.CONTENT_PARENT_CLASSNAME = 'yui-content';
77 proto._tabParent = null;
78 proto._contentParent = null;
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.
84 * @param {YAHOO.widget.Tab} tab A Tab instance to add.
85 * @param {Integer} index The position to add the tab.
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];
95 index = (index === undefined) ? tabs.length : index;
97 var before = this.getTab(index);
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');
108 tabParent.insertBefore(tabElement, before.get('element'));
110 tabParent.appendChild(tabElement);
113 if ( contentEl && !Dom.isAncestor(contentParent, contentEl) ) {
114 contentParent.appendChild(contentEl);
117 if ( !tab.get('active') ) {
118 tab.set('contentVisible', false, true); /* hide if not active */
120 this.set('activeTab', tab, true);
124 var activate = function(e) {
125 YAHOO.util.Event.preventDefault(e);
128 if (this == self.get('activeTab')) {
129 silent = true; // dont fire activeTabChange if already active
131 self.set('activeTab', this, silent);
134 tab.addListener( tab.get('activationEvent'), activate);
136 tab.addListener('activationEventChange', function(e) {
137 if (e.prevValue != e.newValue) {
138 tab.removeListener(e.prevValue, activate);
139 tab.addListener(e.newValue, activate);
143 tabs.splice(index, 0, tab);
147 * Routes childNode events.
148 * @method DOMEventHandler
149 * @param {event} e The Dom event that is being handled.
152 proto.DOMEventHandler = function(e) {
153 var el = this.get('element');
154 var target = YAHOO.util.Event.getTarget(e);
155 var tabParent = this._tabParent;
157 if (Dom.isAncestor(tabParent, target) ) {
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) ) {
174 tab.fireEvent(e.type, e);
180 * Returns the Tab instance at the specified index.
182 * @param {Integer} index The position of the Tab.
183 * @return YAHOO.widget.Tab
185 proto.getTab = function(index) {
186 return this.get('tabs')[index];
190 * Returns the index of given tab.
191 * @method getTabIndex
192 * @param {YAHOO.widget.Tab} tab The tab whose index will be returned.
195 proto.getTabIndex = function(tab) {
197 var tabs = this.get('tabs');
198 for (var i = 0, len = tabs.length; i < len; ++i) {
199 if (tab == tabs[i]) {
209 * Removes the specified Tab from the TabView.
211 * @param {YAHOO.widget.Tab} item The Tab instance to be removed.
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
221 if (index + 1 == tabCount) {
222 this.set('activeIndex', index - 1);
224 this.set('activeIndex', index + 1);
229 this._tabParent.removeChild( tab.get('element') );
230 this._contentParent.removeChild( tab.get('contentEl') );
231 this._configs.tabs.value.splice(index, 1);
236 * Provides a readable name for the TabView instance.
240 proto.toString = function() {
241 var name = this.get('id') || this.get('tagName');
242 return "TabView " + name;
246 * The transiton to use when switching between tabs.
247 * @method contentTransition
249 proto.contentTransition = function(newTab, oldTab) {
250 newTab.set('contentVisible', true);
251 oldTab.set('contentVisible', false);
255 * setAttributeConfigs TabView specific properties.
256 * @method initAttributes
257 * @param {Object} attr Hash of initial attributes
259 proto.initAttributes = function(attr) {
260 YAHOO.widget.TabView.superclass.initAttributes.call(this, attr);
262 if (!attr.orientation) {
263 attr.orientation = 'top';
266 var el = this.get('element');
268 if (!YAHOO.util.Dom.hasClass(el, this.CLASSNAME)) {
269 YAHOO.util.Dom.addClass(el, this.CLASSNAME);
273 * The Tabs belonging to the TabView instance.
277 this.setAttributeConfig('tabs', {
283 * The container of the tabView's label elements.
284 * @property _tabParent
289 this.getElementsByClassName(this.TAB_PARENT_CLASSNAME,
290 'ul' )[0] || _createTabParent.call(this);
293 * The container of the tabView's content elements.
294 * @property _contentParent
298 this._contentParent =
299 this.getElementsByClassName(this.CONTENT_PARENT_CLASSNAME,
300 'div')[0] || _createContentParent.call(this);
303 * How the Tabs should be oriented relative to the TabView.
304 * @config orientation
308 this.setAttributeConfig('orientation', {
309 value: attr.orientation,
310 method: function(value) {
311 var current = this.get('orientation');
312 this.addClass('yui-navset-' + value);
314 if (current != value) {
315 this.removeClass('yui-navset-' + current);
320 this.appendChild(this._tabParent);
327 * The index of the tab currently active.
328 * @config activeIndex
331 this.setAttributeConfig('activeIndex', {
332 value: attr.activeIndex,
333 method: function(value) {
334 this.set('activeTab', this.getTab(value));
336 validator: function(value) {
337 return !this.getTab(value).get('disabled'); // cannot activate if disabled
342 * The tab currently active.
344 * @type YAHOO.widget.Tab
346 this.setAttributeConfig('activeTab', {
347 value: attr.activeTab,
348 method: function(tab) {
349 var activeTab = this.get('activeTab');
352 tab.set('active', true);
353 this._configs['activeIndex'].value = this.getTabIndex(tab); // keep in sync
356 if (activeTab && activeTab != tab) {
357 activeTab.set('active', false);
360 if (activeTab && tab != activeTab) { // no transition if only 1
361 this.contentTransition(tab, activeTab);
363 tab.set('contentVisible', true);
366 validator: function(value) {
367 return !value.get('disabled'); // cannot activate if disabled
371 if ( this._tabParent ) {
372 _initTabs.call(this);
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);
389 * Creates Tab instances from a collection of HTMLElements.
394 var _initTabs = function() {
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) {
406 if (contentElements[i]) {
407 attr.contentEl = contentElements[i];
410 tab = new YAHOO.widget.Tab(tabs[i], attr);
413 if (tab.hasClass(tab.ACTIVE_CLASSNAME) ) {
414 this._configs.activeTab.value = tab; // dont invoke method
415 this._configs.activeIndex.value = this.getTabIndex(tab);
420 var _createTabViewElement = function(attr) {
421 var el = document.createElement('div');
423 if ( this.CLASSNAME ) {
424 el.className = this.CLASSNAME;
430 var _createTabParent = function(attr) {
431 var el = document.createElement('ul');
433 if ( this.TAB_PARENT_CLASSNAME ) {
434 el.className = this.TAB_PARENT_CLASSNAME;
437 this.get('element').appendChild(el);
442 var _createContentParent = function(attr) {
443 var el = document.createElement('div');
445 if ( this.CONTENT_PARENT_CLASSNAME ) {
446 el.className = this.CONTENT_PARENT_CLASSNAME;
449 this.get('element').appendChild(el);
454 var _getChildNodes = function(el) {
456 var childNodes = el.childNodes;
458 for (var i = 0, len = childNodes.length; i < len; ++i) {
459 if (childNodes[i].nodeType == 1) {
460 nodes[nodes.length] = childNodes[i];
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
472 * <p><strong>Event fields:</strong><br>
473 * <code><String> type</code> beforeActiveTabChange<br>
474 * <code><<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>>
475 * prevValue</code> the currently active tab<br>
476 * <code><<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>>
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
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><String> type</code> activeTabChange<br>
489 * <code><<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>>
490 * prevValue</code> the formerly active tab<br>
491 * <code><<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>>
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
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
504 * <p><strong>Event fields:</strong><br>
505 * <code><String> type</code> beforeOrientationChange<br>
506 * <code><String>
507 * prevValue</code> the current orientation<br>
508 * <code><String>
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
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><String> type</code> orientationChange<br>
521 * <code><String>
522 * prevValue</code> the former orientation<br>
523 * <code><String>
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
533 var Dom = YAHOO.util.Dom,
534 Event = YAHOO.util.Event;
537 * A representation of a Tab's label and content.
538 * @namespace YAHOO.widget
540 * @extends YAHOO.util.Element
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
546 var Tab = function(el, attr) {
548 if (arguments.length == 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
553 if (!el && !attr.element) {
554 el = _createTabElement.call(this, attr);
558 success: function(o) {
559 this.set('content', o.responseText);
561 failure: function(o) {
565 Tab.superclass.constructor.call(this, el, attr);
567 this.DOM_EVENTS = {}; // delegating to tabView
570 YAHOO.extend(Tab, YAHOO.util.Element);
571 var proto = Tab.prototype;
574 * The default tag name for a Tab's inner element.
575 * @property LABEL_INNER_TAGNAME
579 proto.LABEL_TAGNAME = 'em';
582 * The class name applied to active tabs.
583 * @property ACTIVE_CLASSNAME
587 proto.ACTIVE_CLASSNAME = 'selected';
590 * The class name applied to disabled tabs.
591 * @property DISABLED_CLASSNAME
593 * @default "disabled"
595 proto.DISABLED_CLASSNAME = 'disabled';
598 * The class name applied to dynamic tabs while loading.
599 * @property LOADING_CLASSNAME
601 * @default "disabled"
603 proto.LOADING_CLASSNAME = 'loading';
606 * Provides a reference to the connection request object when data is
607 * loaded dynamically.
608 * @property dataConnection
611 proto.dataConnection = null;
614 * Object containing success and failure callbacks for loading data.
615 * @property loadHandler
618 proto.loadHandler = null;
620 proto._loading = false;
623 * Provides a readable name for the tab.
627 proto.toString = function() {
628 var el = this.get('element');
629 var id = el.id || el.tagName;
634 * setAttributeConfigs TabView specific properties.
635 * @method initAttributes
636 * @param {Object} attr Hash of initial attributes
638 proto.initAttributes = function(attr) {
640 Tab.superclass.initAttributes.call(this, attr);
642 var el = this.get('element');
645 * The event that triggers the tab's activation.
646 * @config activationEvent
649 this.setAttributeConfig('activationEvent', {
650 value: attr.activationEvent || 'click'
654 * The element that contains the tab's label.
658 this.setAttributeConfig('labelEl', {
659 value: attr.labelEl || _getlabelEl.call(this),
660 method: function(value) {
661 var current = this.get('labelEl');
664 if (current == value) {
665 return false; // already set
668 this.replaceChild(value, current);
669 } else if (el.firstChild) { // ensure label is firstChild by default
670 this.insertBefore(value, el.firstChild);
672 this.appendChild(value);
678 * The tab's label text (or innerHTML).
682 this.setAttributeConfig('label', {
683 value: attr.label || _getLabel.call(this),
684 method: function(value) {
685 var labelEl = this.get('labelEl');
686 if (!labelEl) { // create if needed
687 this.set('labelEl', _createlabelEl.call(this));
690 _setLabel.call(this, value);
695 * The HTMLElement that contains the tab's content.
699 this.setAttributeConfig('contentEl', {
700 value: attr.contentEl || document.createElement('div'),
701 method: function(value) {
702 var current = this.get('contentEl');
705 if (current == value) {
706 return false; // already set
708 this.replaceChild(value, current);
718 this.setAttributeConfig('content', {
720 method: function(value) {
721 this.get('contentEl').innerHTML = value;
725 var _dataLoaded = false;
728 * The tab's data source, used for loading content dynamically.
732 this.setAttributeConfig('dataSrc', {
737 * Whether or not content should be reloaded for every view.
742 this.setAttributeConfig('cacheData', {
743 value: attr.cacheData || false,
744 validator: YAHOO.lang.isBoolean
748 * The method to use for the data request.
753 this.setAttributeConfig('loadMethod', {
754 value: attr.loadMethod || 'GET',
755 validator: YAHOO.lang.isString
759 * Whether or not any data has been loaded from the server.
763 this.setAttributeConfig('dataLoaded', {
765 validator: YAHOO.lang.isBoolean,
770 * Number if milliseconds before aborting and calling failure handler.
771 * @config dataTimeout
775 this.setAttributeConfig('dataTimeout', {
776 value: attr.dataTimeout || null,
777 validator: YAHOO.lang.isNumber
781 * Whether or not the tab is currently active.
782 * If a dataSrc is set for the tab, the content will be loaded from
787 this.setAttributeConfig('active', {
788 value: attr.active || this.hasClass(this.ACTIVE_CLASSNAME),
789 method: function(value) {
790 if (value === true) {
791 this.addClass(this.ACTIVE_CLASSNAME);
792 this.set('title', 'active');
794 this.removeClass(this.ACTIVE_CLASSNAME);
795 this.set('title', '');
798 validator: function(value) {
799 return YAHOO.lang.isBoolean(value) && !this.get('disabled') ;
804 * Whether or not the tab is disabled.
808 this.setAttributeConfig('disabled', {
809 value: attr.disabled || this.hasClass(this.DISABLED_CLASSNAME),
810 method: function(value) {
811 if (value === true) {
812 Dom.addClass(this.get('element'), this.DISABLED_CLASSNAME);
814 Dom.removeClass(this.get('element'), this.DISABLED_CLASSNAME);
817 validator: YAHOO.lang.isBoolean
821 * The href of the tab's anchor element.
826 this.setAttributeConfig('href', {
828 this.getElementsByTagName('a')[0].getAttribute('href', 2) || '#',
829 method: function(value) {
830 this.getElementsByTagName('a')[0].href = value;
832 validator: YAHOO.lang.isString
836 * The Whether or not the tab's content is visible.
837 * @config contentVisible
841 this.setAttributeConfig('contentVisible', {
842 value: attr.contentVisible,
843 method: function(value) {
845 this.get('contentEl').style.display = 'block';
847 if ( this.get('dataSrc') ) {
848 // load dynamic content unless already loading or loaded and caching
849 if ( !this._loading && !(this.get('dataLoaded') && this.get('cacheData')) ) {
850 _dataConnect.call(this);
854 this.get('contentEl').style.display = 'none';
857 validator: YAHOO.lang.isBoolean
861 var _createTabElement = function(attr) {
862 var el = document.createElement('li');
863 var a = document.createElement('a');
865 a.href = attr.href || '#';
869 var label = attr.label || null;
870 var labelEl = attr.labelEl || null;
872 if (labelEl) { // user supplied labelEl
873 if (!label) { // user supplied label
874 label = _getLabel.call(this, labelEl);
877 labelEl = _createlabelEl.call(this);
880 a.appendChild(labelEl);
885 var _getlabelEl = function() {
886 return this.getElementsByTagName(this.LABEL_TAGNAME)[0];
889 var _createlabelEl = function() {
890 var el = document.createElement(this.LABEL_TAGNAME);
894 var _setLabel = function(label) {
895 var el = this.get('labelEl');
896 el.innerHTML = label;
899 var _getLabel = function() {
901 el = this.get('labelEl');
910 var _dataConnect = function() {
911 if (!YAHOO.util.Connect) {
915 Dom.addClass(this.get('contentEl').parentNode, this.LOADING_CLASSNAME);
916 this._loading = true;
917 this.dataConnection = YAHOO.util.Connect.asyncRequest(
918 this.get('loadMethod'),
921 success: function(o) {
922 this.loadHandler.success.call(this, o);
923 this.set('dataLoaded', true);
924 this.dataConnection = null;
925 Dom.removeClass(this.get('contentEl').parentNode,
926 this.LOADING_CLASSNAME);
927 this._loading = false;
929 failure: function(o) {
930 this.loadHandler.failure.call(this, o);
931 this.dataConnection = null;
932 Dom.removeClass(this.get('contentEl').parentNode,
933 this.LOADING_CLASSNAME);
934 this._loading = false;
937 timeout: this.get('dataTimeout')
942 YAHOO.widget.Tab = Tab;
945 * Fires before the active state is changed.
946 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
947 * <p>If handler returns false, the change will be cancelled, and the value will not
949 * <p><strong>Event fields:</strong><br>
950 * <code><String> type</code> beforeActiveChange<br>
951 * <code><Boolean>
952 * prevValue</code> the current value<br>
953 * <code><Boolean>
954 * newValue</code> the new value</p>
955 * <p><strong>Usage:</strong><br>
956 * <code>var handler = function(e) {var previous = e.prevValue};<br>
957 * myTabs.addListener('beforeActiveChange', handler);</code></p>
958 * @event beforeActiveChange
962 * Fires after the active state is changed.
963 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
964 * <p><strong>Event fields:</strong><br>
965 * <code><String> type</code> activeChange<br>
966 * <code><Boolean>
967 * prevValue</code> the previous value<br>
968 * <code><Boolean>
969 * newValue</code> the updated value</p>
970 * <p><strong>Usage:</strong><br>
971 * <code>var handler = function(e) {var previous = e.prevValue};<br>
972 * myTabs.addListener('activeChange', handler);</code></p>
973 * @event activeChange
977 * Fires before the tab label is changed.
978 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
979 * <p>If handler returns false, the change will be cancelled, and the value will not
981 * <p><strong>Event fields:</strong><br>
982 * <code><String> type</code> beforeLabelChange<br>
983 * <code><String>
984 * prevValue</code> the current value<br>
985 * <code><String>
986 * newValue</code> the new value</p>
987 * <p><strong>Usage:</strong><br>
988 * <code>var handler = function(e) {var previous = e.prevValue};<br>
989 * myTabs.addListener('beforeLabelChange', handler);</code></p>
990 * @event beforeLabelChange
994 * Fires after the tab label is changed.
995 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
996 * <p><strong>Event fields:</strong><br>
997 * <code><String> type</code> labelChange<br>
998 * <code><String>
999 * prevValue</code> the previous value<br>
1000 * <code><String>
1001 * newValue</code> the updated value</p>
1002 * <p><strong>Usage:</strong><br>
1003 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1004 * myTabs.addListener('labelChange', handler);</code></p>
1005 * @event labelChange
1009 * Fires before the tab content is changed.
1010 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1011 * <p>If handler returns false, the change will be cancelled, and the value will not
1013 * <p><strong>Event fields:</strong><br>
1014 * <code><String> type</code> beforeContentChange<br>
1015 * <code><String>
1016 * prevValue</code> the current value<br>
1017 * <code><String>
1018 * newValue</code> the new value</p>
1019 * <p><strong>Usage:</strong><br>
1020 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1021 * myTabs.addListener('beforeContentChange', handler);</code></p>
1022 * @event beforeContentChange
1026 * Fires after the tab content is changed.
1027 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1028 * <p><strong>Event fields:</strong><br>
1029 * <code><String> type</code> contentChange<br>
1030 * <code><String>
1031 * prevValue</code> the previous value<br>
1032 * <code><Boolean>
1033 * newValue</code> the updated value</p>
1034 * <p><strong>Usage:</strong><br>
1035 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1036 * myTabs.addListener('contentChange', handler);</code></p>
1037 * @event contentChange
1041 YAHOO.register("tabview", YAHOO.widget.TabView, {version: "2.3.0", build: "442"});