/*-------- TOGGLER -----------*/

/* 

added selectors

*/
 
 Toggler = Class.create();
 
 Toggler.prototype = {
	  initialize: function(baseID, options) {

		this.currentShowing = ''
		this.togglers = '';
		
		this.options = {
			
			/* class placed around a single complete member of a Toggler (including the trigger) - usually a div - or if we use no tagToHide */
			togglerWrapperClass : '',
			
			/* ID for a trigger (useful for a one-off show/hide) */
			triggerId : '',
			
			/* base node for the trigger.  Usually a header or DT element.  This is used when you define a togglerWrapperClass or use a triggerTag */
			triggerBaseNode : '',
			
			/* node for the trigger when the main thing you click is in a child element of the triggerbaseNode */
			triggerTag : '',
			
			/* use if you want to set a specific node in the triggerBaseNode to be the change indicator - it will receive the on class and be the clickable element depending on the triggerOnly setting below */
			triggerChangeIndicatorNode: '',
			
			/* 	false - use the whole basenode to change the state 
				true - use only the trigger to change the state  */
			triggerOnly: false,
			
			/* the node below the trigger which gets hidden/shown, usually a DIV */
			tagToHide: '',
			
			/* used if we do a general replacement in a whole base DIV for more than one block of togglers, and we want to use the same baseName for more than one call */
			groupName: '',
			
			parentHasOnClass: '',
			
			/* set this to the first one to open.  Can be an ID or number.  If number, you cannot already have an ID in the HTML.  Passing 'none' keeps them all off at startup */
			first: 0,
			
			/* set this to activate accordian-type toggling */
			accordian: false,
			
			action: 'click',
			
			collapsable: true
		}
		
		this.options.togglerBaseNode = baseID;
		
		Object.extend(this.options, options || {})
		
		this.container = $(baseID);
		this.init(); 
		
		if (this.options.first != 'none') this.open(this.options.first);
		
	  },
	  
	  init: function() {
	  	
	  	/* 	four types of togglers:
	  		1. a group of elements inside a wrapper with a class.
	  		2. a single tag with its immediate previous sibling as the trigger.  You only set a tagToHide in this case.
	  		3. a single tag with x number of wrapper tags and the trigger set by triggerTag.  You set a triggerBaseNode, a triggerTag, and an optional togglerWrapperClass
	  		4. a single ID targeting a one-to-one or one-to-many separated toggle
	  	*/
	  	
	  	// get the togglers //
	  	
	  	if (this.options.togglerWrapperClass) {
	  		var togglers =  this.container.getElementsByClassName(this.options.togglerWrapperClass);
	  	}
	  	else {
	  		if (this.options.tagToHide) {
	  		var togglers = this.container.getElementsByTagName(this.options.tagToHide);
	  		}
	  		else {
	  			//using a selector //
	  			var togglers = $$(this.options.selectorToHide);
	  		}
	  	}
	
		this.togglers = togglers;
	
		// got the togglers //
	
		var groupName = this.options.groupName || this.options.togglerBaseNode;
	
		this.groupName = groupName;
	
		// make sure we are not targeting a one-to-one or one-to-many separated toggle //
		if (!this.options.triggerId) {
			
			for(var x = 0; x < togglers.length; x++) {
		
				if (!togglers[x].id) togglers[x].id = groupName + '-toggler-' + x;
				
				// figure out the triggers //
				
				// if you use a triggerBaseNode without a triggerTag, your trigger must be inside of the togglerWrapperClass - if you do use a triggerTag, your trigger needs to be the previousSibling of the togglerWrapperClass //
				if (this.options.triggerBaseNode) {		
					
					if (!this.options.triggerTag) {
						var target = this.options.triggerOnly ? 
						togglers[x].getElementsByTagName(this.options.triggerChangeIndicatorNode) :
						togglers[x].getElementsByTagName(this.options.triggerBaseNode)
						target = target[0];
					}
					else {
						// using a triggerTag accesses the trigger outside of the main togglerWrapperClass //
						$(togglers[x].parentNode).cleanWhitespace();
						var targetHolder = togglers[x].previousSibling;
						
						var target = targetHolder.getElementsByTagName(this.options.triggerTag);
						target = target[0];
						
						}
				}
				// we are not doing any wrapping of nodes for style, just a barebones toggle with the immediate sibling //
				else {
					$(togglers[x].parentNode).cleanWhitespace();
					var target = togglers[x].previousSibling;
				}
				
				
				// done triggers //
				
				if (!target.id) target.id = groupName + '-toggler-tr-' + x;
				
				target.style.cursor = 'pointer';
				target.style.outline = 'none';
				
				// add the click //
				Event.observe(target, this.options.action, this.open.bindAsEventListener(this));
			
				var contentToHide = togglers[x].getElementsByTagName(this.options.tagToHide);
	
				// set contentToHide to the tagName inside the wrapper -- otherwise set it to the tagName itself //
				contentToHide = contentToHide[0] ? contentToHide[2] : togglers[x];
				
				// hide the contentToHides //
				
				$(contentToHide).style.display = 'none';
			}	
		}	
		
		else {
				var target = $(this.options.triggerId);
				target.style.cursor = 'pointer';
				target.style.outline = 'none';
				
				// add the click //
				Event.observe(target, this.options.action, this.open.bindAsEventListener(this));
			
				for(var x = 0; x < togglers.length; x++) {
					if (this.options.tagToHide) {
						var contentToHide = togglers[x].getElementsByTagName(this.options.tagToHide);
		
						// set contentToHide to the tagName inside the wrapper -- otherwise set it to the tagName itself //
						contentToHide = contentToHide[2] ? contentToHide[2] : togglers[x];
						$(contentToHide).style.display = 'none';
					}
					else {
						// if selector //
						for(var x = 0; x < togglers.length; x++) {
							var contentToHide = togglers[x];
							$(contentToHide).style.display = 'none';
						}
					}
				
				}
			
		}
		
		
	  },
	  
	  
	 getBase: function(id) { // takes an element or ID, returns element //
		
			// getBase returns the node which is the wrapper for the trigger //
			
			// if parentHasOnClass is true, the immediate parent is returned //
			if (this.options.parentHasOnClass) {
				return $(id).parentNode;
			}
			
			else {
				
				// otherwise, get the node that will hold the on class //
				if (this.options.triggerBaseNode) {
					var t = $(id).tagName
					var parent = $(id);
					
					while( t != this.options.triggerBaseNode.toUpperCase()) {
						parent = parent.parentNode
						t = parent.tagName
					}
					return $(parent);
				}
				
				// otherwise, the trigger carries the on class //
				else {
					return $(id);
				}
			}
			
		},
	  
	  changeIndicator: function(base) {
	  
	  		if (this.options.triggerChangeIndicatorNode) {
	  			var changer = $(base).getElementsByTagName(this.options.triggerChangeIndicatorNode)[0];
	 			(changer.className == 'on') ? changer.removeClassName('on') : $(changer).addClassName('on');
	 		}
	 		else {
	 			(base.hasClassName('on')) ? $(base).removeClassName('on') : $(base).addClassName('on');
	 		}
	 		
	 },
	 
	  open: function(e) {
		
		switch (typeof e) {
				case 'number' :
					var clickedTarget = $(this.groupName + '-toggler-tr-' + e);
					break;
				case 'string' :
					var clickedTarget = $(e);
					break;
				default:
					var clickedTarget = Event.element(e);
					break;
		}
		
		
			var triggerBaseNode = this.getBase(clickedTarget);
		
	
			if (this.options.triggerBaseNode) {
				
				if (!this.options.triggerTag) {
					var container = triggerBaseNode.parentNode.parentNode;
					var contentToShow =  container.getElementsByTagName(this.options.tagToHide);
				}
				else {
					var container = triggerBaseNode.getElementsByTagName(this.options.triggerTag)[0];
					var contentToShow =  triggerBaseNode.next();
				}
			
			}
			
			else {
				var container = triggerBaseNode;
				
				if (!this.options.triggerId) {
					var contentToShow =  triggerBaseNode.next();
				}
				else {
					var contentToShow =  this.togglers;
					contentToShow =  contentToShow[2];
				}
			}		
		
				
		if(this.options.accordian) {
		
			if (container.id != this.currentShowing) {		
				
				// use currentShowing in the method to find the node and close it //
				this.closeCurrent();
				
				if (typeof this.options.beforeShow == 'function')
					this.options.beforeShow.apply(this, [contentToShow]);
				
				this.changeIndicator(triggerBaseNode);	
				
				this.show(contentToShow[2]);
		
				this.currentShowing = container.id;
			}
			
			else {
				if (this.options.collapsable) {
					this.closeCurrent();
					this.currentShowing = 0;
				}
			}
			
			
		}
		
		// just open and close the boxes without accordian //
		// TODO: add in looping through multiple triggerId contentnodes //
		else {
		
				if (contentToShow[2].style.display == 'block' ) {
	
					this.changeIndicator(triggerBaseNode);	
					
					if (typeof this.options.beforeHide == 'function')
						this.options.beforeHide.apply(this, [contentToShow]);
					
					this.hide(contentToShow[2]);
	
				}
				else {
	
					this.changeIndicator(triggerBaseNode);	
					
					if (typeof this.options.beforeShow == 'function')
						this.options.beforeShow.apply(this, [contentToShow]);
					
					this.show(contentToShow[2]);
	
				}
			}
		
		Event.stop(e);
		  
	  },
	  
	  closeCurrent: function() {
		  
		  if(this.currentShowing || !this.options.accordian) {
				
				var currentShowing = $(this.currentShowing);
				
				if (this.options.triggerBaseNode) {
					
					if (!this.options.triggerTag) {
						var contentToHide = currentShowing.getElementsByTagName(this.options.tagToHide);
						var triggerBaseNode = currentShowing.getElementsByTagName(this.options.triggerBaseNode);
						triggerBaseNode = triggerBaseNode[0];
					}
					else {
						var triggerBaseNode =  this.getBase(currentShowing);
						contentToHide = triggerBaseNode.next();
						
					}	
				}
				
				else {
					var contentToHide = currentShowing.next();
					var triggerBaseNode = currentShowing;
				}		
				
		  		if (typeof this.options.beforeHide == 'function')
					this.options.beforeHide.apply(this, [contentToHide]);
		  	
		  		this.hide(contentToHide[2]);
		  		this.changeIndicator(triggerBaseNode);	
		  
		  }
	  },
	 
	 
	  show: function(el) {
	  
	  		if (this.options.slide && el.style.display == 'none' ) {
	  			Effect.BlindDown(el, {duration: .5 });
	  		}
	  		else {
	  			el.style.display = 'block'
	  		}
	  },
	 
		  
	  hide: function(el) {
	  		if (this.options.slide && el.style.display == 'block') {
	  			Effect.BlindUp(el, {duration: .5 });
	  		}
	  		else {
	  			el.style.display = 'none'
	  		}
	  			
	  },
	  
	  all: function(e, type) {
	  	
	  			/* default is 'on' */
	  			if (arguments.length == 2) {
	  				Event.stop(e);
	  			}
	  			else type = e;

	  			for(var x = 0; x < this.togglers.length; x++) {
				
					var triggerBaseNode = this.getBase(this.togglers[x].id);
					
					var contentToHide = this.togglers[x].getElementsByTagName(this.options.tagToHide);
					// set contentToHide to the tagName inside the wrapper -- otherwise set it to the node itself //
					contentToHide = contentToHide[0] ? contentToHide[2] : this.togglers[x];
					
					(type == 'off' || type == 'hide') ? this.hide(contentToHide) : this.show(contentToHide);
					
					if ((contentToHide.style.display == 'none') && (type != 'off' && type != 'hide')) {
						this.changeIndicator(triggerBaseNode);	
					}
					if ((contentToHide.style.display == 'block') && (type == 'off' || type == 'hide')) {
						this.changeIndicator(triggerBaseNode);	
					}
					
					this.currentShowing = 'none';
					this.options.accordian = false;
					
				}
				
		}	  
	 
}

// overwrite prototype's show method to specifically add block -- necessary if we want to use Effect.Appear and hide the background with CSS display: none; //

var newShow = { show : function(element) {
	$(element).style.display = 'block';
	return element;
  	}
  }
// 
Element.addMethods(newShow);
