//-----------------------------
//   Tab Functions
//----------------------------- 

//variable to store current active tab for setTimeout operations
var currActive = "";

//Tab object constructor
//		label - The label of the tab itself,  If arg is a string, will use that string
//					as the label.  If arg is a DOM Element, will use that element.
//		content - Content to load/display for each tab.  Can be of the following types:
//				string - sets the content div innerHTML to that string
//				DOM Element - uses that DOM Element for the content (display method is defined in TabSet constructor)
//				function - calls the function reference to display content.  If displayMode in TabSet constructor 
//						is set to "none", function will be called each tab click.  If displayMode in TabSet 
//						constructor is anything else, function will be called ONLY to initally populate the content
//		optionalArgs - Associative Array of optional custom attributes to attach to each tab.  
//			i.e. { canClose : true, searchTerm: "leepfrog" } will set Tab.canClose = true and Tab.searchTerm to "leepfrog"
function Tab(label, content, optionalArgs) {
	this.TabSet;
	this.tabDiv;
	this.content;
	this.label = label;
	//variable to remember the vertical scroll position for each tab
	this.scrollTop = 0;
	
	this.contentDiv = document.createElement("div");
	//set optional arguments
	if(typeof optionalArgs != "undefined") {
		for(var i in optionalArgs) {
			this[i] = optionalArgs[i];
		}
	}
	
	if(typeof content == "function") {
		this.content = content;
	} else
		this.setTabContent(content);
		
	this.isActive = false;
}

//Tab Set Object - pass in an array of tabs to physically create and draw the tabs.
//		tabs - an array of Tab objects to add to the TabSet
//		activeIndex - Index of tab to display initially when TabSet loads.
//		displayMode - How to display the tab content, values:
//						"dom" - use dome calls to append/remove physical elements
//						"hide" - use the style.display property to show/hide the elements
//						"none" - manage content externally (via content function in Tab constructor)
//		parentEl	- Element to append the Tabs to.  Can be a string (element id) or a dom element.
function TabSet(tabs, activeIndex, displayMode, parentEl) {
	var self = this;
	this.tabs = tabs;
	this.currActive;
	this.tabScrollTimer = 0;
	this.displayMode = "dom";
	if(displayMode != "undefined" && displayMode != ""  && displayMode != null)
		this.displayMode = displayMode;

	//create a div to wrp around the whole tab set
	var main = document.createElement("div");
	main.className = "tabSet";
	//main.style.width = "100%";
	main.style.overflow = "hidden";
	
	//create the container div for the actual tabs
	var container = document.createElement("div");
	container.className = 'tabContainer';
	container.style.overflow = "hidden";
	container.style.width = "100%";

	//add nobr element to avoid tabs breaking if window is shrunk
	var nobr = document.createElement("nobr");
	
	//create a navigation div for scrolling tabs
	function end(ev) {
		var el = window.event ? window.event.srcElement : ev.target;
		el.blur();
		self.endScroll();
		Tab.stopEvent(ev);
	}
	var nav = document.createElement("div");
	nav.className = "tabNav";
	nav.style.display = "none";
	nav.style.cssFloat = nav.style.styleFloat = "right";
	
	var left = document.createElement("a");
	left.href = "#";
	left.innerHTML = '<img src="/bluetab/lefttabarrow.gif">';
	left.onmousedown = function() { self.scroll(-1); }
	Tab.addEvent(left, "mouseup", end);
	Tab.addEvent(left, "mouseout", end);
	Tab.addEvent(left, "click", end);
	var right = document.createElement("a");
	right.href = "#";
	right.innerHTML = '<img src="/bluetab/righttabarrow.gif">';
	right.onmousedown = function() { self.scroll(1); }
	Tab.addEvent(right, "mouseup", end);
	Tab.addEvent(right, "mouseout", end);
	Tab.addEvent(right, "click", end);
	nav.appendChild(left);
	nav.appendChild(right);
	
	function resizeMe() {
		self.displayTabNav();
	}
	//add an event to handle displaying/hiding navigation on window resize
	Tab.addEvent(window, "resize", resizeMe);
	Tab.addEvent(window, "load", resizeMe);
	
	nobr.appendChild(container);
	main.appendChild(nav);
	main.appendChild(nobr);
	
	//create the content div for the content of each tab
	var content = document.createElement("div");
	content.className = 'tabContent';
	
	this.tabContainerEl = container;	
	this.mainTabEl = main;	
	this.mainContentEl = content;	
	this.navEl = nav;
	//track the overall width of the TabSet - will be useful for scrolling later.
	var strLen = 0;
	//Loop through the tabs Array and draw out the tab interface
	for (i=0; i < tabs.length; i++) {
		tabs[i].TabSet = this;
		tabs[i].drawTab();
		strLen += tabs[i].label.length;
	}
	
	if(this.displayMode == "none") {
		
	} else {
		if(this.displayMode == "dom") {
			content.appendChild(document.createTextNode(""));
		} else if (this.displayMode == "hide") {
			for (i=0; i < tabs.length; i++) {
				content.appendChild(tabs[i].contentDiv);
				tabs[i].contentDiv.style.display = "none";
			}
		}
		//add the content div
		main.appendChild(content);
	}
	
	
	if(typeof parentEl == "string") 
		document.getElementById(parentEl).appendChild(main);
	else if (typeof parentEl == "object")
		object.appendChild(main);
	else
		document.body.appendChild(main);
	
	if(typeof activeIndex != "undefined" && activeIndex == -1 && tabs.length) {
		this.currActive = tabs[0];
		this.currActive.tabDiv.className = 'tabActive';
	} else if(typeof activeIndex != "undefined" && parseInt(activeIndex) < tabs.length) {
		var idx = parseInt(activeIndex);
		if(typeof processExternal == "function")
			processExternal(tabs[idx]);
		tabs[idx].showTab();
		this.currActive = tabs[idx];
	} else if(tabs.length) {
		//if tabs, set the initial content
		//call function to do external processing on tab activation
		if(typeof processExternal == "function")
			processExternal(tabs[0]);
		tabs[0].showTab();
		this.currActive = tabs[0];
	}

	//Add a scroll wheel handler
	//Tab.addEvent(this.tabContainerEl, "mousewheel", function(event) { self.tabMouseWheel(event); } );
	
	return this;
}

TabSet.prototype.setTabContent = function(content) {
	this.currActive.setTabContent(content);
}

//set the contents of the content div for an individual tab
// content: must be an object or a string
Tab.prototype.setTabContent = function(content) {

	if(typeof content == "string") {
		this.contentDiv.innerHTML = content;
	} else if (typeof content == "object") {
		//remove all child nodes then add the content node
		while(this.contentDiv.hasChildNodes())
			this.contentDiv.removeChild(this.contentDiv.firstChild);
			
		//this.contentDiv.appendChild(content);
		//this.contentDiv.parentNode.replaceChild(content, this.contentDiv);
		this.contentDiv = content.parentNode.removeChild(content);
	}
}

//show a tab
Tab.prototype.showTab = function() {
	var content = this.TabSet.mainContentEl;
	if (typeof content == "undefined") {
		alert("No Content Defined!");
		return;
	}
	
	if(this == this.TabSet.currActive)
		return;
	
	if(this.TabSet.currActive)
		this.TabSet.currActive.tabDiv.className = 'tab tabInactive';
	else 
		this.TabSet.currActive = this;
	this.tabDiv.className = 'tab tabActive';
	
	//function to process external processing needs on the current tab
	if(typeof processExternal == "function")
		processExternal(this);
	if(this.TabSet.displayMode == "none") {
		if(typeof this.content == "function") {
			this.content(this);
		} else {
			alert("content is not a function\nWhat do I do here?");
		}
	} else {
		//remember the scroll position
		this.TabSet.currActive.scrollTop = content.scrollTop;
		
		//if content is a function and contentDiv is empty, display "Loading" message 
		// and delay evaluation of content function
		if(this.contentDiv.childNodes.length == 0 && typeof this.content == "function") {
			var h3 = document.createElement("h3");
			h3.appendChild(document.createTextNode("Loading Tab Contents..."))
			h3.id = "loadingMsg";
			this.contentDiv.appendChild(h3);
			currActive = this;
			
			setTimeout("Tab.delayShowTab(currActive)",25);
		}
		if(this.TabSet.displayMode == "dom") {
		//clear tabset main element contents
			while(content.hasChildNodes()) 
				content.removeChild(content.childNodes.item(0));
		
			try {
				content.appendChild(this.contentDiv);
			} catch(err) { }
		} else if (this.TabSet.displayMode == "hide") {
			this.TabSet.currActive.contentDiv.style.display = "none";
			this.contentDiv.style.display = "block";
		}
		
		content.scrollTop = this.scrollTop;
	}
	//if it is scrolled out of view, scroll into view
	if(this.TabSet.tabContainerEl.clientWidth < this.TabSet.tabContainerEl.scrollWidth) {
		var offL = this.tabDiv.offsetLeft;
		var scrL = this.TabSet.tabContainerEl.scrollLeft;
		var offR = this.tabDiv.offsetLeft + this.tabDiv.offsetWidth;
		var scrR = this.TabSet.tabContainerEl.scrollLeft + this.TabSet.tabContainerEl.clientWidth;
		if(offL < scrL) 
			this.TabSet.tabContainerEl.scrollLeft = offL - 8;
		else if (offR > scrR)
			this.TabSet.tabContainerEl.scrollLeft = offR - this.TabSet.tabContainerEl.clientWidth;
		//alert("offR = " + offR + "\nscrR = " + scrR);
	}
	//Set this as the currently active tab
	this.TabSet.currActive = this;
	return;
}

//used to set tab contents when content is a function reference
//called within a setTimeout so an intermittent "Loading..." message can be displayed
Tab.delayShowTab = function(tab) {
	//call function and set the tab contents
	tab.setTabContent(tab.content());
	
	tab.showTab();
	currActive = "";
}

//function to be overloaded to perform external tasks when making a tab active
function processExternal(tabObj) {
	//overload me
}

///////////////////////////////
//  EVENT HANDLING FUNCTIONS
//////////////////////////////
//called onClick to show the tab that was clicked on

Tab.prototype.tabMouseOut = function() {
	this.tabDiv.className = (this == this.TabSet.currActive) ? 'tabActive' : 'tabInactive';
}

Tab.prototype.tabMouseOver = function() {
	if(!(this == this.TabSet.currActive))
		this.tabDiv.className += ' tabHover';
}
TabSet.prototype.tabMouseWheel = function(ev) {
	var delta = 0;

	if (!ev) 
		ev = window.event;
	//IE and Opera
	if (ev.wheelDelta) { 
		delta = ev.wheelDelta/120;
		// In Opera 9, delta differs in sign as compared to IE.
		if (window.opera)
			delta = -delta;
	// Firefox
	} else if (ev.detail) { 
		// In Firefox, sign of delta is different than in IE.  Also, delta is multiple of 3.
		delta = -ev.detail/3;
	}
	// If delta is nonzero, handle it. Basically, delta is now positive if wheel was scrolled up,
	// and negative, if wheel was scrolled down.
	if (delta) {
		var dis = delta * 20;
		this.tabContainerEl.scrollLeft -= dis;
	}
	
	if (ev.preventDefault)
		ev.preventDefault();
	ev.returnValue = false;
}

var self;   //global variable needed for setTimeout to work
TabSet.prototype.scroll = function(dir) {
	self = this;
	this.tabScrollTimer = setTimeout("self.scroll(" + dir + ")", 1);
	var dis = dir * 2;
	this.tabContainerEl.scrollLeft += dis;
}
TabSet.prototype.endScroll = function() {
	
	if(typeof this.tabScrollTimer == "number") 
		clearTimeout(this.tabScrollTimer);
	return false;
}
var mouseWheel;
//check to see if the tab navigation is required 
TabSet.prototype.displayTabNav = function() {
	var self = this;
	var cliW = this.mainTabEl.clientWidth;
	var scrW = this.tabContainerEl.scrollWidth;
	//if no width is set on mainTabEl, IE reports clientWidth as 0
	if(cliW == 0) {
		this.mainTabEl.style.width = "100%";
		cliW = this.mainTabEl.clientWidth;
		this.mainTabEl.style.width = "";
	}
	if(typeof mouseWheel != "function") {
		mouseWheel = function (ev) {
			self.tabMouseWheel(ev);	
		}
	}
	if(scrW > cliW) {
		this.navEl.style.display = "block";
		var cliL = 0;
		if(this.tabContainerEl.clientLeft)  // needs to account for borders
			cliLR = this.tabContainerEl.clientLeft * 2;
		else
			cliLR = this.tabContainerEl.offsetWidth - this.tabContainerEl.clientWidth;
		this.tabContainerEl.style.width = (cliW - this.navEl.offsetWidth - (cliLR) - 4) + "px";
		Tab.addEvent(this.tabContainerEl, "mousewheel", mouseWheel );
	} else {
		this.navEl.style.display = "none";
		this.tabContainerEl.style.width = "100%";
		Tab.removeEvent(this.tabContainerEl, "mousewheel", mouseWheel );
	}
	//if scrollLeft + clientWidth > scrollWidth, then we need to snap the tabs to the right side
	if(this.tabContainerEl.scrollLeft + this.tabContainerEl.clientWidth > this.tabContainerEl.scrollWidth) 
		this.tabContainerEl.scrollLeft = this.tabContainerEl.scrollWidth + this.tabContainerEl.clientWidth;
	
}
//create the actual tab element
Tab.prototype.drawTab = function () {
	var self = this;
	var newDiv = document.createElement("div");

	isActive = this.isActive;
	newDiv.Tab = this;
	newDiv.className = (isActive) ? 'tabActive' : 'tabInactive';
	newDiv.onmouseover = function() { self.tabMouseOver() };
	newDiv.onmouseout = function() { self.tabMouseOut() };
	newDiv.onclick = function() { self.showTab() };
	
	if(typeof this.label == "object") {
		newDiv.appendChild(this.label);
	} else {
		newDiv.innerHTML = this.label;
	}
	
	this.tabDiv = newDiv;

	this.TabSet.tabContainerEl.appendChild(newDiv);
	
	if(isActive) {
		this.TabSet.currActive = this;
	}
}

TabSet.prototype.addTab = function(tabObj) {
	tabObj.TabSet = this;
	for(var i=0; i < tabObj.TabSet.tabs.length; i++) {
		if(tabObj.TabSet.tabs[i] == tabObj)
			return;
	}
	tabObj.TabSet.tabs.push(tabObj);
	tabObj.drawTab();
	//show tab navigation, if necessary
	this.displayTabNav();
	tabObj.showTab();
}

TabSet.prototype.removeTab = function(tabObj) {
	tabObj.removeTab();	
}

Tab.prototype.removeTab = function() {
	var exists = false;
	for(var i=0; i < this.TabSet.tabs.length; i++) {
		if(this.TabSet.tabs[i] == this) {
			//check if it is the last tab, if it is, select the last tab
			//  otherwise, select the next tab after removal
			if(i == (this.TabSet.tabs.length-1)) 
				var nextTab = i-1;
			else
				var nextTab = i;
			this.TabSet.tabs.splice(i,1);
			exists = true;
			break;
		}
	}
	if(exists) {
		this.TabSet.tabContainerEl.removeChild(this.tabDiv);
		this.TabSet.tabs[nextTab].showTab();
		//show tab navigation, if necessary
		this.TabSet.displayTabNav();
	}
}

//helper functions

// Walks up the DOM tree to try and find an element with a Tab object associated with it.
Tab.closeTab = function(el) {
	if(!el.Tab) {
		while(!el.Tab && el.tagName.toLowerCase() != "body")
			el = el.parentNode;
	}
	el.Tab.removeTab();
}
Tab.addEvent = function(el, evname, func) {
	if (typeof el.addEventListener == "function") {
		if(evname == "mousewheel")
			evname = "DOMMouseScroll";
		el.addEventListener(evname, func, true);
	} else {
		el.attachEvent("on" + evname, func);
	}
}

Tab.removeEvent = function(el, evname, func) {
	if (typeof el.removeEventListener == "function") {
		el.removeEventListener(evname, func, true);
	} else {
		el.detachEvent("on" + evname, func);
	}
}
Tab.stopEvent = function(ev) {
	if (!ev) 
		ev = window.event;
	if (typeof ev.preventDefault == "function") {
		ev.preventDefault();
	} else {
		ev.cancelBubble = true;
		ev.returnValue = false;
	}
}
//-----------------------------
//   End Tab Functions
//----------------------------- 
