/* 

InfoTab Class:
	Version: 0.015
	Date: 20070214
	Author: Marty Stake

Dependencies:
	prototype.js and effects.js if you want effects

*/

var InfoTabManager = Class.create();

InfoTabManager.prototype = {
		
		initialize: function(baseId, options) {
			
			this.slides = [];
			this.triggers = [];
			this.options = { 

				baseName: 			'tabs',
				
				// trigger options
				triggerWrapper: 	'tabs-triggers',
				triggerBaseNode:	 '',
				triggerActivator: 	'a',
				
				// slide options
				slides: 			'',
				useSlideClass: 		false,
				slideBaseWrapper: 	'div',
				parentHasOnClass: 	false,
				
				// main options
				firstIndex: 		1,
				action: 			'click',
				onState: 			'on',
				offState: 			'off',
				hoverState: 		'hover',
				noFollow: 			false,
	
				// indicator
				onBackground: 		'',
				horizontalConstrain: true
			}
			
			// check to see if the base tab wrapper is the first property passed in the constructor.  If not, pass the options property to the element property, and get the baseId from the options //
			if (typeof baseId == 'string') 
				options.baseName = baseId;
			else {
				options = baseId;
			}
		
			Object.extend( this.options, options || {});
			
			if (!this.options.slides) this.options.slides = this.options.baseName
			
			// set up the trigger items which change the slides  //
			var triggers = $(this.options.triggerWrapper).getElementsByTagName(this.options.triggerActivator);
			this.triggers = $A(triggers);
			this.setTriggers(this.triggers);
			
			// set up the data that switches when the trigger is clicked //
			this.totalSlides = this.setslides(this.options.slides);
			
			// set up global event handlers //
			Event.observe(this.options.baseName, 'mouseout', this.detectExit.bindAsEventListener(this, this.options.baseName));
			
			this.currentSlide = this.options.firstIndex ? this.options.firstIndex - 1 : 0;
			
			if (this.options.mode == 'contained') this.currentSlide = -1;
						
		},
		
		_isChildOf: function(el, base) {
			
			var toElement = el;
		
			// see if the element is a child of the base, the base itself (you will get this because of padding) //
			// do not reset if you roll over the trigger box //
			if(toElement) {
				if ( ( $(toElement).childOf(base) ) || (base == toElement.id ) || (toElement.id == this.options.triggerWrapper) ) {
					return true;
				}
			
			return false;
			
			}
			
			
		},
		
		
		detectExit : function(e, base) {
		
			// get the element that you left //
			var toElement = e.relatedTarget || e.toElement;
			
			// check to see if a mouseover element is a child.  if so, return false so we dont run the mouse event //	
			if(this._isChildOf(toElement, base)) {
					return false;
				}
			
			// otherwise, you are leaving the box.  Reset the tabs. //
			this.reset(e);

		},
		
		setTriggers: function(els) {  // takes array of elements
			for(var x=0; x<els.length; x++) {
				
				els[x].id = this.options.baseName + "-tr-" + x;
			
				// hide the initial state that is on for javascriptless browsers
				this.getBase(els[x].id).removeClassName(this.options.onState);				
				
				if (this.options.mode == 'contained') {
					Event.observe(els[x], 'mouseover', this.show.bind(this) );
				}
				
				else {
					Event.observe(els[x], this.options.action, this.show.bind(this) );
				}
				
				if ( (this.options.action != 'click') && (this.options.noFollow) )
						Event.observe(els[x], 'click', function(e) { Event.stop(e) } )
			}
	
			return els.length;
		},
		
		
		setslides: function(id) { // takes an id
	
				// if we want to use a class to define the slides //
				if (this.options.useSlideClass) 
					this.slides = $A( $(id).getElementsByClassName(this.options.useSlideClass, this.options.slideBaseWrapper ) );
				else 
					this.slides = $A( $(id).getElementsByTagName(this.options.slideBaseWrapper) );
				
				// set an off button if there are off buttons //
				for(var x=0; x<this.slides.length; x++) {
						this.slides[x].id = this.options.baseName + "-sl-" + x;
						
						var offButtons = $(this.slides[x]).getElementsByClassName('close');
						
						if (offButtons != '') Event.observe(offButtons[0], 'click', this.reset.bindAsEventListener(this) )
						
						// set the onmouseout to shut off the slide if it is on //
						Event.observe($(this.slides[x]), 'mouseout', this.detectExit.bindAsEventListener(this, this.slides[x].id))
						
						this.slides[x].style.display = 'none'; // hide em all and set up the initial "on" so it wont goof up the page if JS is off //
						
				}
				
			return this.slides.length;

		},
		
		
		getIndex: function(node, el) { // takes an element //
					
			if (el == null) {
				el = node;
				node = this.triggers;
			}
				
			if (node.indexOf(el) > 0) return node.indexOf(el);
			
			// do this in case we need to use spans or ems or whatever and we are not using a whole triggerActivator to show the slide //	
			var index = -1;	
			while (index == -1) {
				index = node.indexOf(el);
				el = el.parentNode;
			}
			
			return index;
		},
		
		getBase: function(id) { // takes an id, returns element //
		
			// the getBase returns the node which carries the on class 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);
				}
			}
			
		},
	
		show : function(e, index) { // takes an index //
			
			// change the behavior of index depending on the mode you are in and set the index //
			if (typeof e != 'number') {
				index = this.getIndex(this.triggers, Event.element(e));
			}
			else {
				index = e; // this would be an index if smartSwap is calling - or a call outside the object //
			}
		
			// hide everything - reset all only if the button is not on already //
			if (this.currentSlide != index) {
				
				this.reset();
				
				this.currentSlide = index;
		
				// run a callback to add any functionality you want after we show //
				if (typeof this.options.beforeShow == 'function') 
					this.options.beforeShow.apply(this, arguments);
		
				// turn on the trigger you rolled over //
				parentNode = this.getBase(this.triggers[index].id)
				parentNode.addClassName(this.options.onState);
				
				var slideId = this.options.baseName + "-sl-" + index;
				
				if (this.options.horizontalConstrain)
					this.horizontalConstrain( $(slideId), $(parentNode) );
				
				this.slideOn( $(slideId) );
			
				// run a callback to add any functionality you want after we show //
				if (typeof this.options.afterShow == 'function') 
					this.options.afterShow.apply(this, arguments);
			}
			
			else {
				return false;
			}
			
		},
		
		slideOn: function(slide) {
			
			if (slide.style.display == 'none') {
				if (this.options.effect) {
					this.slideEffectOn = new Effect.Appear(slide, 
						{ duration: .2, 
						  to: 1 } )
				}
				else {
					slide.style.display = 'block';
				}
				
			}
			
		},
		
		reset : function(e) {
		
			if (this.options.onBackground) {
				$(this.options.triggerWrapper).style.backgroundImage = ''
				$(this.options.triggerWrapper).style.backgroundPosition = ''
				$(this.options.triggerWrapper).style.backgroundRepeat = ''
			}
			
			this.triggers.each(function(trigger) { 
				var parentNode = this.getBase(trigger.id)
				parentNode.removeClassName(this.options.onState);
				}.bind(this))
				
			this.slides.each(function(slide) {
				$(slide).style.display = 'none';
			}.bind(this))
				
				
			// run a callback to add any functionality you want after we show //
				if (typeof this.options.afterReset == 'function') 
					this.options.afterReset.apply(this, arguments);
					
			if(e) {
				Event.stop(e);
			}
			
			// reset currentSlide so that the first //
			this.currentSlide = -1;

		}

}

InfoTabManager.AddOns = {

		horizontalConstrain : function(slide, trigger) {
			
			var slideBase = $(this.options.slides);
			var triggerBase = $(this.options.triggerWrapper);
			
			var triggerOffsetLeft = trigger.offsetLeft; 
			var triggerOffsetRight = trigger.offsetLeft + trigger.getWidth(); 
			
			var useOffset = false;
			var rightShift = 0;  // use this to cancel out any shadows or such //
			var leftShift = 0;  // use this to cancel out any shadows or such //
	
			var horizontalOrientation = ($(slideBase).getWidth() - (slide.getWidth() + triggerOffsetLeft + leftShift)) > 0 ? 'left' : 'right';
			
			switch(horizontalOrientation) {
				case 'left' :
					if ( (slide.getWidth() < triggerOffsetRight) || 
						 (slide.getWidth() + triggerOffsetLeft < $(slideBase).getWidth()) )
						useOffset = true
				break
					
				case 'right': 
					if ( (slide.getWidth() + ($(slideBase).getWidth() - triggerOffsetRight) < $(this.options.slides).getWidth()) ) 
					useOffset = false //set this true to use an offset on the right ones //
				break
			}
			
			if (useOffset) {
				var horizontalPosition = horizontalOrientation == 'left' ? triggerOffsetLeft + leftShift : $(this.options.slides).getWidth() - (triggerOffsetRight - rightShift);
			}
			else {
				var horizontalPosition = horizontalOrientation == 'left' ? leftShift : rightShift;
			}
				
			// set the position //
			if (horizontalOrientation == 'left') {
				slide.style.right = '';
				slide.style.left = horizontalPosition + 'px';
			}
			else {
				slide.style.right = horizontalPosition + 'px';
				slide.style.left = '';
			}
			
			if (this.options.onBackground) {
				// set the background images for the on state //
				triggerBase.style.backgroundImage = 'url(' + this.options.onBackground + ')'
				triggerBase.style.backgroundPosition = triggerOffsetLeft + 70 + 'px 0'
				triggerBase.style.backgroundRepeat = 'no-repeat';
			}
			
		},
	
		sendToBack : function(el) {
			
			// see if the animation is still running - if so, stop the effect so we only have one runnning //
			if (this.backEffect) this.backEffect.cancel();
			this.backEffect = new Effect.Fade(el, 
				{ duration: .5, 
				  to: .2 } )
		},
		
		sendToFront : function(el) {
			
			// see if the animation is still running - if so, stop the effect so we only have one runnning //
			if (this.backEffect) this.backEffect.cancel();
			this.backEffect = new Effect.Appear(el, 
				{ duration: .5, 
				  to: 1 })
				
		}
		
	}
	
// add the add-on methods to the main tab prototype //
Object.extend(InfoTabManager.prototype, InfoTabManager.AddOns)

// 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);
