if(detectBrowser.modernBrowser()){




//
// StackedDeck is a type of blind/accordion that uses z-index stacking and 
// either horizontal or vertical offsetting to accomplish its slide effect.
//
var StackedDeck = Class.create();
StackedDeck.prototype = {	
	initialize: function(elements,containerElement,options){
		var _openHeight = null;
		var _sliceHeight = null;
		var _springLoadingSpeed = null;
		var _baseZIndex = null;
		this.reversed = false;
		this.zIndexStep = 1;
		if(typeof(options)=='object'){
			if(options['horizontal']==true || options['sideways']==true){
				this.sideways=true;
			}
			if(parseInt(options['zstep']) > -1){
				this.zIndexStep = options['zstep'];
			}
			if(options['dead_endcap']==true){
				this.deadEndCap = true;
			}
			if(options['endcap_covers_last_item']==true){
				this.endCapCoversLastItem = true;
			}
			// hack which applies "active_first" as well as "active" to the first li when it is active
			// and "inactive_first" as well as "inactive" for inactive
			if(options['apply_first_class']==true){
				this.applyFirstClass = true;
			}
		}
		if(typeof(containerElement)!='undefined'){
			// get class and acquire height from class. Otherwise, we default to 40/80
			var params = this.getCSSParams(containerElement);
			_sliceHeight = params['sliceHeight'];
			_openHeight = params['openHeight'];
			_springLoadingSpeed = params['springLoadingSpeed'];
			_baseZIndex = params['baseZIndex'];
			if(!(_springLoadingSpeed>=0)) { _springLoadingSpeed = null; }
			
			if(this.sideways==true){
				var styleObj = {'width':(((elements.length - 1) * _sliceHeight) + _openHeight) + "px"};
				containerElement.setStyle(styleObj);
			} else {
				containerElement.setStyle({'height':(((elements.length - 1) * _sliceHeight) + _openHeight) + "px"});
			}
			if(containerElement.hasClassName("reversed")) { this.reversed = true; }
		}

		this.sliceHeight = (_sliceHeight!=null)?(_sliceHeight):(40);
		this.openHeight = (_openHeight!=null)?(_openHeight):(80);		
		this.springloadDelay = (_springLoadingSpeed!=null)?_springLoadingSpeed:75;
		this.springloaded = (parseInt(this.springloadDelay) <= 0)?false:true;
		this.currentOpenIndex = null;
		this.isAnimating = false;
		this.nextEffect = null;
		this.switchFlag = false;
		this.baseZIndex = (_baseZIndex==null)?100:_baseZIndex;
		this.springloaderBasket = null;
		this.activators = []; // an array of activation functions for the sliders which can be triggered with slideToIndex()
		this.makeSliders(elements);
	},

	getCSSParams: function(containerElement){
		if(typeof(containerElement)!='undefined'){ // type check must stay here since some callers will have not checked containerElement's validity
			var numericClassNames = $A(containerElement.classNames()).select(function(item){ return parseInt(item) > -1; });
			if(numericClassNames.length >= 2){
				return {
					'sliceHeight':parseInt(numericClassNames[0]),
					'openHeight':parseInt(numericClassNames[1]),
					'springLoadingSpeed':parseInt(numericClassNames[2]),
					'baseZIndex':parseInt(numericClassNames[3])
				};
			}
		}
		return {};
	},

	// create a set of accordion sliders that are associated with each other
	makeSliders: function(elements){
		this.currentOpenIndex = elements.length;
		elements.each(function(item,i){
			var topLocation = ((i==0)?(0):((this.sliceHeight * (i-1)) + (this.openHeight))) + 'px';
			if(this.reversed==true) { topLocation = ((this.sliceHeight * i)) + 'px'; }
			var sliderStyle = {
				'zIndex':this.reversed?(this.baseZIndex + elements.length - (i*this.zIndexStep)):(this.baseZIndex + (i*this.zIndexStep)),
				'position':'absolute'
			};
			if(this.sideways){
				sliderStyle['left'] = topLocation;
			} else {
				sliderStyle['top'] = topLocation;
			}
			$(item).setStyle(sliderStyle);

			var activationFunc;
			activationFunc = this.activators[i] = this.makeActivationEventHandlerFunc(this.makeSlideFunc(elements,item,i));
			// quick hack for mouseup and mouse over
			if(item.getElementsByClassName('rangetitle').length > 0){
				var eventAction = 'mouseup';

				// this is a "dead" end panel with no hovers, no clicks, etc
				if(this.deadEndCap==true && i==elements.length-1){
				} else {
					if(i==0 && this.applyFirstClass==true){
						var obj = new mouseOverClassify({
							'ignore_position':true,
							'adderFunction':function(element){
								if(element.hasClassName("inactive")){
									element.addClassName("setstackitem_hover_first_inactive");	
								} else {
									element.addClassName("setstackitem_hover_first_active");
								}
							},
							'removerFunction':function(element){
								element.removeClassName("setstackitem_hover_first_active");
								element.removeClassName("setstackitem_hover_first_inactive");
							}
						});
						obj.classify(item, null);//'setstackitem_hover_first');
					} else {
						var obj = new mouseOverClassify({'ignore_position':true});
						obj.classify(item, 'setstackitem_hover');
					}
				}
			}else{
				var eventAction = 'mouseover';
			}
			
			if(this.springloaded){
				// springload this slider activation by waiting a bit and then checking if we're still on top of the same item
				// this is a "dead" end panel with no hovers, no clicks, etc
				if(this.deadEndCap==true && i==elements.length-1){
					// do nothing
				} else {
					Event.observe(item, eventAction, 
						function(ev){
							this.springloaderBasket = item;
							setTimeout(function(e){
								if(this.springloaderBasket==item) { activationFunc(e); }
							}.bind(this,ev),this.springloadDelay);
						}.bind(this)
					);
				}
			} else {
				Event.observe(item, eventAction, activationFunc);				
			}
		}.bind(this));
	},
	
	// check for a queued effect. If one is present cancel the current one and start the new one
	checkForAnimationChange: function(effect){
		if(this.switchFlag==true){
			this.switchFlag=false;
			this.haveStoppedAnimating();
			effect.cancel();
			if(typeof(this.nextEffect)=='function') { this.nextEffect(); }
		}
	},

	// reset animation flags back to initial state before a transition has begun.
	resetFlagsAndClosures: function(){
		this.nextEffect = null;
		this.switchFlag = false;	
	},

	// queue up the next direction for the sliders
	signalAnimationChange: function(nextAnimationFunc){
		if(this.isAnimating==false){
			nextAnimationFunc();
			this.resetFlagsAndClosures();
		} else {
			this.nextEffect = nextAnimationFunc;
			this.switchFlag = true;
		}
	},
	
	// lock access to animation
	haveStartedAnimating: function(effect){
		this.isAnimating = true;
	},

	// unlock access to animation
	haveStoppedAnimating: function(effect,itemIndex){
		this.currentOpenIndex = itemIndex;
		this.isAnimating = false;
	},

	// create an event handler for accordion activation
	makeActivationEventHandlerFunc: function(runEffectFunc) {
		return function(ev){
			//Event.stop(ev);
			this.signalAnimationChange(runEffectFunc);
		}.bind(this);
	},

	slideToIndex: function(index){
		if(typeof(this.activators[index])=='function'){
			this.activators[index]();
		}
	},

	// create animation func for sliding effect
	makeSlideFunc: function(elements, currentElement, i){
		var getOpenLocation = function(j,sliceHeight,itemElement){
			return j * sliceHeight; // # of items "above/before" j is j items
		}.bind(this);
		
		var getClosedLocation = function(j,sliceHeight,openHeight,itemElement){
			if(j==0) { return this.reversed?(-sliceHeight):0; }
			// endCapCoversLastItem AND if this is the last panel AND previous panel is open
			if(this.endCapCoversLastItem && j==elements.length - 1 && itemElement.previous()==currentElement) {
				// end cap should cover last item of previous panel
				// We check how many elements less than 10 the previous set contains and nudge by however many
				// missing panels there are in the previous panel.
				var previousPanelContents = itemElement.previous().getElementsBySelector("ul.imagerange")[0];
				var params = this.getCSSParams(previousPanelContents);
				if(parseInt(params['sliceHeight']) > 0 && parseInt(params['openHeight']) > 0){
					// note: this won't work for a stack-in-a-stack-in-a-stack. It will only work for 2-deep. (stack-in-a-stack)
					var nudgeFactor = (10 - previousPanelContents.getElementsBySelector("li.stackitem").length) * (params['sliceHeight']);
					var result = this.reversed?((((j - 1) * sliceHeight) + openHeight)  - sliceHeight):(((j - 1) * sliceHeight) + openHeight);
					return result - nudgeFactor;
				} else {
					return this.reversed?((((j - 1) * sliceHeight) + openHeight)  - sliceHeight):(((j - 1) * sliceHeight) + openHeight);
				}
			}
			if(i < j) { return this.reversed?((((j - 1) * sliceHeight) + openHeight)  - sliceHeight):(((j - 1) * sliceHeight) + openHeight); }
			return this.reversed?((j * sliceHeight) - sliceHeight):(j * sliceHeight);
		}.bind(this);

		// return a function that makes element, the i'th element, slide to its open location. All other elements should slide to their locations too
		return function(){
			new Effect.Parallel(
				elements.collect(function(item,itemIndex){
					var fxOptions = {
						'x':(item==currentElement)?0:0,
						'y':((item==currentElement)?(getOpenLocation(itemIndex,this.sliceHeight,item)):(getClosedLocation(itemIndex,this.sliceHeight,this.openHeight,item))),
						'mode':'absolute',
						'sync':true,
						'beforeStart':function(effect){
							if(effect.element==currentElement){
								if(itemIndex==0 && this.applyFirstClass==true){
									effect.element.removeClassName("inactive_first");
									effect.element.addClassName("active_first");
								}
								effect.element.removeClassName("inactive");
								effect.element.addClassName("active");
							} else {
								if(itemIndex==0 && this.applyFirstClass==true){
									effect.element.removeClassName("active_first");
									effect.element.addClassName("inactive_first");
								}
								effect.element.removeClassName("active");
								effect.element.addClassName("inactive");
							}
						}.bind(this)
					};
					if(this.sideways){	// swap dimensions if we are travelling sideways
						var temp = fxOptions.y;
						fxOptions.y = fxOptions.x;
						fxOptions.x = temp;
					}
					return new Effect.Move(item,fxOptions);
				}.bind(this)),
				{
					'fps': 50, 'wait': false, 'duration': 200/1000, 
					'beforeUpdate': this.checkForAnimationChange.bind(this), 
					'beforeStart': this.haveStartedAnimating.bind(this), 
					'afterFinish': (function(itemIndex){
						return function(effect){
							this.haveStoppedAnimating(effect,itemIndex);
						}.bind(this);
					}.bind(this))(i)
				}
			);
		}.bind(this);
	}
};




}