/* 	
	
	Tabs by Marty Stake
	Requires prototype.js 1.6.  
	If you want effects, you need Scriptaculous as well.	
	
	These are really flexible.  You can do a lot of stuff more than just simple
	tabs - including accordians and multiple sets of content for one set of tabs.
	
	new Tabs( optional element selector, { 
		triggers: '.triggers li a',
		slides: '.slides div'
	} );
	
	<ul class="triggers">
		<li><a href="#">Tab One</a></li>
		<li><a href="#">Tab Two</a></li>
		<li><a href="#">Tab Three</a></li>
	</ul>
		
	<div class="slides">	
		<div>
			<h2>Tab One Header</h2>
			<p>Tab One Content</p>
		</div>
			
		<div>
			<h2>Tab Two Header</h2>
			<p>Tab Two Content</p>
		</div>
			
		<div>
			<h2>Tab Three Header</h2>
			<p>Tab Three Content</p>
		</div>
	</div>
	
	Version .72
	
*/

var TabManager = { 

	// we can use this to allow the tab sets to talk to each other before they completely "start" -- if you don't do this, then you will get errors that a tab set does not exist when you try to access it in any of the callback functions - you need to use startAll after you define all of the tab sets to get them running -- real example: if you want to make sure one tabSet doesn't start swapping until another is finished //
	
	instances: [],

	options : { 
		defer: false
	},
	
	defer: function() {
		this.options.defer = true;
	},
	
	getTabSet : function(index) {
		return this.instances[index];
	},
		 
	activate: function(tabSet) {
		tabSet.start[tabSet.options.mode].bind(tabSet)();		
	},

	activateAll: function() {
		this.instances.each(function(tabSet) {
			tabSet.start[tabSet.options.mode].bind(tabSet)();
		}.bind(this));
	}
		
}

if( typeof(Tabs) == 'undefined' )
	var Tabs = Class.create( { 
		
	initialize: function(element, options) {
	
		this.slideIDs = [];
		this.triggerIDs = [];
		
		TabManager.instances.push(this);
		
		this.options = {
			mode: 'firstOn',
			tabSetId: 'tabSet',
			triggers: '',
			triggerBaseNode: '',
			parentHasOnClass: true,
			buttonForward: '',
			buttonBack: '',
			buttonRotate: '',
			buttonPause: '',
			firstIndex: 1,
			action: 'click',
			onClass: 'on',
			noFollow: false,
			duration: 5, // in seconds
			effectDuration: .4,			
			showFirstEffect: false,
			autoPlay: false
			//defaultTab
		}
		
		if ( arguments.length > 1 ) {
			options.tabContainer = ( typeof element == 'string' ) ? $$(element)[0] : element;
			options.tabSetId = options.tabContainer.id || options.tabSetId;
			this.hasContainer = true;
		}
		else 
			options = element;
	
		Object.extend( this.options, options || {} );
	
		// set the first "on" slide //
		this.currentSlide = this.options.firstIndex ? this.options.firstIndex - 1 : 0;
		this.previousSlide = -1;
	
		// set up the triggers  //
		if ( this.options.triggers ) {
			if ( this.hasContainer )
				var triggers = (typeof this.options.triggers == 'string') ? this.options.tabContainer.select(this.options.triggers) : this.options.triggers;
			else 
				var triggers = (typeof this.options.triggers == 'string') ? $$(this.options.triggers) : this.options.triggers;
			
			this.triggers = this.setTriggers(triggers);
			this.totalSlides = this.triggers.length;
		} 
		
		// set up the data that switches when the trigger is clicked. // 
		// if we pass in something thats not an array, make it one. //
		
		if ( this.options.slides ) {
			if (typeof this.options.slides == 'string') 
				var slides = [ this.options.slides ];
			else {
				var slides = (typeof this.options.slides[0] == 'string') ? this.options.slides : [ this.options.slides ];
			}
		}
	
		 // AJAX Mode //
		
		if ( !this.options.remote ) {
			this.slides = this.setSlides(slides);
			this.totalSlides = this.slides[0].length;
		}
		else {
			Object.extend( Tabs.prototype, TabManager.AJAX )
			if ( this.options.remote.slideContainer ) {
				if ( this.hasContainer ) 					
					this.slideContainer = typeof ( this.options.remote.slideContainer == 'string' ) ? this.options.tabContainer.select(this.options.remote.slideContainer)[0] : this.options.remote.slideContainer;
				else 
					this.slideContainer = typeof this.options.remote.slideContainer == 'string' ? $$(this.options.remote.slideContainer)[0] : this.options.remote.slideContainer;
			}
			else {
				var scParent = $(this.triggers[0]).up(1);
				this.slideContainer = scParent.insert( { after: new Element('div', { id: this.options.tabSetId + '-remote-container', className : 'remote-container' } ) });
				this.slideContainer = $$('#' + this.options.tabSetId + '-remote-container')[0];
			}
			
			this.slideContainer.setStyle( { display: 'block' });
		}
		
		// if there are any buttons referenced in the options //
		if (this.options.buttonForward || this.options.buttonBack || this.options.buttonRotate || this.options.buttonPause) {
			Object.extend(Tabs.prototype, TabManager.Controls)
			this.setButtons();
		}
		
		if (this.options.verticalConstrain) {
			Object.extend(Tabs.prototype, TabManager.Placement)
		}
		
		if (this.options.autoPlay) {
			this.autoPlayCounter = (this.options.mode == 'contained' || this.options.mode == 'collapsed') ? -1 : 0;
			if (this.options.autoPlay == 'once') {
				this.options.autoPlayOnceThru = true;
			}
		}
	
		// user function call before start //
		if (typeof this.options.beforeStart == 'function')
			this.options.beforeStart.apply(this, arguments);
			
		if ( this.options.defaultTab ) {
			this.defaultTab = ( typeof this.options.defaultTab == 'string' ) ? $$(this.options.defaultTab) : this.options.defaultTab;
		}
		
		// start the TabManager on the chosen mode if not manually starting //
		if ( !TabManager.options.defer ) {
			this.start[this.options.mode].bind(this)(); 
		}
		
	},
	
	
/*	Property: start
	Pick a display mode based on a user defined option.
*/

	start: {
		
		// Mode: firstOn
		// Show the first tab based on the firstIndex option
		
		'firstOn' : function() {
			this.showFirst();
			if (this.options.autoPlay) this.startAutoPlay();
		},
		
		// Mode: collapsed / collapsable
		// All slides are off at the outset, then once one tab is activated, they stay on
		
		'collapsed' : function() {
			this.currentSlide = -1;
			if (window.location.href.split('#')[1]) this.showFirst();
			if (this.options.autoPlay) this.startAutoPlay();
			this.options.showFirstEffect = true;
		},
		
		'collapsable' : function() {
			this.currentSlide = -1;
			if (window.location.href.split('#')[1]) this.showFirst();
			if (this.options.autoPlay) this.startAutoPlay();
			this.options.showFirstEffect = true;
		},
		
		// Mode: contained
		// the slide will show up and stay on until leaving the provided container 
		// or on hover in-and-out.  This lets you have a default background state
		
		'contained' : function() {
			Event.observe(this.options.tabContainer, 'mouseout', this.detectExit.bindAsEventListener(this, this.options.tabContainer));
			this.currentSlide = -1;
			if (this.options.autoPlay) this.startAutoPlay();
		}
	
	},
	
/*	Method: setTriggers
	Set up the elements on the page that will control the tab structure - the action on these will cause the "slides" to change.
	
	Arguments:
	els - an array of elements
	
	Returns: 
	the number of tabs
*/		
	
	setTriggers: function(els) { 
		
		// in case we pass in a native nodeList //
		els = $A ( els );
		
		els.each(function(el, counter) {
			
			this.triggerIDs[counter] = el.id = el.id || this.options.tabSetId + '-tr-' + counter;
		
			// hide the initial state
			this.getBase(el.id).removeClassName(this.options.onClass);				
			
			// call before setting up - passes the trigger element //
			if (typeof this.options.beforeSetup == 'function')
					this.options.beforeSetup.apply(this, [els[counter]]);
			
			Event.observe(el, this.options.action, this.swap.bind(this) );
			
			// make sure you dont follow the link if the option is set that way //
			if ( (this.options.action != 'click') && (this.options.noFollow) ) 
					Event.observe(el, 'click', function(e) { Event.stop(e) } )
		
			el.style.outline = 'none';
			el.style.cursor = 'pointer';
		
		}.bind(this) )
		
		return els;
	},
	
/*	Property: setSlides
	Set up the elements on the page that will show and hide based on interaction with the triggers.
	
	Arguments:
	els - an array of ids or a single id
	
	Returns: 
	the number of slides
*/		

	setSlides: function(slides) { 
	
		slides.each(function(set, setCounter) {
	
			this.slideIDs[setCounter] = [];
			
			if ( this.hasContainer ) 
				var slideSet = (typeof set == 'string') ? this.options.tabContainer.select(set) : set;
			else 
				var slideSet = (typeof set == 'string') ? $$(set) : set;
				
			// in case we pass in a native nodeList //
			slideSet = $A ( slideSet );	
				
			slideSet.each(function(subEl, counter) {
				
				this.slideIDs[setCounter][counter] = $(subEl).id = $(subEl).id || this.options.tabSetId + '-sl-' + setCounter + '-' + counter;
				$(subEl).style.display = 'none'; // hide em all and set up the initial "on" so it wont goof up the page if JS is off //
					
			}.bind(this) );
		
		}.bind(this) );
		
		return this.slideIDs;

	},

/*	Property: getIndex
	Get the index of an element based on a node.  If no node, use the set of triggers
	
	Arguments:
	node - the base node holding the element
	el - the element we want to find the index of
	
	Returns: 
	the index
*/			
	
	getIndex: function(node, el) { // takes an element //
				
		if (el == null) {
			el = node;
			node = this.triggers;
		}
		// do this in case we need to use spans or ems or whatever //	
		while (node.indexOf(el) < 0) {
			el = el.parentNode;
		}
		
		return node.indexOf(el);
	},
	
/*	Property: getBase
	Get the wrapper element for a specific id.  Based on user options for parentHasOnClass and triggerBaseNode.
	
	Options:
	- if parentHasOnClass is true, the immediate parent element is returned
	- if there is a triggerBaseNode return that element
	- if neither is true, return the original element
	
	Arguments:
	id - the id of the trigger
	
	Returns: 
	the element which we will apply a class
*/			
	
	getBase: function(id) { 
	
		// the getBase returns the extended 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);
			}
		}
		
	},
	
	getCurrentTrigger: function() {
		return this.triggers[this.currentSlide];
	},
	
	
/*	Property: swap
	Switch the slides based on the trigger acted upon and stop any automatic looping
	
	Arguments:
	e - event / which is turned into an index 	
*/
	
	swap: function(e) { 
		
		// if we are showing an effect just cut out so we cant click a button a million times and mess up the effects //
		
		if ( this.isAnimating(e) ) return;
		
		switch(typeof e) {
			case 'number':
				var next = e;
				break
			case 'string':
				var next = this.getIndex(this.slideIDs[0], e);
				break
			default:
				var next = this.getIndex(this.triggers, Event.element(e));
				break
		}
		
		var current = this.currentSlide;
		
		if (current != next) {
			this.currentSlide = next;
			this.toggle(current, next);
		}
		else {
			if (this.options.mode == 'collapsable') {
				this.reset();
			}
		}
		
		// dont go to the linked page and stop default action //
		if(e && e.stop) e.stop();
		// if we are rotating, override it and shut it off //
		this.stopAutoPlay();	
		
	},
	
	
/*	Property: smartSwap
	Switch the slides based on automatic looping or button presses
	
	Arguments:
	e - event / which is turned into an index 	
	direction - whether to move forward or backward in the slide set.  Can be 'forward' or 'backward'.  If not provided default to 'forward'
	
*/
	
	smartSwap: function(e, direction) {
		
		// we will take the total number of els and auto go through them.

		// if we are showing an effect just cut out so we cant click a button a million times and mess up the effects //
		if ( this.isAnimating(e) ) return;
		
		var current = this.currentSlide;
	
		switch (direction) {
			case 'forward' :
				var next = (current >= this.totalSlides-1) ? 0 : current + 1;
				this.stopAutoPlay();
				break;
			case 'backward' :
				var next = (current == 0) ? this.totalSlides-1 : current - 1;
				this.stopAutoPlay();
				break;
			default:
				var next = (current >= this.totalSlides-1) ? 0 : current + 1;
				break;
		}
		
		// set to the next slide so we know where we are //
		this.previousSlide = current;	
		this.currentSlide = next;	
			
		if( !this._TabsPlayer) { // if we are auto-cycling, we don't want to use a general toggle //
			this.toggle(current, next);
		}

		// if we have the autoPlayFlag on, cycle through the elements only once //
		if (this.options.autoPlay == 'once' ) {
			if (this.autoPlayCounter < this.totalSlides) {
				// MS removed this.totalSlides -1 in the if statement to carousel to first one)
				this.autoPlayCounter++;
				this.toggle(current, next);
			}
			else { 
				this.stopAutoPlay();
				if (this.options.mode == 'contained' || this.options.mode == 'collapsed') { 
					this.reset(); 
				}
			}		
		}
			
		// dont go to the linked page if onclick //
		if(e && e.stop) e.stop();
	
	},
	

	toggle : function(current, next) { // takes indexes for the next and current slides //
		
		this.show(next);
		if (current >= 0 ) 
			this.hide(current);
		
	},
	
	show : function(e, index) { // takes an index //
		
		index = e;
		
		// user function call before showing //
		if (typeof this.options.beforeShow == 'function')
			this.options.beforeShow.apply(this, [this.getBase(this.triggers[index]), index]);
				
		if (this.defaultTab) {
			this.defaultTab.invoke('setStyle', { 'display' : 'none' } )
		}		
				
		if (this.options.triggers) { 

			/* look to see if we passed in an ID (to turn on the tab with the bookmark in the URL). */
			if (typeof index == 'string') {
				index = this.getIndex(this.triggerIDs, index);
			}
			
			var parentNode = this.getBase(this.triggers[index].id)
			parentNode.addClassName(this.options.onClass);
		}
		
		
		if (!this.options.remote) {
		
			this.slides.each( function(set, counter)  {
					// turn on vertical constraining for the wrapper -- you must have a height on the base wrapper (in the CSS) for this to work //
					if (this.options.verticalConstrain) this.verticalConstrain(index);
					slideId = set[index];
					this.change( $(slideId), counter );
			}.bind(this) )

		}
		
		else {
			// call getData with the index number and the either an html file or the rel attribute which points to a file.  TODO: loop backwards through the nodes to find the "a" with the rel.  We would do this if the trigger is a <span> or something like that. //
			var data = parentNode.getAttribute('rel') || this.triggers[index].getAttribute('rel') ||  this.triggers[index].parentNode.getAttribute('rel') || this.options.remote.url;
			this.getData(index, data);
		}

		// user function call after showing //
		if (typeof this.options.afterShow == 'function')
			this.options.afterShow.apply(this, [this.getBase(this.triggers[index]), index]);
			
		if ( this.options.cookie ) {
			var cookieValue = this.triggers[index].id;
			Cookie.create( this.options.cookie.name, cookieValue, this.options.cookie.days || 365 );
		}

	},
	
	hide : function(e, index) { // takes an index //
	
		index = e;
	
		// user function call before hiding //
		if (typeof this.options.beforeHide == 'function')
			this.options.beforeHide.apply(this, [this.getBase(this.triggers[index]), index]);
		
		
		if (!this.options.remote) {
		
			this.slides.each( function(set, counter)  {
					slideId = set[index];
					this.change( $(slideId), counter );
			}.bind(this) )
			
		}
		
		if ( (this.options.triggers && !this.options.effects) || (this.options.effects && !this.options.effects.slide) ) { 
			parentNode = this.getBase(this.triggers[index].id)
			$(parentNode).removeClassName(this.options.onClass);
		}

		// user function call after showing //
		if (typeof this.options.afterHide == 'function')
			this.options.afterHide.apply(this, [this.getBase(this.triggers[index]), index]);
		
	},
	
	change: function(el, setCounter) { // takes an element
	
		// if we start with all off, then dont do any type of change //
		if (!el) return; 
		
		// set up a parallel queue if we are running effects //
		if ( !this.effects && this.options.effects ) this.effects = [];
		
		// if no effects just toggle on and off //
		if ( !this.options.effects || !this.options.showFirstEffect || setCounter > 0 ) {
			el.style.display = ( (el.style.display) == 'block' ) ? 'none' : 'block';
			this.options.showFirstEffect = true;
			return true;
		}
	
		else {
		
			// show //
			if (el.style.display == 'none') {
				
				if (this.options.effects.fade) {
				
					this.showing = new Effect.Appear( el, 
						{ duration: this.options.effectDuration, 
						  to: .99999,
						  afterFinish: function() { this.showing = null; }.bind(this),
						  queue : { scope : this.options.tabSetId + 'show', limit : this.totalSlides } } );
				}
				
				if (this.options.effects.slide) {
					// thanks to stickman to get the smoothness worked out using scale //
					
					var scaleType = this.options.effects.orientation == 'horizontal' ? { scaleX: true, scaleY: false } : { scaleX: false, scaleY: true };
					
					if ( this.options.effects.orientation == 'horizontal' ) {
						el.setStyle({'width' : '0px', 'display' : 'block' });
					}
					else {
						el.setStyle({'height' : '0px', 'display' : 'block' });
					}
										
					var h = this.options.effects.height ? this.options.effects.height : el.scrollHeight;
					var w = this.options.effects.width ? this.options.effects.width : el.scrollWidth;
					
					this.effects.push(
						new Effect.Scale(el, 100, Object.extend ( {
							sync: true,
							scaleFrom: 0,
							scaleContent: false,
							scaleMode: { 
								originalHeight: h,
								originalWidth: w
							}
						}, scaleType ) )
					);
					 
				} 
				
					  
				// use the provided show animation function //
				if (this.options.showEffect) {
					this.options.showEffect.apply(this, arguments)
				}		  
						  
				return;
			}
				
			
			// hide //
			if (el.style.display == 'block') {
				
				if (this.options.effects.fade) {
				
					this.hiding = new Effect.Fade( el, 
						{ duration: this.options.effectDuration, 
						  to: 0,
						  afterFinish: function() { this.hiding = null }.bind(this),
						  queue : { scope : this.options.tabSetId + 'hide', limit : this.totalSlides } } );
				}
				
				if (this.options.effects.slide) {
				
					var scaleType = this.options.effects.orientation == 'horizontal' ? { scaleX: true, scaleY: false } : { scaleX: false, scaleY: true };
				
					this.effects.push(
						new Effect.Scale(el, 0, Object.extend( {
							sync: true,
							scaleContent: false,
							afterFinish: function() { el.setStyle( 
								{ 'display' : 'none', 'height' : 'auto' }); 
							
								if ( this.options.triggers ) { 
									var slide = this.slideIDs[0].indexOf(el.id);
									parentNode = this.getBase(this.triggers[slide]);									$(parentNode).removeClassName(this.options.onClass);
								} 
							
							
							}.bind(this)
						}, scaleType )  )
					
					);
					
			
				}
					
				// use the provided hide animation function //
				if (this.options.hideEffect) {
					this.options.hideEffect.apply(this, arguments)
				}
				
				if ( this.options.effects.slide && this.effects.length >= 2 ) {
			
					 this.showing = new Effect.Parallel(this.effects, {
							duration: this.options.effectDuration,
							queue : { 
									scope : this.options.tabSetId + 'slide', 
									limit : this.totalSlides 
								},  
							afterFinish: function() { this.effects = null; this.showing = null; }.bind(this)
					})
				
				}
				
				return;
								
			}
		}
	
		
	},
	
	isAnimating: function(e) {
	
		if ( this.showing || this.hiding || (this.remoteLoading && this.options.remote.useTrigger) ) {
		
			if(e && e.stop) e.stop();
			
			if ( (typeof e == 'number') || (typeof e == 'string') )  {
				return true;
			} // from flash or getURL or if you use inline js calls.  whoa. //
			
			if ( (this.showing || this.hiding) && this.options.effects.fade ) {
				
				if ( this.showing ) {
				
					this.showing.cancel();
					if ( this.options.effects.fade ) {
						this.showing.element.setOpacity(.99999).setStyle( { 'display' : 'block' })
					}
					
					this.showing = null;
				}
			
				if ( this.hiding ) {
					this.hiding.cancel();
					if ( this.options.effects.fade ) {
						this.hiding.element.setOpacity(0).setStyle( { 'display' : 'none' })
					}
					this.hiding = null;
				}
	
			}
			
			else {
				return true;
			}
		}
	
		return false;
		
	},
	
	
	startAutoPlay: function(e) {
		
		if (!this._TabsPlayer) {
			this._TabsPlayer = setInterval(this.smartSwap.bind(this), this.options.duration * 1000);
		}
		
		if(e && e.stop) {
			e.stop();
			this.options.autoPlayOnceThru = false;
		}
	
	},
	
	stopAutoPlay: function(e) {
		
		clearInterval(this._TabsPlayer);
		this._TabsPlayer = null;
		this.options.autoPlayOnceThru = false;
		if(e && e.stop) e.stop();
	
	},
	
	detectExit : function(e, base) {
	
		// emulate onMouseLeave//
		
		// get the element that you left //
		var toElement = e.relatedTarget || e.toElement;
		
		if (toElement == null) return;
		
		// check to see if a mouseover element is a child.  if so, return false so we dont run the mouse event //	
		if( ( $(toElement).childOf(base) ) || (base == toElement.id ) ) {
				return false;
			}
		
		// otherwise, you are leaving the box.  Reset the tabs. //
		this.reset(e);
	
	},
	
	reset : function(e) {
	
		// if we are showing an effect just cut out so we cant click a button a million times and mess up the effects //
		
		if ( this.isAnimating(e) ) return;
	
		this.triggerIDs.each(function(trigger) { 
			var parentNode = this.getBase(trigger)
			parentNode.removeClassName(this.options.onClass);
		}.bind(this))
			
		this.slideIDs[0].each(function(slide) {
			el = $(slide);
			if (el.style.display == 'block') this.change(el);
		}.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 (this.defaultTab) {
			this.defaultTab.invoke('setStyle', { 'display' : 'block' } )
		}		
				
		if(e && e.stop) e.stop();
		
		// reset currentSlide  //
		this.currentSlide = -1;
	
	},
	
	showFirst : function() {
	
		// if we link to a bookmark inside of a hidden tab, show it! //
		var bookmark = window.location.href.split('#');
		if ( bookmark[1] ) {
		
			var len = this.options.remote ? this.triggerIDs.length : this.slideIDs[0].length
			var bMark = $(bookmark[1]);
		
			for(var x = 0; x<len; x++) {
				if (bMark) {
					
					// if the id is inside of a hidden slide or the an actual tab itself //
					// also check the base trigger for the ID if not on the trigger itself //
			
					if ( (bMark).descendantOf(this.slideIDs[0][x]) || 
						(bookmark[1] == this.triggerIDs[x]) ||
						(bookmark[1] == this.getBase(this.triggerIDs[x]).id ) ) {
							this.currentSlide = x;
							this.show( this.currentSlide );
							return true;				
					}
				}
			}
		
		}
		
		
		if ( this.options.cookie ) {
			
			var cook = Cookie.read ( this.options.cookie.name );
			
			if ( this.triggerIDs.indexOf( cook ) != -1) { 
			
				this.currentSlide = this.getIndex( this.triggerIDs, cook );
				this.show( this.currentSlide );
				return true;
			}
		
		}
		
		if ( this.options.random ) {
		
			function rnd() {
				rnd.today=new Date();
				rnd.seed=rnd.today.getTime();
				
				rnd.seed = (rnd.seed*9301+49297) % 233280;
				return rnd.seed/(233280.0);
			}
		
			this.currentSlide = rand(this.totalSlides) - 1;
			
		}
		
		
		// no bookmark or cookie.  Use the options setting to turn on the first slide //
		this.show( this.currentSlide );
	}
	
} );

TabManager.Controls = {

		buttons: { 	'buttonForward' : { 
						name: 'forward-button', 
						type: 'forward',
						action: 'this.smartSwap'
				 	 },
				 	 
					'buttonBack' : { 
						name: 'backward-button', 
						type: 'backward',
						action: 'this.smartSwap'
				 	 },
				 	 
					'buttonRotate' : { 
						name: 'rotate-button', 
						type: 'rotate',
						action: 'this.startAutoPlay'
				 	 },
					'buttonPause' : { 
						name: 'pause-button', 
						type: 'pause',
						action: 'this.stopAutoPlay'
				 	 }
		},		

		setButtons: function() {
		
			var buttons = TabManager.Controls.buttons;
		
			for (buttonType in buttons) {
				
				if(this.options[buttonType]) {
					
					var _button = ( typeof this.options[buttonType] == 'string' ) ? $$(this.options[buttonType]) : this.options[buttonType];
			
					_button.each(function(button, count) {
					
						button.id = button.id || this.options.tabSetId + '-' + buttons[buttonType].name + '-' + count;
					
						Event.observe( button, 'click', eval(buttons[buttonType].action).bindAsEventListener(this, buttons[buttonType].type) );
					
						// user function call before hiding - passes the button element //
						if (typeof this.options.beforeSetup == 'function')
							this.options.beforeSetup.apply(this, [button]);
					
						button.style.cursor = 'pointer';
				
					}.bind(this) );
				
				}
			}
			
		}
}


TabManager.Placement = {
	
		verticalConstrain: function(index) {
			
			// hidden slides need to be positioned absolutely, and the wrapper relatively (or absolutely) - height is based on the wrapper holding the slides //
			
			var set = this.options.tabContainer;
			var slideOffset = this.options.verticalOffset || 0;
			
			var baseHeight = $(set).getHeight();
			
			var totalBasePadding = parseInt($(set).getStyle('padding-top')) + parseInt($(set).getStyle('padding-bottom'));
			var totalBaseBordersTop = parseInt($(set).getStyle('border-top-width')) || 0
			var totalBaseBordersBottom = parseInt($(set).getStyle('border-bottom-width')) || 0
		
			var totalBaseBorders = totalBaseBordersTop + totalBaseBordersBottom;
		
			baseHeight = baseHeight - totalBasePadding - totalBaseBorders;
			
			var parentNode = this.getBase(this.triggers[index].id)
			var triggerTop = parentNode.offsetTop;
		
			var slideId = this.slideIDs[0][index];
			var slideHeight = $(slideId).getHeight();
			
			var slideBasePadding = parseInt($(slideId).getStyle('padding-top')) + parseInt($(slideId).getStyle('padding-bottom'));
			var slideBordersTop = parseInt($(slideId).getStyle('border-top-width')) || 0
			var slideBordersBottom = parseInt($(slideId).getStyle('border-bottom-width')) || 0
			
			var slideBorders = slideBordersTop + slideBordersBottom;
			
			// include the borders and padding in the slide //
			slideHeight = slideHeight + slideBasePadding + slideBordersTop + slideBordersBottom;
		
			var useScroll = true;
			if (useScroll) {
				if (slideHeight > baseHeight) {
					$(slideId).style.height = baseHeight - slideBasePadding - slideBorders + 'px';
					$(slideId).style.overflow = 'auto';
					
				}
			}
			
			var useOffset = false;
			
			
			
			// if the height of the slide is less than the total height of the container, orient to the top. Else, pop to the bottom of the container //
			var verticalOrientation = ((triggerTop + slideHeight) < (baseHeight + slideOffset) ) ? 'top' : 'bottom';
		
			switch(verticalOrientation) {
				case 'top' :
					useOffset = true
				break
							
				case 'bottom': 
					useOffset = false //set this true to use an offset on the bottom ones //
				break
			}
			
			
			if (useOffset) {
				if (verticalOrientation == 'top') {
					var verticalPosition = ((triggerTop - slideOffset) > 0) ? triggerTop - slideOffset : -slideOffset;
				}
				else {
					// this is wrong //
					var verticalPosition = 0 + slideOffset;
				}
			}
			else {
				var verticalPosition = 0 - slideOffset;
			}
			
			if (verticalOrientation == 'top') {
				$(slideId).style.top = verticalPosition + 'px';
				$(slideId).style.bottom = 'auto';
			}
			else {
				$(slideId).style.top = 'auto';
				$(slideId).style.bottom = verticalPosition + 'px';
			}
				
		
	}
}


// extend the AJAX functions //
TabManager.AJAX = { 

			indicateLoad : function(state) {
				
				// show spinner //
				if (state == 'load') {
					var loading = '<p class=\"loading\">';
					loading += this.options.remote.spinner ? '<img src=\"' + this.options.remote.spinner + '\ " />' : 'Loading...'
					loading += '</p>';
					
					if( this.options.remote.useTrigger )  {
						this.triggerContents = this.getCurrentTrigger().innerHTML;
						this.getCurrentTrigger().innerHTML = loading;
					}
					else {
						$(this.slideContainer).innerHTML = loading;
					}
					
				}
				
				// hide spinner //
				else {
					if(this.options.remote.useTrigger) {
						this.getCurrentTrigger().innerHTML = this.triggerContents;
					}
					
					
				}

			},
			
			getData: function(index, url, options) {
					
				this.remoteLoading = true;	
				var cacheNum = this.options.remote.cache ? 0 : new Date().getTime();
				var params = this.triggers[index].href.toQueryParams();
				
				Object.extend( params, {
						slide: index,
						ms: cacheNum 
				});
					
				if (this.options.remote.indicateLoad) 
					this.indicateLoad('load');
				
				var req = new Ajax.Request(url, {
					method: 'get',
					parameters: params,
					onSuccess: function(request) {
						// run a callback to add any functionality you want after we show //
						if (typeof this.options.onRemote == 'function') {
							this.options.onRemote.apply(this, [request]);
						}
						else {			
							$(this.slideContainer).update(request.responseText); 
						
							// user function call after update // 
							if (typeof this.options.afterUpdate == 'function')
								this.options.afterUpdate.apply(this, [request]);
							
							if (this.options.remote.indicateLoad) 
								this.indicateLoad('done');
					
							this.remoteLoading = false;
						}
					}.bind(this),
					
					onFailure: function() { 
						alert('I in ur page messing up ur ajax'); 
					}
					
				  });
	
			}
		}
			
var Cookie = {

	create : function(name,value,days,path,domain) {
  
  		// Cookie.create('name', 'value', 365 );
  
		  if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		  }
		  else expires = "";
		  
		  var path = ((!path   || path=='') ? '/' : path)
		  var domain = ((!domain || domain=='') ? window.location.hostname : domain);
		 
		  document.cookie = name+"="+value+expires+"; path=" + path + "; domain=" + domain;
		
	},
	
	read : function(name) {
	
		  var nameEQ = name + "=";
		  var ca = document.cookie.split(';');
		  for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		  }
		  return null;

	},
	
	erase : function(name) {
	
		createCookie(name,"",-1);
		
	}

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