/**
 * Tabs
 * Creates a set of tabs as an instance
 *
 * version   1.0
 * author    Fred LeBlanc
 * modified  2007-03-15
 *
 * change log
 *  v1.0   [2007-03-15] - Stable release
 *  v0.5   [2007-03-15] - Placed tab ul inside of a div for better styling
 *  v0.4   [2007-02-28] - Added functionality to trigger custom events
 *  v0.3   [2007-02-28] - Expanded addTab to define normal and highlight class names
 *  v0.2   [2007-02-28] - Added functionality for creating and loading tabs
 *  v0.1   [2007-02-27] - Initial build
 */

/**
 * constructor
 * since v0.1
 *
 * return void
 */
function TabSet() {
	this.tabs          = [];
	this.currentTab    = 0;
	this.locked        = false;
	this.element       = null;
	this.instanceName  = (arguments[0] != undefined) ? arguments[0] : this.generateRandomString();
	this.listeners     = [];
	
	this.config        = {
		showEmptyTabs : false,
		startingTab   : 0
		};
}

/**
 * adds an event listener
 * since v0.4
 *
 * param string  name     Name of tab to add listener to
 * param string  trigger  Trigger causing action
 * param mixed   action   Action to perform
 * return void
 */
TabSet.prototype.addListener = function(name, trigger, action) {
	this.enforceLock();
	
	this.removeListener(name, trigger);
	this.listeners.push({name: name, trigger: trigger, action: action});
}

/**
 * removes an event listener
 * since v0.4
 *
 * param string  name     Name of tab to remove listener from
 * param string  trigger  Trigger to remove
 * return boolean
 */
TabSet.prototype.removeListener = function(name, trigger) {
	var i;
	
	this.enforceLock();
	
	i = this.listeners.length;
	
	while (i--) {
		if (this.listeners[i].name == name && this.listeners[i].trigger == trigger) {
			this.listeners.splice(i, 1);
			return true;
		}
	}
	
	return false;
}

/**
 * broadcasts an event
 * since v0.4
 *
 * param string  type   Type of event triggered
 * param object  event  Event that occurred
 * return void
 */
TabSet.prototype.broadcastEvent = function(type, event) {
	var i;
	
	name = event.target;	
	
	for (i = 0; i < this.listeners.length; i++) {
		if (this.listeners[i].name == name && this.listeners[i].trigger == type) {
			eval(this.listeners[i].action);	
		}
	}
}

/**
 * prints tabs, called by use when finished configuring
 * since v0.2
 *
 * param string  id  ID of element where tabs should be placed
 * return void
 */
TabSet.prototype.placeTabs = function(id) {
	this.element = this.getElementById(id);
	
	// lock this object
	this.locked = true;
	
	if (this.config.showEmptyTabs == false) {
		this.removeEmptyTabs();
	}
	
	this.element.appendChild(this.createTabs());
	this.selectTab(this.config.startingTab);
}

/**
 * selects a tab
 * since v0.2
 *
 * param string  key  Key of tab to select
 * return void
 */
TabSet.prototype.selectTab = function(key) {
	this.broadcastEvent('onTabSelected', {key: key, target: this.tabs[key].name, type: 'onTabLoaded'});	
	if (this.tabs[key] == undefined) {
		throw ('There is no tab in position ' + key + '.');	
	}
	
	clearInterval(rotateInterval);
	this.highlightTab(key);
	this.loadContent(key);
	this.broadcastEvent('onTabLoaded', {key: key, target: this.tabs[key].name, type: 'onTabLoaded'});
}

/**
 * selects a tab through auto-rotate
 * since v1.0
 *
 * param string  key  Key of tab to rotate-select
 * return void
 */
TabSet.prototype.rotateSelectTab = function(key) {
	this.broadcastEvent('onTabSelected', {key: key, target: this.tabs[key].name, type: 'onTabLoaded'});	
	if (this.tabs[key] == undefined) {
		throw ('There is no tab in position ' + key + '.');	
	}
	
	this.highlightTab(key);
	this.loadContent(key);
	this.broadcastEvent('onTabLoaded', {key: key, target: this.tabs[key].name, type: 'onTabLoaded'});
}

/**
 * highlights a given tab, setting the rest to off
 * since v0.2
 *
 * param integer  key  Key in tabs array of tab to highlight
 * return void
 */
TabSet.prototype.highlightTab = function(key) {
	var i, liTab;
	
	for (i = 0; i < this.tabs.length; i++) {
		liTab = this.getElementById(this.instanceName + '_tab_' + i);
		liTab.className = (i == key) ? this.tabs[i].highlight : this.tabs[i].normal;
	}
}

/**
 * loads tab content into the content stage
 * since v0.2
 *
 * param integer  key  Key in tabs array of content to load
 * return void
 */
TabSet.prototype.loadContent = function(key) {
	var divContent;
	
	divContent            = this.getElementById(this.instanceName + '_content');
	divContent.innerHTML  = this.tabs[key].content;
}

/**
 * gets element by id
 * since v0.2
 *
 * param string  id  ID of element to get
 * return element
 */
TabSet.prototype.getElementById = function(id) {
	var element;
	
	element = document.getElementById(id);
	
	if (element == undefined) {
		throw ('Could not find element with id ' + id + '.');	
	}
	
	return element;
}

/**
 * creates tabs
 * since v0.2
 *
 * return void
 */
TabSet.prototype.createTabs = function() {
	var divContainer, ulTabs, liTab, aTab, divContent, uberTextNode, i;
	
	divContainer  = document.createElement('div');
	divContent    = document.createElement('div');
	divTabs       = document.createElement('div');
	divClear      = document.createElement('div');
	ulTabs        = document.createElement('ul');
	
	// format ul
	ulTabs.className = 'tabs_tab_container';
	
	// create li's, a's
	for (i = 0; i < this.tabs.length; i++) {
		liTab         = document.createElement('li');
		aTab          = document.createElement('a');
		uberTextNode  = document.createTextNode(this.tabs[i].name);
		
		// format li
		liTab.className = this.tabs[i].normal;
		liTab.setAttribute('id', this.instanceName + '_tab_' + i);
		
		// format a
		aTab.href = '#';
		aTab.setAttribute('tabKey', i);
		
		with (this) {
			aTab.onclick = function() {
				selectTab(this.getAttribute('tabKey'));
				return false;
			}
		}

		aTab.appendChild(uberTextNode);
		liTab.appendChild(aTab);
		ulTabs.appendChild(liTab);
	}
	
	// format divs
	divContent.appendChild(document.createTextNode(''));
	divContent.className = 'tabs_content_area';
	divContent.setAttribute('id', this.instanceName + '_content');
	
	divTabs.className = 'tabs_tabs_area';
	divTabs.appendChild(ulTabs);
	
	divClear.className = 'tabs_clear';
	divTabs.appendChild(divClear);
	
	divContainer.appendChild(divTabs);
	divContainer.appendChild(divContent);
	return divContainer;
}

/**
 * generates a random string
 * since v0.2
 *
 * param integer  length  Length of string to generate
 * return string
 */
TabSet.prototype.generateRandomString = function() {
	var charSet, output, i, length, randomNumber;
	
	charSet = 'abcdefghijklmnopqrstuvwxyz0123456789';
	length  = (arguments[0] == undefined) ? 6 : arguments[0];
	output  = '';
	
	for (i = 0; i < length; i++) {
		randomNumber  = Math.floor(Math.random() * charSet.length);
		output        = output + charSet.substr(randomNumber, 1);
	}
	
	return output;
}

/**
 * adds a tab to tab set
 * since v0.1
 *
 * param string  name       Name of tab to add
 * param string  content    Content of tab to add
 * param string  normal     (Optional) Class name for normal display
 * param string  highlight  (Optional) Class name for highlight display
 * return void
 */
TabSet.prototype.addTab = function(name, content) {
	var tab, norma, highlight;
	
	this.enforceLock();
	
	normal     = (arguments[2] != undefined) ? arguments[2] : 'tabs_normal';
	highlight  = (arguments[3] != undefined) ? arguments[3] : 'tabs_highlight';
	
	// throw error alert
	if (this.getTabPosition(name) != -1) {
		throw ('Tab ' + name + ' already exists.');
	}
	
	tab = { name: name, content: content, normal: normal, highlight: highlight };
	this.tabs.push(tab);
}

/**
 * removes empty tabs from the tab set
 * since v0.2
 *
 * return void
 */
TabSet.prototype.removeEmptyTabs = function() {
	var i, newTabList;
	
	newTabList = [];
	
	for (i = 0; i < this.tabs.length; i++) {
		if (this.tabs[i].content.length > 0) {
			newTabList.push(this.tabs[i]);
		}
	}
	
	this.tabs = newTabList;
}

/**
 * checks if object is locked, throws error if it is
 * since v0.2
 *
 * return void
 */
TabSet.prototype.enforceLock = function() {
	if (this.locked == true) {
		throw ('Cannot make changes, object is locked.');	
	}
}

/**
 * returns tab array position if set, -1 if not
 * since v0.1
 *
 * param string  name  Name of tab to check for
 * return integer
 */
TabSet.prototype.getTabPosition = function(name) {
	var i;
	
	for (i = 0; i < this.tabs.length; i++) {
		if (this.tabs[i].name == name) {
			return i;	
		}
	}
	
	return -1;
}

/**
 * sets configuration variable
 * since v0.1
 *
 * param string  key    Key of configuration variable to set
 * param mixed   value  Value of configuration option
 * return void
 */
TabSet.prototype.setConfig = function(key, value) {
	this.enforceLock();
	
	if (eval('this.config.' + key) == undefined) {
		throw ('Could not set configuration variable ' + key + '. Invalid key.');	
	}
	
	eval('this.config.' + key + ' = ' + value);
}

/**
 * Tab Auto-Rotate
 * 
 */
var rotateInterval;
var rotationKey = 0;

function startAutoRotate(tabSet) {
	rotateInterval = setInterval(
		function() {
			rotationKey++;
			rotationKey = (rotationKey % 4);
			tabSet.rotateSelectTab(rotationKey);
		}, 6000);
}