/*

	GEM Products Inc
	
	Basic Controls Definition script
	
	Requires the prototype.js and scriptaculous.js libs...


	v1.0.1
	--------------------------------------------
	- Added a member variable to TabController called "displayStyle". Used for the default tabelement.style.display value when creating a new tab
	- Added a "callbackFunction" member variable to the TabController and the controller will execute the callback functions as appropriate...

	v1.0.2
	=============================================
	- Added properties and functionality to GEM.Controls.TabController class that allows a user to expand/collapse all tabs.
		- New "toggleCollapseAll()" method
		- Controller automatically adds an "expand/collapse" psuedo-tab to the tab list for the user to click on.
		- Ability to set the "showsExpandCollapse" property true/false to hide or show the expand/collapse tab
*/


if (!GEM)				var GEM = { };
if (!GEM.Controls) 			GEM.Controls = { };

/*
GEM.Controls.Control = {
	/// event handlers. should mimic the javascript events
	Events: {
		mouseclick: null,
		mousedown: null,
		mouseup: null,
		mouseover: null,
		mouseout: null,
		keypress: null,
	},
	/// the layout properties should mimic CSS style layout and positioning properties
	Layout: {
		position: "static",	
		left: 0,
		right: 0,
		width: 100,
		height: 100
	},
	_private: {
		element: null,
		nodeName: null
	},
};

GEM.Controls.ControlBase = Class.create({
	_private: GEM.Controls.Control._private,
	_element: null,
	initialize: function(domElement) {
	
		this._element = domElement;
	},
	getDomElement: function() {
		return _private.element;
	},
});
*/
GEM.Controls.CollapsableControl = Class.create({

	/// member variables
	containerElement: null,
	divToCollapse: null,
	animationDuration: 0.5,
	labelElement: null,
	collapseText: "Collapse",
	expandText: "Expand",
	disableCollapse: false,
	disabledText: "",
	
	/// event callback functions
	onCollapseBegin: null,
	onCollapseEnd: null,
	onExpandBegin: null,
	onExpandEnd: null,
	
	_currentEffect: null,
	
	initialize: function(divToCollapse, labelElement, addlOptions) {
		
		if (addlOptions) {
			Object.extend(this, addlOptions);	
		}
		
		if (divToCollapse) {
			this.divToCollapse = $(divToCollapse);	
			this.divToCollapse.collapser = this;
			this.divToCollapse.setAttribute("controlclass", "GEM.Controls.CollapsableControl");
			/*
			/// ensure that the 'divToCollapse' element only has 1 direct child, and that child must be a div element
			var children = $A(this.divToCollapse.childNodes);
			if (children.length != 1 || children[0].nodeName.toLowerCase() != "div") {
				var div = Builder.node("div");
				div.innerHTML = this.divToCollapse.innerHTML;
				this.divToCollapse.innerHTML = "";
				this.divToCollapse.appendChild(div);
			}
			*/
			
		}
		else {
			throw("Cannot create a collapsable control without an element to use...");	
		}
		
		
		if (labelElement) {
			this.labelElement = $(labelElement);
			this.labelElement.collapser = this;
			if (!this.disableCollapse) {
				this.labelElement.style.cursor = "pointer";
				this.labelElement.onclick = (this.disableCollapse) ? null : (function() {  this.collapser.toggle(); });
				this.labelElement.onmouseover = function() {this.style.textDecoration = 'underline'; };
				this.labelElement.onmouseout = function() {this.style.textDecoration = 'none'; };
				this.updateLabel((typeof(this.divToCollapse.isHidden) == "undefined" || this.divToCollapse.isHidden == false) ? this.collapseText : this.expandText);
			}
			else {
				this.updateLabel(this.disabledText);
			}
		}
		
		
	},
	collapse: function(effectOpts) {
		if (!this._checkOkToCollapse()) return;
		
		if (typeof(this.divToCollapse.isHidden) == "undefined" || this.divToCollapse.isHidden == false) {
			if (this.onCollapseBegin) this.onCollapseBegin();
			var collapser = this;
			this._currentEffect = new Effect.SlideUp(this.divToCollapse, Object.extend({ duration: this.animationDuration, afterFinish:(function(){ collapser._currentEffect = null; if (collapser.onCollapseEnd) collapser.onCollapseEnd(); }) }, effectOpts || {} ));
			this.divToCollapse.isHidden = true;
			this.updateLabel(this.expandText);
		}
	},
	expand: function(effectOpts) {
		if (!this._checkOkToCollapse()) return;
		
		if (typeof(this.divToCollapse.isHidden) != "undefined" && this.divToCollapse.isHidden == true) {
			if (this.onExpandBegin) this.onExpandBegin();
			var collapser = this;
			this._currentEffect = new Effect.SlideDown(this.divToCollapse, Object.extend({ duration: this.animationDuration, afterFinish:(function(){ collapser._currentEffect = null; if (collapser.onCollapseEnd) collapser.onCollapseEnd(); }) }, effectOpts || {} ))
			this.divToCollapse.isHidden = false;
			this.updateLabel(this.collapseText);
		}
	},
	toggle: function() {
		if (!this._checkOkToCollapse()) return;
			
		if (typeof(this.divToCollapse.isHidden) == "undefined" || this.divToCollapse.isHidden == false) 
			this.collapse();
		else
			this.expand();
	},
	/// function checks that everything is ok to send a collapse or expand command
	_checkOkToCollapse: function() {
		return (!this.disableCollapse && this.divToCollapse && !this._currentEffect);	
	},
	/// function that sets the text or inner HTML of the label element...
	updateLabel: function(html) {
		if (this.labelElement) {
			this.labelElement.innerHTML = html;	
		}
	}
	
	
});

/* 
Provides a class that is linked to a container element. The container can be of any type (div, td, span, etc...) The container can have any content elements. 
This class will scan the contents of the container for collapsable controls, and performs batch operations on them.
*/
GEM.Controls.CollapsableControlContainer = Class.create({
	containerElement: null,
	labelElement: null,
	allCollapsed: false,
	animationDuration: 1.0,
	disableCollapse: false,
	disabledText: "",
	expandText: "Expand All",
	collapseText: "Collapse All",
	expandAllLabel: null,
	collapseAllLabel: null,
	
	initialize: function(containerElement) {
		if (!containerElement) {
			throw("Error. Cannot create a collapsable control container without a container element");
		}	
		
		this.containerElement = $(containerElement);
		this.containerElement.collapserContainer = this;
		this.containerElement.setAttribute("controlclass", "GEM.Controls.CollapsableControlContainer");
		
		/// EXTEND THE CURRENT INSTANCE BASED ON A SECOND PARAMETER PASSED INTO THE FUNCTION.
		Object.extend(this, arguments[1] || { });
		
		if (this.labelElement) {
			this.labelElement.collapserContainer = this;
			if (!this.disableCollapse) {
				this.labelElement.style.cursor = "pointer";
				this.labelElement.onclick = (this.disableCollapse) ? null : (function() {  this.collapserContainer.toggleAll(); });
				this.labelElement.onmouseover = function() { this.style.textDecoration = 'underline'; };
				this.labelElement.onmouseout = function() { this.style.textDecoration = 'none'; };
				this.updateLabel(this.collapseText);
			}
			else {
				this.updateLabel(this.disabledText);
			}
		}
		
		/// create a new instance of a "collapse all" label if one was not specified in the optional second function parameter
		if (!this.collapseAllLabel) {
			this.collapseAllLabel = Builder.node("span");
			this.collapseAllLabel.innerHTML = this.collapseText;
		}
		this.collapseAllLabel.collapserContainer = this;
		this.collapseAllLabel.style.cursor = "pointer";
		this.collapseAllLabel.onclick = (function() {  this.collapserContainer.collapseAll(); });
		this.collapseAllLabel.onmouseover = function() { this.style.textDecoration = 'underline'; };
		this.collapseAllLabel.onmouseout = function() { this.style.textDecoration = 'none'; };
	
		
		/// create a new instance of a "expand all" label if one was not specified in the optional second function parameter
		if (!this.expandAllLabel) {
			this.expandAllLabel = Builder.node("span");
			this.expandAllLabel.innerHTML = this.expandText;
		}
		this.expandAllLabel.collapserContainer = this;
		this.expandAllLabel.style.cursor = "pointer";
		this.expandAllLabel.onclick = (function() {  this.collapserContainer.expandAll(); });
		this.expandAllLabel.onmouseover = function() { this.style.textDecoration = 'underline'; };
		this.expandAllLabel.onmouseout = function() { this.style.textDecoration = 'none'; };
		
	},
	toggleAll: function() {
		if (!this.allCollapsed)
			this.collapseAll();
		else 
			this.expandAll();
	},
	collapseAll: function() {
		var list = this.containerElement.findDescendantsWithAttributeValue("controlclass", "GEM.Controls.CollapsableControl");
		//alert("found "+list.length+" collapsable controls for collapseAll");
		for (var i = 0; i < list.length; ++i) {
			list[i].collapser.collapse({ duration: this.animationDuration });
		}
		this.allCollapsed = true;
		this.updateLabel(this.expandText);
	},
	expandAll: function() {
		var list = this.containerElement.findDescendantsWithAttributeValue("controlclass", "GEM.Controls.CollapsableControl");
		//alert("found "+list.length+" collapsable controls for expandAll");
		for (var i = 0; i < list.length; ++i) {
			list[i].collapser.expand({ duration: this.animationDuration });
		}
		this.allCollapsed = false;
		this.updateLabel(this.collapseText);
	},
	updateLabel: function(html) {
		if (this.labelElement) this.labelElement.innerHTML = html;
	},
	notifyCollapseChange: function(descendantElement) {
		
	},
	setAddlCollapseAllLabel: function(element) {
		var e = $(element);
		e.innerHTML = this.collapseText;
		e.collapserContainer = this;
		e.style.cursor = "pointer";
		e.onclick = (function() {  this.collapserContainer.collapseAll(); });
		e.onmouseover = function() { this.style.textDecoration = 'underline'; };
		e.onmouseout = function() { this.style.textDecoration = 'none'; };
	},
	setAddlExpandAllLabel: function(element) {
		var e = $(element);
		e.innerHTML = this.expandText;
		e.collapserContainer = this;
		e.style.cursor = "pointer";
		e.onclick = (function() {  this.collapserContainer.expandAll(); });
		e.onmouseover = function() { this.style.textDecoration = 'underline'; };
		e.onmouseout = function() { this.style.textDecoration = 'none'; };
	}
});


GEM.Controls.TabController = Class.create({ 
	tabsContainer: null,
	tabsList: null,
	contentContainer: null,
	tabs: null,
	activeTabIndex: 0,
	activeTabClass: "",
	inactiveTabClass: "",
	mouseoverTabClass: "",
	displayStyle: "inline",		/// used for setting the tabElement.style.display property default values...
	expandCollapseAllTab: null,
	
	_allExpanded: false,
	expandCollapseElement: null,
	_showsExpandCollapse: true,
	animateExpand: true,
	showsHeadersWhileCollapsed: false,
	expandHtml: "+",
	collapseHtml: "Collapse",
	animationDuration: 1.5,
	_animateCount: 0,
	
	callbackFunctions: {
		willSelectTab: null,	// params: (sender, oldTab, newTab)
		didSelectTab: null,		// params: (sender, oldTab, newTab)
		didExpandTabs: null,
		didCollapseTabs: null,		
		didAddTab:null,					// params: (sender, tabAdded)
		didRemoveTab: null			// params: (sender, tabRemoved)
	},
	
	initialize: function(tabsContainer) {
	
		/// create the dynamic property functionality.	
		//this.setDynamicProperty("showsExpandCollapse");
		
		this.tabs = [];	/// init an empty array....		
																													
		this.tabsContainer = (tabsContainer) ? $(tabsContainer) : Builder.node("div");
		this.tabsList = Builder.node("ul", { /*className: this.classTabUL*/ });
		this.tabsList.style.listStyle = "none";		// for horizontal layout
		this.tabsList.style.margin = "0px";
		this.tabsList.style.padding = "0px";
		this.tabsContainer.appendChild(this.tabsList);
		

		if (arguments[1]) {
			Object.extend(this, arguments[1]);	
		}
		
		if (!this.expandCollapseElement) {
			this.expandCollapseElement = Builder.node("li", { className: this.inactiveTabClass });
			this.expandCollapseElement.innerHTML = this.expandHtml;
			this.expandCollapseElement.style.display = this.displayStyle;
		}
		this.expandCollapseElement.tabController = this;
		this.expandCollapseElement.onclick = function(e) { this.tabController.toggleCollapseAll(); };
		if (this.getShowsExpandCollapse()) {
			this.tabsList.appendChild(this.expandCollapseElement);
		}
		
		if (arguments[1] && !Object.isUndefined(arguments[1].defaultExpandedTabs) && arguments[1].defaultExpandedTabs) {
			this.toggleCollapseAll(false);
		}
		
		this._log("Finished initializing TabController");
	},
	
	getActiveTab: function() {
		return this.tabs[this.activeTabIndex];
	},
	addTab:function(tabId, headerText, contentElement, autoSelect) {
		if (this._animateCount > 0)		/// skip select operations while any expand/collapse animations are still playing.
			return;
		
		var tab = new GEM.Controls.Tab(tabId, headerText, contentElement, this);
		
		this.tabs.push(tab);
		/// insert the new tab before the expand/collapse element (this element should always be at the end of the list)
		this.tabsList.insertBefore(tab.tabElement, this.expandCollapseElement);
		
		if (this.tabs.length == 1 || (autoSelect && autoSelect === true)) {
			this.selectTab(tab);	
		}
		else {
			tab.contentElement.style.display = "none";	
			//tab.contentElement.style.visibility = "hidden";
		}
		
		if (this.callbackFunctions.didAddTab) {
			this.callbackFunctions.didAddTab(this, tab);
		}
		return tab;
		
	},
	removeTab: function(tab) {
		if (this._animateCount > 0)		/// skip select operations while any expand/collapse animations are still playing.
			return;
			
		if (Object.isNumber(tab)) {
			tab = this.tabs[tab];
		}
		
		var newSelection = -1;
		if (this.getActiveTab == tab) {
			newSelection = (this.activeTabIndex == this.tabs.length-1) ? this.activeTabIndex - 1 : this.activeTabIndex;
		}
		
		this.tabs = this.tabs.without(tab);
		this.tabsList.removeChild(tab.tabElement);
		
		if (newSelection >= 0) {
			this.selectTab(newSelection);
		}
		
		
		if (this.callbackFunctions.didRemoveTab) {
			this.callbackFunctions.didRemoveTab(this, tab);
		}
	},
	selectTab: function(tab) {
		if (this._animateCount > 0)		/// skip select operations while any expand/collapse animations are still playing.
			return;
		
		if (Object.isNumber(tab)) {
			tab = this.tabs[tab];
		}
		
		var currentTab = this.getActiveTab();
		
		if (this.callbackFunctions.willSelectTab) {
			this.callbackFunctions.willSelectTab(this, currentTab, tab);
		}	
		
		for (var i = 0; i < this.tabs.length; ++i) {
			if (this.tabs[i] == tab) {
				this.tabs[i].tabElement.className = this.activeTabClass
				this.tabs[i].contentElement.style.display = "block";
				//this.tabs[i].contentElement.style.visibility = "visible";
			}
			else {
				this.tabs[i].tabElement.className = this.inactiveTabClass
				this.tabs[i].contentElement.style.display = "none";
				//this.tabs[i].contentElement.style.visibility = "hidden";
			}
		}
		
		this.activeTabIndex = this.tabs.indexOf(tab);
		
		if (this.callbackFunctions.didSelectTab) {
			this.callbackFunctions.didSelectTab(this, currentTab, tab);
		}	
	},
	toggleCollapseAll: function(animated) {
		if (this._animateCount > 0)		/// skip select operations while any expand/collapse animations are still playing.
			return;
		
		if (arguments.length == 0)
			animated = this.animateExpand;
		
		if (this._allExpanded) {
			/// OPERATIONS TO COLLAPSE (HIDE) ALL CONTENT ELEMENTS
			
			/// add all content elements back to view and make visible
			for (var i = 0; i < this.tabs.length; ++i) {
				
				this.tabs[i].tabElement.style.display = this.displayStyle;
				
				if (i == this.activeTabIndex)
					continue;
				
				if (animated)  {
					new Effect.BlindUp(this.tabs[i].contentElement, { duration: this.animationDuration, afterFinish: (function() { --this._animateCount; }).bind(this) });
					++this._animateCount;
				}
				else 
					this.tabs[i].contentElement.style.display = "none";
			}
			
			this.expandCollapseElement.innerHTML = this.expandHtml;
			this._allExpanded = false;
			
			if (this.callbackFunctions.didCollapseTabs) {
				this.callbackFunctions.didCollapseTabs(this);
			}
		}
		else {
			/// OPERATIONS TO EXPAND (SHOW) ALL CONTENT ELEMENTS
			
			/// add all content elements back to view and make visible
			for (var i = 0; i < this.tabs.length; ++i) {
					
				this.tabs[i].tabElement.style.display = "none";

				if (i == this.activeTabIndex)
					continue;
				
				if (animated) {
					new Effect.BlindDown(this.tabs[i].contentElement, {duration: this.animationDuration, afterFinish: (function() { --this._animateCount; }).bind(this)} );
					++this._animateCount;
				}
				else
					this.tabs[i].contentElement.style.display = "block";
			}
			
			this.expandCollapseElement.innerHTML = this.collapseHtml;
			this._allExpanded = true;
			
			
			if (this.callbackFunctions.didExpandTabs) {
				this.callbackFunctions.didExpandTabs(this);
			}
		}
	},
	getShowsExpandCollapse: function() {
		return this._showsExpandCollapse;
	},
	setShowsExpandCollapse: function(newValue) {
		
		if (!newValue) {
			this.expandCollapseElement.style.display = "none";
		}
		else {
			this.expandCollapseElement.style.display = this.displayStyle;
		}
		
		this._showsExpandCollapse = newValue;
		return newValue;
	},
	
	_logCallback: null,
	_log:function(message) {
		if (this._logCallback) this._logCallback.call(this, message);
	}
});

GEM.Controls.Tab = Class.create({ 
	id: null,
	headerText: "",
	tabElement: null,
	tabLinkElement: null,
	contentElement: null,
	contentHeaderElement: null,
	tabController: null,
	
	initialize: function(tabId, headerText, contentElement, tabController) {
		/// populate properties
		this.id = tabId;
		this.headerText = headerText;
		this.tabController = tabController;
		this.contentElement = contentElement;
		
		var selectAction = function(e) { this.tabInstance.select(); };
		
		/// create the HTML LI (list item) DOM element
		this.tabElement = Builder.node("li", { id: "tab_"+this.id, className: this.tabController.inactiveTabClass });
		this.tabElement.style.display = tabController.displayStyle;//"inline";	/// for horizontal layout
		this.tabElement.tabInstance = this;
		this.tabElement.onmouseover = function(e) { this.tabInstance._tabMouseOver(); };
		this.tabElement.onmouseout = function(e) { this.tabInstance._tabMouseOut(); };
		this.tabElement.onclick = selectAction;
		
		/// create the HTML A (anchor link) DOM element
		this.tabLinkElement = Builder.node("a", { /*className: this.tabController.classTabLink*/ });
		this.tabLinkElement.src = "javascript:void(0);";
		this.tabLinkElement.innerHTML = headerText;
		this.tabLinkElement.tabInstance = this;
		this.tabLinkElement.onclick = selectAction;
		
		this.tabElement.appendChild(this.tabLinkElement);
	},
	
	select: function() {
		this.tabController.selectTab(this);
	},
	
	isActive: function() {
		return (this.tabController.getActiveTab() == this);
	},
	
	_tabMouseOver: function() {
		if (this.tabController.getActiveTab() == this) {
			
		}
		else {
			if (this.tabController.mouseoverTabClass && this.tabController.mouseoverTabClass != "")
				this.tabElement.className = this.tabController.mouseoverTabClass;
		}
	},
	_tabMouseOut: function() {
		if (this.tabController.getActiveTab() == this) {
			this.tabElement.className = this.tabController.activeTabClass
		}
		else {
			this.tabElement.className = this.tabController.inactiveTabClass
		}
	}
});

