Help:Tabs/js

ShoutWiki — express yourself and be heard!
Jump to: navigation, search
/**
 * JS Tab System, jacked and hacked from the jsprefs in wikibits.js
 *
 * Original code by Dantman
 * Refactored a bit by Jack Phoenix on 11 April 2014
 * Support for linking to a particular tab by MatmaRex on 30 December 2016.
 * @note Should be rewritten to properly use jQuery like how mediawiki.special.preferences.js does.
 */
var TabSystem = {
	/**
	 * @property {boolean}
	 * Is the user's browser a KHTML-based one (usually, but not always, Konqueror)?
	 */
	isKHTML: ( navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled ) ),
 
	/**
	 * @property {boolean}
	 * Is the user's browser Opera?
	 */
	isOpera: navigator.userAgent.toLowerCase().indexOf( 'opera' ) != -1,
 
	/*
		Written by Jonathan Snook, http://www.snook.ca/jonathan
		Add-ons by Robert Nyman, http://www.robertnyman.com
		Author says "The credit comment is all it takes, no license. Go crazy with it!:-)"
		From http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/
	*/
	getElementsByClassName: function( oElm, strTagName, oClassNames ) {
		var arrReturnElements = [];
		if ( typeof oElm.getElementsByClassName == 'function' ) {
			/* Use a native implementation where possible FF3, Saf3.2, Opera 9.5 */
			var arrNativeReturn = oElm.getElementsByClassName( oClassNames );
			if ( strTagName == '*' ) {
				return arrNativeReturn;
			}
			for ( var h = 0; h < arrNativeReturn.length; h++ ) {
				if ( arrNativeReturn[h].tagName.toLowerCase() == strTagName.toLowerCase() ) {
					arrReturnElements[arrReturnElements.length] = arrNativeReturn[h];
				}
			}
			return arrReturnElements;
		}
 
		var arrElements = ( strTagName == '*' && oElm.all ) ? oElm.all : oElm.getElementsByTagName( strTagName );
		var arrRegExpClassNames = [];
		if ( typeof oClassNames == 'object' ) {
			for ( var i = 0; i < oClassNames.length; i++ ) {
				arrRegExpClassNames[arrRegExpClassNames.length] =
					new RegExp( "(^|\\s)" + oClassNames[i].replace( /\-/g, "\\-" ) + "(\\s|$)" );
			}
		} else {
			arrRegExpClassNames[arrRegExpClassNames.length] =
				new RegExp( "(^|\\s)" + oClassNames.replace( /\-/g, "\\-" ) + "(\\s|$)" );
		}
 
		var oElement;
		var bMatchesAll;
		for ( var j = 0; j < arrElements.length; j++ ) {
			oElement = arrElements[j];
			bMatchesAll = true;
			for ( var k = 0; k < arrRegExpClassNames.length; k++ ) {
				if ( !arrRegExpClassNames[k].test( oElement.className ) ) {
					bMatchesAll = false;
					break;
				}
			}
			if ( bMatchesAll ) {
				arrReturnElements[arrReturnElements.length] = oElement;
			}
		}
 
		return arrReturnElements;
	},
 
	/**
	 * Main function that performs all the magic on all div elements that have
	 * class="tab" and are inside a div that has class="tabcontainer".
	 */
	main: function() {
		var tabcontainers = TabSystem.getElementsByClassName( document, 'div', 'tabcontainer' );
		for ( var tc = 0; tc < tabcontainers.length; tc++ ) {
			if ( !tabcontainers[tc] || !document.createElement ) {
				return;
			}
			if ( tabcontainers[tc].nodeName.toLowerCase() == 'a' ) {
				return; // Occasional IE problem
			}
 
			tabcontainers[tc].className += ' jstabs';
 
			var sections = [];
			var children = tabcontainers[tc].childNodes;
			var seci = 0;
 
			for ( var i = 0; i < children.length; i++ ) {
				if ( children[i].className && children[i].className.match( /tab/i ) ) {
					children[i].id = 'tabsection-' + seci + '-' + tc;
					children[i].className += ' tabsection';
					// Opera and KHTML-based browsers get a special class
					if ( TabSystem.isOpera || TabSystem.isKHTML ) {
						children[i].className += ' tabsection operatabsection';
					}
					var legends = TabSystem.getElementsByClassName( children[i], 'div', 'tab' );
					sections[seci] = {};
					legends[0].className = 'mainTab';
					if ( legends[0] && legends[0].firstChild.nodeValue ) {
						sections[seci].text = legends[0].firstChild.nodeValue;
					} else {
						sections[seci].text = '# ' + seci;
					}
					sections[seci].secid = children[i].id;
					seci++;
					if ( sections.length != 1 ) {
						children[i].style.display = 'none';
					} else {
						var selectedid = children[i].id;
					}
				}
			}
 
			var toc = document.createElement( 'ul' );
			toc.className = 'tabtoc';
			toc.id = 'tabtoc-' + tc;
			toc.selectedid = selectedid;
 
			for ( i = 0; i < sections.length; i++ ) {
				var li = document.createElement( 'li' );
				if ( i === 0 ) {
					li.className = 'selected';
				}
				var a = document.createElement( 'a' );
				a.href = '#' + sections[i].secid;
				a.appendChild( document.createTextNode( sections[i].text ) );
				a.secid = sections[i].secid;
				li.appendChild( a );
				toc.appendChild( li );
				// Capture current value of variables in the closure
				( function ( i, a ) {
					$( window ).on( 'hashchange', function () {
						if ( location.hash === '#' + sections[i].secid ) {
							TabSystem.uncoverTabSection( toc, a );
						}
					} )
					if ( location.hash === '#' + sections[i].secid ) {
						TabSystem.uncoverTabSection( toc, a );
					}
				} )( i, a );
			}
 
			tabcontainers[tc].parentNode.insertBefore( toc, tabcontainers[tc] );
		}
	},
 
	/**
	 * Show the contents of a tab section when the user clicks on the tab.
	 *
	 * @return {boolean} Always false
	 */
	uncoverTabSection: function( ul, a ) {
		var oldsecid = ul.selectedid;
		var newsec = document.getElementById( a.secid );
		if ( oldsecid != a.secid ) {
			document.getElementById( oldsecid ).style.display = 'none';
			newsec.style.display = 'block';
			ul.selectedid = a.secid;
			var lis = ul.getElementsByTagName( 'li' );
			for ( var i = 0; i < lis.length; i++ ) {
				lis[i].className = '';
			}
			a.parentNode.className = 'selected';
		}
		return false;
	}
};
 
// Attach the onload handler using jQuery.
$( function() {
	TabSystem.main();
} );