/*------------------------------------------------------------------------------
Function		TabInterface()
Author:			Aaron Gustafson (aaron at easy-designs dot net)
Creation Date:	7 December 2006
Version:		0.4.3
Homepage:		http://github.com/easy-designs/tabinterface.js
License:		MIT License (see MIT-LICENSE)
Note:			If you change or improve on this script, please let us know by emailing the author (above) with a link to your demo page.
------------------------------------------------------------------------------*/
function TabInterface( _cabinet, _i )
{
	// Public Properties
	this.Version = '0.4.3'; // version

	// Private Properties
	var
		_active = false, // ID of the active "folder"
		// the tab list
		_index = document.createElement( 'ul' ),
		// prototype elements
		_cabinets =
		{
			div: document.createElement( 'div' ),
			li: document.createElement( 'li' )
		};

	// Private Methods
	function initialize()
	{
		var _id, node, nextNode,
		headers = [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ],
		i, len, _tag, rexp,
		arr, folder, tab, heading;
		
		// set the id
		_id = _cabinet.getAttribute( 'id' ) || 'folder-' + _i;
		if( !_cabinet.getAttribute( 'id' ) ) _cabinet.setAttribute( 'id', _id );
		
		// set the ARIA roles
		_cabinet.parentNode.setAttribute( 'role', 'application' );
		_cabinet.setAttribute( 'role', 'presentation' );
		_index.setAttribute( 'role', 'tablist' );
		_cabinets.div.setAttribute( 'role', 'tabpanel' );
		_cabinets.div.setAttribute( 'aria-hidden', 'true' );
		_cabinets.li.setAttribute( 'role', 'tab' );
		_cabinets.li.setAttribute( 'aria-selected', 'false' );
		_cabinets.li.setAttribute( 'tabindex', '-1' );

		// trim whitespace
		node = _cabinet.firstChild;
		while( node )
		{
			nextNode = node.nextSibling;
			if( node.nodeType == 3 && !( /\S/ ).test( node.nodeValue ) )
				_cabinet.removeChild( node );
			node = nextNode;
		}

		// find the first heading
		for( i=0, len=headers.length; i<len; i++ )
		{
			if( _cabinet.firstChild.nodeName.toLowerCase() == headers[i] )
			{
				_tag = headers[i];
				break;
			}
		}

		// flip it on
		addClassName( _cabinet, 'tabbed-on' );
		removeClassName( _cabinet, 'tabbed' );
		// establish the folders
		rexp = new RegExp( '<(' + _tag + ')', 'ig' );
		arr = _cabinet.innerHTML.replace( rexp, "||||<$1" ).split( '||||' );
		arr.shift();
		_cabinet.innerHTML = '';
		// add the index
		addClassName( _index, 'index' );
		_cabinet.appendChild( _index );
		// re-insert the chunks
		for( i=0, len=arr.length; i<len; i++ )
		{
			// build the div
			folder = _cabinets.div.cloneNode( true );
			addClassName( folder, 'folder' );
			folder.setAttribute( 'id', _id + '-' + i );
			folder.setAttribute( 'aria-labelledby', _id + '-' + i + '-tab' );
			folder.innerHTML = arr[i];
			_cabinet.appendChild( folder );
			// build the tab
			tab = _cabinets.li.cloneNode( true );
			tab.setAttribute( 'id', _id + '-' + i + '-tab' );
			tab.onclick = swap; // set the action
			tab.onkeydown = moveFocus;	// add the keyboard control
			heading = folder.getElementsByTagName( _tag )[0];
			if( heading.getAttribute( 'title' ) )
			{
				tab.innerHTML = heading.getAttribute( 'title' );
			}
			else
			{
				tab.innerHTML = heading.innerHTML;
				addClassName( heading, 'hidden' );
			}
			_index.appendChild( tab );
			// active?
			if( i === 0 )
			{
				addClassName( folder, 'visible' );
				folder.setAttribute( 'aria-hidden', 'false' );
				tab.setAttribute( 'aria-selected', 'true' );
				tab.setAttribute( 'tabindex', '0' );
				_active = folder.getAttribute( 'id' );
				addClassName( tab, 'active' );
			}
		}
	}
	function swap( e )
	{
		e = ( e ) ? e : event;
		var
		tab = e.target || e.srcElement,
		old_folder = document.getElementById( _active ),
		new_folder;
		tab = getTab( tab );
		new_folder = document.getElementById( tab.getAttribute( 'id' ).replace( '-tab', '' ) );
		removeClassName( document.getElementById( _active + '-tab' ), 'active' );
		removeClassName( old_folder, 'visible' );
		old_folder.setAttribute( 'aria-hidden', 'true' );
		addClassName( tab, 'active' );
		addClassName( new_folder, 'visible' );
		new_folder.setAttribute( 'aria-hidden', 'false' );
		_active = new_folder.getAttribute( 'id' );
	}
	function addClassName( e, c )
	{
		var classes = ( !e.className ) ? [] : e.className.split( ' ' );
		classes.push( c );
		e.className = classes.join( ' ' );
	}
	function removeClassName( e, c )
	{
		var classes = e.className.split( ' ' );
		for( var i=classes.length-1; i>=0; i-- )
		{
			if( classes[i] == c ) classes.splice( i, 1 );
		}
		e.className = classes.join( ' ' );
	}
	function getTab( tab )
	{
		while ( tab.nodeName.toLowerCase() != 'li' )
		{
			tab = tab.parentNode;
		}
		return tab;
	}
	function moveFocus( e )
	{
		e = ( e ) ? e : event;
		var
		tab = e.target || e.srcElement,
		key = e.keyCode || e.charCode,
		pass = true;
		tab = getTab( tab );
		switch ( key )
		{
			case 37: // left arrow
			case 40: // down arrow
				move( tab, 'previous', false );
				pass = false;
				break;
			case 39: // right arrow
			case 38: // up arrow
				move( tab, 'next', false );
				pass = false;
				break;
			case 36: // home
				move( tab, 'previous', true );
				pass = false;
				break;		
			case 35: // end
				move( tab, 'next', true );
				pass = false;
				break;		
			case 27: // escape
				tab.blur();
				pass = false;
				break;
			case 13: // enter
			case 32: // space
				swap( e );
				pass = false;
				break;
			default:
				pass = true;
				break;
		}
		if ( ! pass )
		{
			return cancel( e );
		}
	}
	function move( tab, direction, complete )
	{
		if ( complete )
		{
			if ( direction == 'previous' )
			{
				tab.parentNode.childNodes[0].focus();
			}
			else
			{
				tab.parentNode.childNodes[tab.parentNode.childNodes.length-1].focus();
			}
		}
		else
		{
			var target = direction == 'previous' ? tab.previousSibling
													: tab.nextSibling;
			if ( target )
			{
				target.focus();
			}
		}
	}
	function cancel( e )
	{
		if ( typeof e.stopPropagation == "function" )
		{
			e.stopPropagation();
		}
		else
			if ( typeof e.cancelBubble != "undefined" )
			{
				e.cancelBubble = true;
			}
		if ( e.preventDefault )
		{
			e.preventDefault();
		}
		return false;
	}

	// start it up
	initialize();
};


//*
//* (c)2006 Dean Edwards/Matthias Miller/John Resig
//* Special thanks to Dan Webb's domready.js Prototype extension
//* and Simon Willison's addLoadEvent
//*
//* For more info, see:
//* http://dean.edwards.name/weblog/2006/06/again/
//* http://www.vivabit.com/bollocks/2006/06/21/a-dom-ready-extension-for-prototype
//* http://simon.incutio.com/archive/2004/05/26/addLoadEvent
//*
//* Thrown together by Jesse Skinner (http://www.thefutureoftheweb.com/)
//*
//*
//* To use: call addDOMLoadEvent one or more times with functions, ie:
//*
//*	function something() {
//*		// do something
//*	}
//*	addDOMLoadEvent(something);
//*
//*	addDOMLoadEvent(function() {
//*		// do other stuff
//*	});

function addDOMLoadEvent(func)
{
	if (!window.__load_events)
	{
		var init = function ()
		{
			// quit if this function has already been called
			if (arguments.callee.done) return;
	
			// flag this function so we don't do the same thing twice
			arguments.callee.done = true;
	
			// kill the timer
			if (window.__load_timer)
			{
				clearInterval(window.__load_timer);
				window.__load_timer = null;
			}
	
			// execute each function in the stack in the order they were added
			for (var i=0;i < window.__load_events.length;i++)
			{
				window.__load_events[i]();
			}
			window.__load_events = null;
		};

		// for Mozilla/Opera9
		if (document.addEventListener)
		{
			document.addEventListener("DOMContentLoaded", init, false);
		}

		// for Internet Explorer
		/*@cc_on @*/
		/*@if (@_win32)
		document.write("<scr"+"ipt id=__ie_onload defer src=//0><\/scr"+"ipt>");
		var script = document.getElementById("__ie_onload");
		script.onreadystatechange = function() {
		if (this.readyState == "complete") {
						init(); // call the onload handler
		}
		};
		/*@end @*/

		// for Safari
		if (/WebKit/i.test(navigator.userAgent))
		{ // sniff
			window.__load_timer = setInterval(function()
			{
				if (/loaded|complete/.test(document.readyState))
				{
					init(); // call the onload handler
				}
			}, 10);
		}

		// for other browsers
		window.onload = init;

		// create event function stack
		window.__load_events = [];
	}

	// add function to event stack
	window.__load_events.push(func);
}

// Load up TabInterface
if( document.getElementById && document.getElementsByTagName && document.createElement )
{
	var cabinets = Array();
		addDOMLoadEvent( function()
		{
			var collection = document.getElementsByTagName( '*' );
			var cLen = collection.length;
			for( var i=0; i<cLen; i++ )
			{
				if( collection[i] && /\s*tabbed\s*/.test( collection[i].className ) )
				{
					cabinets.push( new TabInterface( collection[i], i ) );
				}
			}
		}
		);
}
