/*
 * Skiviez Tabs
 *
 * Nicholas Piasecki <nick@skiviez.com>
 *
 * Library Dependencies:
 *   * prototype.js 1.5.1 or later (http://prototype.conio.net)
 *
 * Tested Browsers:
 *   * Firefox 2.0.0.4 [WinVista]
 *   * Firefox 2.0.0.4 [MacTiger]
 *   * IE 6.0 [WinXpSp2]
 *   * IE 7.0 [WinVista]
 *   * Opera 9.21 [WinVista]
 *       - On initial load when background image is not in cache, the tabs
 *         are displayed in a corrupt fashion. When mousing over, the problem
 *         resolves itself. Not critical; this is a bug in the Opera browser.
 *   * Opera 9.21 [MacTiger/Intel]
 *   * Safari 2.0.4 (419.3) [MacTiger/Intel]
 *   * Safari 3.0.1 (522.12.2) [WinVista]
 *
 * Revisions:
 *   * 2007-06-17
 *       - Initial release
 *
 * License and Credits:
 *     The suggested CSS is largely inspired by the work of Klaus Hartl
 *   (http://stilbuero.de/jquery/tab). The use of a <dl> was my own
 *   inspiration.
 *     Skiviez, Inc. maintains the copyright (2007) for this particular
 *   JavaScript implementation. You are free to use this implementation on your
 *   Web site, personal or commercial, provided that you include this copyright
 *   notice.
 *
 * Converts a definition list (<dl>) into a set of hoverable tabs. 
 *
 * By using a definition list, users with JavaScript disabled in their browsers
 * see a normally formatted definition list. Additionally, the source code
 * required for these tabs is unobtrusive and semantically valid; only a
 * combination of <dl>, <dt>, and <dd> tags is required.
 *
 * Internally, the script works by searching for all <dl>s on the page with the
 * class "replace-with-ski-tabs". The <dt>s for such <dl>s are floated to the
 * left, and their contents (which must be text nodes; no block elements are
 * allowed here) are wrapped in <a> and <span> tags, which provides some nice
 * hooks for styling with CSS. The "replace-with-ski-tabs" class is replaced
 * with the class "ski-tabs". Some suggested CSS is available in the
 * Ski-Tabs.css document.
 *
 * The <dt>s become the tabs, and the <dd>s are the tab content. However, the
 * placement of the <dd>s in the source code markup make it difficult to style
 * the tabs as floated to the left. To work around this, the script hides all
 * of the <dd>s and dynamically places the inner HTML of the selected tab's
 * <dd> in a new <dd> with class "ski-tabs-content" that is dynamically created
 * when the script is run.
 *
 * You can specify which tab is selected when the page is loaded by applying
 * the "selected" class to a <dt> element. Otherwise, the first tab is the one
 * that is selected.
 *
 */

var CSkiTabs = Class.create();

CSkiTabs.prototype = {
	
	// Constants and Configuration
	
	REPLACED_CLASS_NAME: 'ski-tabs',
	
	REPLACEMENT_CLASS_NAME: 'replace-with-ski-tabs',
	
	SELECTED_CLASS_NAME: 'selected',
	
	// Members
	
	oDdContent: null,
	
	oDtSelected: null,
				
	// Methods
	
	_findAssociatedDd: function( oDt ) {
		
		var oElem = oDt;
		while ( oElem = oElem.nextSibling ) {
			
			if ( oElem.tagName && oElem.tagName.match( /DD/i ) ) {
				return oElem;
			}
		}
		
		return null;
	},
	
	_findFirstDt: function( oDl ) {
		
		for (var i = 0; i < oDl.childNodes.length; ++i ) {
			var oElem = oDl.childNodes[i];

			if (oElem.tagName && oElem.tagName.match( /DT/i ) ) {
				return oElem;	
			}	
		}
		
		return null;
		
	},
	
	initialize: function( oDl ) {
		
		oDl.addClassName( this.REPLACED_CLASS_NAME );
		oDl.removeClassName( this.REPLACEMENT_CLASS_NAME );
		
		// Create a content area for the selected tab
		this.oDdContent = $( document.createElement( 'dd' ) );
		this.oDdContent.addClassName( 'ski-tabs-content' );
		oDl.appendChild( this.oDdContent );
		
		$A( oDl.childNodes ).each(this._setupChild.bind(this));
		
		if ( !this.oDtSelected ) {
			this.select( this._findFirstDt( oDl ) );
		}
		
	},
	
	_onDtSelected: function( oEvt ) {
		Event.stop( oEvt );
		
		var oDt = Event.findElement( oEvt, 'dt' );
		if ( oDt ) {
			this.select( oDt );
		}
	},
	
	select: function( oDt ) {
		if ( oDt ) {
			this.unselect( this.oDtSelected );
			oDt.addClassName( this.SELECTED_CLASS_NAME );
			this.oDdContent.innerHTML =
				oDt.oDd ? oDt.oDd.innerHTML : '';
			this.oDtSelected = oDt;			
		}	
	},
	
	_setupChild: function( oElem, iIdx ) {
		
		if ( oElem != this.oDdContent && oElem.tagName ) {
			
			// For dt tags, add an anchor and a span,
			// associate with the next dd tag, and if it's
			// the first tag, mark it as selected
			if ( oElem.tagName.match( /DT/i ) ) {
			
				// Give the tab (a <dt>) a reference to its associated <dd>
				oElem.oDd =
					this._findAssociatedDd( oElem );
					
				// Wrap in <a> and <span> for CSS styling hooks
				var sText = oElem.firstChild.nodeValue;
				var oWrapperA = document.createElement( 'a' );
				var oWrapperSpan = document.createElement( 'span' );
				oWrapperSpan.appendChild( document.createTextNode( sText ) );
				oWrapperA.appendChild( oWrapperSpan );
				oElem.replaceChild( oWrapperA, oElem.firstChild );

				// Listen to mouseover to select the tab as the user mouses
				// over it
				Event.observe(
					oElem,
					'mouseover',
					this._onDtSelected.bindAsEventListener(this),
					false);
				
				if ( oElem.hasClassName( this.SELECTED_CLASS_NAME ) ) {
					this.select( oElem );	
				}
				
			} else if ( oElem.tagName.match( /DD/i ) ) {
				oElem.style.display = 'none';
			}
		}
							
	},
	
	unselect: function( oDt ) {
		if ( oDt ) {
			oDt.removeClassName( this.SELECTED_CLASS_NAME );
		}
	}
	
};

/******************************************************************************/

Event.observe(
	window,
	'load',
	function() {
		$$( 'dl.replace-with-ski-tabs' ).each(
			function( oElem, iIdx ) {
				new CSkiTabs(oElem);	
			}
		);
	},
	false);
