/*------------------------------------------------------------------------------
Class: adobe.Carousel
Author:	
btapley
------------------------------------------------------------------------------*/
/*  
	Animator.js 1.1.9
	
	This library is released under the BSD license:

	Copyright (c) 2006, Bernard Sumption. All rights reserved.
	
	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:
	
	Redistributions of source code must retain the above copyright notice, this
	list of conditions and the following disclaimer. Redistributions in binary
	form must reproduce the above copyright notice, this list of conditions and
	the following disclaimer in the documentation and/or other materials
	provided with the distribution. Neither the name BernieCode nor
	the names of its contributors may be used to endorse or promote products
	derived from this software without specific prior written permission. 
	
	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
	ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
	ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
	DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
	OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
	DAMAGE.

*/
function Animator(A){this.setOptions(A);var B=this;this.timerDelegate=function(){B.onTimerEvent()};this.subjects=[];this.target=0;this.state=0;this.lastTime=null}Animator.prototype={setOptions:function(A){this.options=Animator.applyDefaults({interval:20,duration:400,onComplete:function(){},onStep:function(){},transition:Animator.tx.easeInOut},A)},seekTo:function(A){this.seekFromTo(this.state,A)},seekFromTo:function(B,A){this.target=Math.max(0,Math.min(1,A));this.state=Math.max(0,Math.min(1,B));this.lastTime=new Date().getTime();if(!this.intervalId){this.intervalId=window.setInterval(this.timerDelegate,this.options.interval)}},jumpTo:function(A){this.target=this.state=Math.max(0,Math.min(1,A));this.propagate()},toggle:function(){this.seekTo(1-this.target)},addSubject:function(A){this.subjects[this.subjects.length]=A;return this},clearSubjects:function(){this.subjects=[]},propagate:function(){var B=this.options.transition(this.state);for(var A=0;A<this.subjects.length;A++){if(this.subjects[A].setState){this.subjects[A].setState(B)}else{this.subjects[A](B)}}},onTimerEvent:function(){var C=new Date().getTime();var A=C-this.lastTime;this.lastTime=C;var B=(A/this.options.duration)*(this.state<this.target?1:-1);if(Math.abs(B)>=Math.abs(this.state-this.target)){this.state=this.target}else{this.state+=B}try{this.propagate()}finally{this.options.onStep.call(this);if(this.target==this.state){window.clearInterval(this.intervalId);this.intervalId=null;this.options.onComplete.call(this)}}},play:function(){this.seekFromTo(0,1)},reverse:function(){this.seekFromTo(1,0)},inspect:function(){var B="#<Animator:\n";for(var A=0;A<this.subjects.length;A++){B+=this.subjects[A].inspect()}B+=">";return B}};Animator.applyDefaults=function(C,B){B=B||{};var D,A={};for(D in C){A[D]=B[D]!==undefined?B[D]:C[D]}return A};Animator.makeArray=function(C){if(C==null){return[]}if(!C.length){return[C]}var A=[];for(var B=0;B<C.length;B++){A[B]=C[B]}return A};Animator.camelize=function(C){var E=C.split("-");if(E.length==1){return E[0]}var B=C.indexOf("-")==0?E[0].charAt(0).toUpperCase()+E[0].substring(1):E[0];for(var D=1,A=E.length;D<A;D++){var F=E[D];B+=F.charAt(0).toUpperCase()+F.substring(1)}return B};Animator.apply=function(C,B,A){if(B instanceof Array){return new Animator(A).addSubject(new CSSStyleSubject(C,B[0],B[1]))}return new Animator(A).addSubject(new CSSStyleSubject(C,B))};Animator.makeEaseIn=function(A){return function(B){return Math.pow(B,A*2)}};Animator.makeEaseOut=function(A){return function(B){return 1-Math.pow(1-B,A*2)}};Animator.makeElastic=function(A){return function(B){B=Animator.tx.easeInOut(B);return((1-Math.cos(B*Math.PI*A))*(1-B))+B}};Animator.makeADSR=function(D,B,C,A){if(A==null){A=0.5}return function(E){if(E<D){return E/D}if(E<B){return 1-((E-D)/(B-D)*(1-A))}if(E<C){return A}return A*(1-((E-C)/(1-C)))}};Animator.makeBounce=function(A){var B=Animator.makeElastic(A);return function(C){C=B(C);return C<=1?C:2-C}};Animator.tx={easeInOut:function(A){return((-Math.cos(A*Math.PI)/2)+0.5)},linear:function(A){return A},easeIn:Animator.makeEaseIn(1.5),easeOut:Animator.makeEaseOut(1.5),strongEaseIn:Animator.makeEaseIn(2.5),strongEaseOut:Animator.makeEaseOut(2.5),elastic:Animator.makeElastic(1),veryElastic:Animator.makeElastic(3),bouncy:Animator.makeBounce(1),veryBouncy:Animator.makeBounce(3)};function NumericalStyleSubject(B,C,E,D,A){this.els=Animator.makeArray(B);if(C=="opacity"&&window.ActiveXObject){this.property="filter"}else{this.property=Animator.camelize(C)}this.from=parseFloat(E);this.to=parseFloat(D);this.units=A!=null?A:"px"}NumericalStyleSubject.prototype={setState:function(E){var D=this.getStyle(E);var A=(this.property=="opacity"&&E==0)?"hidden":"";var B=0;for(var C=0;C<this.els.length;C++){try{this.els[C].style[this.property]=D}catch(F){if(this.property!="fontWeight"){throw F}}if(B++>20){return }}},getStyle:function(A){A=this.from+((this.to-this.from)*A);if(this.property=="filter"){return"alpha(opacity="+Math.round(A*100)+")"}if(this.property=="opacity"){return A}return Math.round(A)+this.units},inspect:function(){return"\t"+this.property+"("+this.from+this.units+" to "+this.to+this.units+")\n"}};function ColorStyleSubject(A,B,D,C){this.els=Animator.makeArray(A);this.property=Animator.camelize(B);this.to=this.expandColor(C);this.from=this.expandColor(D);this.origFrom=D;this.origTo=C}ColorStyleSubject.prototype={expandColor:function(B){var C,E,D,A;C=ColorStyleSubject.parseColor(B);if(C){E=parseInt(C.slice(1,3),16);D=parseInt(C.slice(3,5),16);A=parseInt(C.slice(5,7),16);return[E,D,A]}if(window.DEBUG){alert("Invalid colour: '"+B+"'")}},getValueForState:function(A,B){return Math.round(this.from[A]+((this.to[A]-this.from[A])*B))},setState:function(C){var A="#"+ColorStyleSubject.toColorPart(this.getValueForState(0,C))+ColorStyleSubject.toColorPart(this.getValueForState(1,C))+ColorStyleSubject.toColorPart(this.getValueForState(2,C));for(var B=0;B<this.els.length;B++){this.els[B].style[this.property]=A}},inspect:function(){return"\t"+this.property+"("+this.origFrom+" to "+this.origTo+")\n"}};ColorStyleSubject.parseColor=function(D){var A="#",C;if(C=ColorStyleSubject.parseColor.rgbRe.exec(D)){var B;for(var E=1;E<=3;E++){B=Math.max(0,Math.min(255,parseInt(C[E])));A+=ColorStyleSubject.toColorPart(B)}return A}if(C=ColorStyleSubject.parseColor.hexRe.exec(D)){if(C[1].length==3){for(var E=0;E<3;E++){A+=C[1].charAt(E)+C[1].charAt(E)}return A}return"#"+C[1]}return false};ColorStyleSubject.toColorPart=function(A){if(A>255){A=255}var B=A.toString(16);if(A<16){return"0"+B}return B};ColorStyleSubject.parseColor.rgbRe=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i;ColorStyleSubject.parseColor.hexRe=/^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;function DiscreteStyleSubject(B,C,E,D,A){this.els=Animator.makeArray(B);this.property=Animator.camelize(C);this.from=E;this.to=D;this.threshold=A||0.5}DiscreteStyleSubject.prototype={setState:function(C){var A=0;for(var B=0;B<this.els.length;B++){this.els[B].style[this.property]=C<=this.threshold?this.from:this.to}},inspect:function(){return"\t"+this.property+"("+this.from+" to "+this.to+" @ "+this.threshold+")\n"}};function CSSStyleSubject(D,M,J){D=Animator.makeArray(D);this.subjects=[];if(D.length==0){return }var A,N,B;if(J){B=this.parseStyle(M,D[0]);N=this.parseStyle(J,D[0])}else{N=this.parseStyle(M,D[0]);B={};for(A in N){B[A]=CSSStyleSubject.getStyle(D[0],A)}}var A;for(A in B){if(B[A]==N[A]){delete B[A];delete N[A]}}var A,H,E,I,L,K;for(A in B){var G=String(B[A]);var C=String(N[A]);if(N[A]==null){if(window.DEBUG){alert("No to style provided for '"+A+'"')}continue}if(L=ColorStyleSubject.parseColor(G)){K=ColorStyleSubject.parseColor(C);I=ColorStyleSubject}else{if(G.match(CSSStyleSubject.numericalRe)&&C.match(CSSStyleSubject.numericalRe)){L=parseFloat(G);K=parseFloat(C);I=NumericalStyleSubject;E=CSSStyleSubject.numericalRe.exec(G);var F=CSSStyleSubject.numericalRe.exec(C);if(E[1]!=null){H=E[1]}else{if(F[1]!=null){H=F[1]}else{H=F}}}else{if(G.match(CSSStyleSubject.discreteRe)&&C.match(CSSStyleSubject.discreteRe)){L=G;K=C;I=DiscreteStyleSubject;H=0}else{if(window.DEBUG){alert("Unrecognised format for value of "+A+": '"+B[A]+"'")}continue}}}this.subjects[this.subjects.length]=new I(D,A,L,K,H)}}CSSStyleSubject.prototype={parseStyle:function(B,C){var G={};if(B.indexOf(":")!=-1){var I=B.split(";");for(var E=0;E<I.length;E++){var D=CSSStyleSubject.ruleRe.exec(I[E]);if(D){G[D[1]]=D[2]}}}else{var A,H,F;F=C.className;C.className=B;for(var E=0;E<CSSStyleSubject.cssProperties.length;E++){A=CSSStyleSubject.cssProperties[E];H=CSSStyleSubject.getStyle(C,A);if(H!=null){G[A]=H}}C.className=F}return G},setState:function(B){for(var A=0;A<this.subjects.length;A++){this.subjects[A].setState(B)}},inspect:function(){var B="";for(var A=0;A<this.subjects.length;A++){B+=this.subjects[A].inspect()}return B}};CSSStyleSubject.getStyle=function(B,C){var A;if(document.defaultView&&document.defaultView.getComputedStyle){A=document.defaultView.getComputedStyle(B,"").getPropertyValue(C);if(A){return A}}C=Animator.camelize(C);if(B.currentStyle){A=B.currentStyle[C]}return A||B.style[C]};CSSStyleSubject.ruleRe=/^\s*([a-zA-Z\-]+)\s*:\s*(\S(.+\S)?)\s*$/;CSSStyleSubject.numericalRe=/^-?\d+(?:\.\d+)?(%|[a-zA-Z]{2})?$/;CSSStyleSubject.discreteRe=/^\w+$/;CSSStyleSubject.cssProperties=["azimuth","background","background-attachment","background-color","background-image","background-position","background-repeat","border-collapse","border-color","border-spacing","border-style","border-top","border-top-color","border-right-color","border-bottom-color","border-left-color","border-top-style","border-right-style","border-bottom-style","border-left-style","border-top-width","border-right-width","border-bottom-width","border-left-width","border-width","bottom","clear","clip","color","content","cursor","direction","display","elevation","empty-cells","css-float","font","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","height","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-top","margin-right","margin-bottom","margin-left","max-height","max-width","min-height","min-width","orphans","outline","outline-color","outline-style","outline-width","overflow","padding","padding-top","padding-right","padding-bottom","padding-left","pause","position","right","size","table-layout","text-align","text-decoration","text-indent","text-shadow","text-transform","top","vertical-align","visibility","white-space","width","word-spacing","z-index","opacity","outline-offset","overflow-x","overflow-y"];function AnimatorChain(C,A){this.animators=C;this.setOptions(A);for(var B=0;B<this.animators.length;B++){this.listenTo(this.animators[B])}this.forwards=false;this.current=0}AnimatorChain.prototype={setOptions:function(A){this.options=Animator.applyDefaults({resetOnPlay:true},A)},play:function(){this.forwards=true;this.current=-1;if(this.options.resetOnPlay){for(var A=0;A<this.animators.length;A++){this.animators[A].jumpTo(0)}}this.advance()},reverse:function(){this.forwards=false;this.current=this.animators.length;if(this.options.resetOnPlay){for(var A=0;A<this.animators.length;A++){this.animators[A].jumpTo(1)}}this.advance()},toggle:function(){if(this.forwards){this.seekTo(0)}else{this.seekTo(1)}},listenTo:function(A){var B=A.options.onComplete;var C=this;A.options.onComplete=function(){if(B){B.call(A)}C.advance()}},advance:function(){if(this.forwards){if(this.animators[this.current+1]==null){return }this.current++;this.animators[this.current].play()}else{if(this.animators[this.current-1]==null){return }this.current--;this.animators[this.current].reverse()}},seekTo:function(A){if(A<=0){this.forwards=false;this.animators[this.current].seekTo(0)}else{this.forwards=true;this.animators[this.current].seekTo(1)}}};function Accordion(J){this.setOptions(J);var B=this.options.initialSection,G;if(this.options.rememberance){G=document.location.hash.substring(1)}this.rememberanceTexts=[];this.ans=[];var F=this;for(var C=0;C<this.options.sections.length;C++){var A=this.options.sections[C];var D=new Animator(this.options.animatorOptions);var I=this.options.from+(this.options.shift*C);var H=this.options.to+(this.options.shift*C);D.addSubject(new NumericalStyleSubject(A,this.options.property,I,H,this.options.units));D.jumpTo(0);var E=this.options.getActivator(A);E.index=C;E.onclick=function(){F.show(this.index)};this.ans[this.ans.length]=D;this.rememberanceTexts[C]=E.innerHTML.replace(/\s/g,"");if(this.rememberanceTexts[C]===G){B=C}}this.show(B)}Accordion.prototype={setOptions:function(A){this.options=Object.extend({sections:null,getActivator:function(B){return document.getElementById(B.getAttribute("activator"))},shift:0,initialSection:0,rememberance:true,animatorOptions:{}},A||{})},show:function(B){for(var A=0;A<this.ans.length;A++){this.ans[A].seekTo(A>B?1:0)}if(this.options.rememberance){document.location.hash=this.rememberanceTexts[B]}}};
/*------------------------------------------------------------------------------

Class: adobe.DataWheel
Author:
btapley

------------------------------------------------------------------------------*/
adobe.use("adobe.u");

adobe.DataWheel = Class.create({
/*------------------------------------------------------------------------------

	Method: initialize
	
	Parameters:
	oid - string or number
	options - object
	
	Returned Value:
	Array length

------------------------------------------------------------------------------*/

	initialize: function(oid, options) {
		this.oid = oid;
		this.options = {
			range: 1	
		};
		Object.extend(this.options, options);		
		this.range = this.options.range;
		this.revolutions = 0;
		this.data = [];
	},
/*------------------------------------------------------------------------------

	Method: add
	
	Parameters:
	data - object
	
	Returned Value:
	Array length

------------------------------------------------------------------------------*/
	add: function(data) {
		return this.data.push.apply(this.data, $A(data));
	},
/*------------------------------------------------------------------------------

	Method: read
	
	Returned Value:
	Array slice

------------------------------------------------------------------------------*/
	read: function() {
		return this.data.slice(0, this.range);
	},
/*------------------------------------------------------------------------------

	Method: next
	
	Rotate the data by the range set on initialization
	
	Returned Value:
	Array slice

------------------------------------------------------------------------------*/
	next: function(times) {
		var times = times||1;
		this.data.revolve(this.range*times);
		this.revolutions = this.revolutions+times;
		return this.read();
	},
/*------------------------------------------------------------------------------

	Method: back
	
	Rotate the data by the range on initialization
	
	Returned Value:
	Array slice

------------------------------------------------------------------------------*/
	back: function(times) {
		var times = times||1;
		this.data.revolve((this.range*times)*-1);
		this.revolutions = this.revolutions-times;
		return this.read();
	}
});
/*---------------------------------------------------------------------- 
	Namespace: u.element
----------------------------------------------------------------------*/
adobe.u.element = {
/*---------------------------------------------------------------------- 
	Function: create
	Create an element in the domain of another element
	
	Parameters:
	element - node
	name - nodename as string
	attrs(optional) - hash of attributes
	
	Returned Value:
	New Node instance
----------------------------------------------------------------------*/
	create: function(element, name, attrs) {
		var e = Element.getDocument(element).createElement(name);
		return (attrs) ? Object.extend(e, attrs||{}) : e;
	},
/*---------------------------------------------------------------------- 
	Function: getDocument
	Get the owner document with cross-browser support
	
	Parameters:
	element - node
	
	Returned Value:
	Document node
----------------------------------------------------------------------*/
	getDocument: function(element) {
		return element.ownerDocument || element.document;
	},
/*---------------------------------------------------------------------- 
	Function: getParent
	Get the parent node with cross-browser support
	
	Parameters:
	element - node
	
	Returned Value:
	Document node
----------------------------------------------------------------------*/
	getParent: function(element) {
		return (document.body.parentElement) ? element.parentElement : element.parentNode;
	},
/*---------------------------------------------------------------------- 
	Function: getParent
	Get the parent node with cross-browser support
	
	Parameters:
	element - node
	replacement - node
	
	Returned Value:
	Element argument
----------------------------------------------------------------------*/
	swap: function(element, replacement) {
		replacement.parentNode.replaceChild(element.parentNode.replaceChild(replacement, element));
		return element;
	},
/*---------------------------------------------------------------------- 
	Function: getMaxChildHeight
	Get the maximum height of this elements children or of a selection of descendants
	
	Parameters:
	element - node
	selector - string
	
	Returned Value:
	Number
----------------------------------------------------------------------*/
	getMaxChildHeight: function(element, selector) {
		var elements = (selector) ? 
				element.select(selector) : 
				element.childElements();
		
		return elements.inject(0, function(accum, element) {
      			return accum = Math.max(accum, element.scrollHeight)
		});
	},
/*---------------------------------------------------------------------- 
	Function: getTextContent
	Get the textContent property with cross-browser support
	
	Parameters:
	element - node
	
	Returned Value:
	String
----------------------------------------------------------------------*/
	getTextContent: function(element) {
		return element.textContent || element.text || element.innerText || "";
	}
}

Element.addMethods(adobe.u.element);

adobe.Carousel = Class.create((function() {
	
	var _orient = {
		x: 0,
		y: 1
	}
	
	var _states = {
		FORWARD: 1,
		BACKWARD: 0,
		ANIMATING: 2
	};
	
	var _attributes = [
		{
			offset: "offsetWidth",
			scroll: "scrollWidth",
			style: "width",
			pos: "left",
			cssname: "carousel-x",
			floatvalue: "left"
		},
		{
			offset: "offsetHeight",
			scroll: "scrollHeight",
			style: "height",
			pos: "top",
			cssname: "carousel-y",
			floatvalue: "none"
		}
	];
	
	var _state_translations = [];
	
	_state_translations[_states.FORWARD] = {
		backward: function(times) {
			this.sprites[this.incoming_sprite_index]
				.update(this.carousel_items.back(times).each(cp))
				.show();
			this.loadSubjects();
			this.sprites.invoke("updateSkipped");
			this.animator.reverse();
		},
		forward: function(times) {
			this.sprites.revolve(1);
			this.sprites[this.incoming_sprite_index]
				.update(this.carousel_items.next(times).each(cp))
				.show();
			this.loadSubjects();
			this.animator.play();
		}
	};
	_state_translations[_states.BACKWARD] = {
		backward: function(times) {
			this.sprites.revolve(1);
			this.sprites[this.incoming_sprite_index]
				.update(this.carousel_items.back(times).each(cp))
				.show();
			this.loadSubjects();
			this.sprites.invoke("updateSkipped");
			this.animator.reverse();
		},
		forward: function(times) {
			this.sprites[this.incoming_sprite_index]
				.update(this.carousel_items.next(times).each(cp))
				.show();
			this.loadSubjects();
			this.animator.play();
		}
	};
	_state_translations[_states.ANIMATING] = {
		backward: Prototype.emptyFunction,
		forward: Prototype.emptyFunction
	};
	

	function cp(el) {
		return el.cloneNode(true);
	}

	return {
/*------------------------------------------------------------------------------
	
	Method: initialize
	
	Parameters:
	container - element or id as string
	options - hash
	
	Options:
	axis - "x" or "y"
	clipping_mask - selector string
	duration - integer, default is 400
	oninitialized - Prototype.emptyFunction
	remember - boolean, default is false
	rotate - integer, default is 0
	select_button_back - selector string
	select_button_next - selector string
	select_item - selector string
	transition - Animator transition, default is Animator.tx.easeInOut
	visible - integer, default is 1
	offset - integer, default is 0
	
	Returned Value:
	None
	
	Example:
>	new adobe.Carousel("myCarousel")	
	
------------------------------------------------------------------------------*/
	initialize: function(container, options) {
			this.container = $(container);
			if(!$(container)) return;
			this.options = Object.extend({
				axis: "x",
				duration: 400, 
				offset: 0,
				remember: false,
				rotate: 0,
				select_mask: "",
				select_button_back: ".carousel-back",
				select_button_next: ".carousel-next",
				select_item: ".carousel-item",
				style_uninitialized: "",
				style_initialized: "",
				transition: Animator.tx.easeInOut,
				visible: 1
			}, options);
			
			this.currentstate = _states.FORWARD;
			this.outgoing_sprite_index = 1;
			this.incoming_sprite_index = 0;
			
/*---------------------------------------------------------------------------------------

	Layout

---------------------------------------------------------------------------------------*/
			
			var attributes = _attributes.clone();
			
			var orient_index = _orient[this.options.axis];
			
			if(orient_index > 0) {
				attributes.revolve(1);
			}
			
			var attributes1 = attributes[0];
			var attributes2 = attributes[1];
			
			this.clipping_mask = (this.options.select_mask) ? this.container.select(this.options.select_mask).first() : this.container;
		
			this.sprite_orient = attributes1.pos;
			var item_range = this.options.visible;
			this.sprite_size = this.clipping_mask[attributes1.offset] - (this.options.offset*2);
			var item_size = Math.floor(this.sprite_size/item_range);
			
			this.clipping_mask.addClassName(attributes1.cssname);
			
			var styles = {
				position: "absolute",
				zIndex: 0
			};
			styles[attributes1.style] = this.sprite_size.pixelate();
			
			this.sprites = [new adobe.CarouselSprite(1, styles), 
					new adobe.CarouselSprite(2, styles)];
			
			this.sprites.invoke("setParent", this.clipping_mask);
			
			var item_style = {};
			item_style[attributes1.style] = item_size.pixelate();
			item_style["cssFloat"] = attributes1.floatvalue;
			
			var items = this.clipping_mask.select(this.options.select_item);
			
			items = items.invoke("wrap", "div").invoke("setStyle", item_style);
			
			// this must follow setting the primiary size attributes
			
			this.clipping_mask.style[attributes2.style] = (items.inject(0, 
				function(accum, div) {
					return Math.max(accum, div[attributes2.scroll]);	
			})).pixelate();
			
			this.carousel_items = new adobe.DataWheel("test", {
				range: item_range
			});
			
			var removed = items.collect(function(div) {
				return this.clipping_mask.removeChild(div);
			}, this);
			
			this.carousel_items.add(removed);
			
			
			/* set initial state */
			
			var remember = Prototype.emptyFunction;
			var revolutions = this.options.rotate;
			
			if(this.options.remember) {
				adobe.use("adobe.Cookie");
				
				revolutions = parseInt(adobe.Cookie.get(this.container.identify()));

				// write cookies
				remember = (function() {
					adobe.Cookie.set(this.container.identify(), this.carousel_items.revolutions);
				}).bind(this);
			}
			
			if(revolutions) {
				var m = (revolutions > 0) ? "next" : "back";
				this.carousel_items[m](Math.abs(revolutions));
			}
			
			this.animator = new Animator({
				duration: this.options.duration,
				transition: this.options.transition,
				onComplete: (function() {
					this.sprites.invoke("updateSkipped");
					this.animator.clearSubjects();
					this.sprites[this.nextstate].hide();
					this.currentstate = this.nextstate;
					this.container.fire("carousel:endrevolve");
					remember(); // lexical reference so this must follow the state init
				}).bind(this)	
			});
			
			this.loadSubjects();
			this.animator.jumpTo(1);
			
			this.sprites[this.incoming_sprite_index]
				.update(this.carousel_items.read().collect(cp))
				.show();
				
			this.sprites[this.outgoing_sprite_index]
				.hide();
			
			this.container.select(this.options.select_button_next)
				.invoke("writeAttribute", "href", "#")
				.invoke("observe", "click", adobe.u.nonEvent)
				.invoke("observe", "click", this.next.bind(this, 1));
				
			this.container.select(this.options.select_button_back)
				.invoke("writeAttribute", "href", "#")
				.invoke("observe", "click", adobe.u.nonEvent)
				.invoke("observe", "click", this.back.bind(this, 1));
			
			
			if(this.options.style_initialized) {
				this.container.addClassName(this.options.style_initialized);
			}
			
			if(this.options.style_uninitialized && this.container.hasClassName(this.options.style_uninitialized)) {
				this.container.removeClassName(this.options.style_uninitialized);
			}
			
			this.container.fire("carousel:initialized");
		},
/*---------------------------------------------------------------------------------------

		Method: loadSubjects
		
		Returned Value:
		None

---------------------------------------------------------------------------------------*/
		loadSubjects: function() {
			this.animator.addSubject(new NumericalStyleSubject(this.sprites[0].element, 
									this.sprite_orient, 
									this.sprite_size+this.options.offset, 
									this.options.offset));
									
			this.animator.addSubject(new NumericalStyleSubject(this.sprites[1].element, 
									this.sprite_orient, 
									this.options.offset, 
									this.sprite_size*-1));
		},
/*---------------------------------------------------------------------------------------

		Method: next
		
		Parameters:
		times - Integer
		
		Events Fired:
		"carousel:startrevolve"
		
		Returned Value:
		None

---------------------------------------------------------------------------------------*/
		next: function(times) {
			this.container.fire("carousel:startrevolve");
			
			this.outgoing_sprite_index = 1;
			this.incoming_sprite_index = 0;
			
			var translate = this.currentstate;
			this.currentstate = _states.ANIMATING;
			this.nextstate = _states.FORWARD;
			_state_translations[translate].forward.call(this, times);					
		},
/*---------------------------------------------------------------------------------------

		Method: back
		
		Parameters:
		times - Integer
		
		Events Fired:
		"carousel:startrevolve"
		
		Returned Value:
		None

---------------------------------------------------------------------------------------*/
		back: function(times) {
			this.container.fire("carousel:startrevolve");
			
			this.outgoing_sprite_index = 0;
			this.incoming_sprite_index = 1;
			
			var translate = this.currentstate;
			this.currentstate = _states.ANIMATING; //prevent 
			this.nextstate = _states.BACKWARD;
			_state_translations[translate].backward.call(this, times);
		},
/*---------------------------------------------------------------------------------------

		Method: observe
		Attach a callback to an event.
		
		Observable Custom Events:
		carousel:startrevolve - fires before a carousel revolution
		carousel:endrevolve - fires after a carousel revolution
		
		Parameters:
		eventname - string
		func - function reference
		
		Returned Value:
		None

---------------------------------------------------------------------------------------*/
		observe: function(eventname, func) {
			this.container.observe(eventname, func);
		},
/*---------------------------------------------------------------------------------------

		Method: stopObserving
		Detach a callback from an event.
		
		Parameters:
		eventname - string
		func - function reference
		
		Returned Value:
		None

---------------------------------------------------------------------------------------*/
		stopObserving: function(eventname, func) {
			this.container.stopObserving(eventname, func);
		}
	};
})());

adobe.CarouselSprite = Class.create({
	initialize: function(oid, styles) {
		this.oid = oid;
		this.element = new Element("div");
		this.element.setStyle(styles);
		this.parent = null;
		this.skipped = [];
	},
	hide: function() {
		this.element.style.visibility = "hidden";
		return this;
	},
	show: function() {
		this.element.style.visibility = "visible";
		return this;
	},
	update: function(elements) {
		var n=0;
		while(n=this.element.childNodes[0]) {
			this.element.removeChild(n);	
		}
		for(var i=0;i<elements.length;i++) {
			var element = elements[i];
			if(element.getParent()) {
				this.skipped.push([element, i]);
				continue;
			}
			this.element.appendChild(element);
		}
		return this;
	},
	updateSkipped: function() {
		var s;
		while(s = this.skipped.shift()) {
			var skippednode = s[0],
			afternode = this.element.childNodes[s[1]];
			if(afternode) {
				this.element.insertBefore(skippednode,afternode);	
			} else {
				this.element.appendChild(skippednode);
			}	
		}
		return this;
	},
	setParent: function(parent) {
		this.parent = parent;
		parent.appendChild(this.element);
	}	
});

/*---------------------------------------------------------------------------------------

	Class: adobe.CarouselAutomator
	Provide transport controls for a carousel

---------------------------------------------------------------------------------------*/

adobe.CarouselAutomator = Class.create({
/*---------------------------------------------------------------------------------------

	Method: initialize
	
	Parameters:
	carousel - <Carousel> instance
	options - hash
	
	Options:
	interval - integer, default is 6000
	pauseOnHover - boolean, default is true
	stopOnFocus - boolean, default is true
	
	Returned Value:
	Class instance
	
	Example:
>	new adobe.CarouselAutomator(myCarousel)

---------------------------------------------------------------------------------------*/

	initialize: function(carousel, options) {
		
		this.carousel = carousel;
		this.options = Object.extend({
			interval: 6000,
			pauseOnHover: true,
			stopOnFocus: true
		}, options);
		this.interval;
		this.timer = null;
		this.playing = false;
		this.resetRotationInterval();
		
		if(this.options.pauseOnHover) {
			
			this.stopMethod = this.stop.bind(this);
			this.playMethod = this.play.bind(this);
			
			this.enableFocusPause();
			
			if(this.options.stopOnFocus) {
				
				var disableFocusHandler = this.tearDownAutomation.bind(this);
				
				this.carousel.observe("mousedown", disableFocusHandler);
				this.carousel.observe("keydown", disableFocusHandler);
				
			}
		}
	},
/*---------------------------------------------------------------------------------------

	Method: play
	Begin carousel automation.
	
	Returned Value:
	None

---------------------------------------------------------------------------------------*/
	play: function() {
		var _do = (function () {
			this.carousel.next();
		}).bind(this);
		this.timer = window.setInterval(_do, this.interval);
		this.playing = true;
	},
/*---------------------------------------------------------------------------------------

	Method: setRotationInterval
	
	Parameters:
	ms - Number
	
	Returned Value:
	None

---------------------------------------------------------------------------------------*/
	setRotationInterval: function(ms) {
		this.interval = parseInt(ms);
		if(this.playing) {
			this.stop();
			this.play();
		}
	},
/*---------------------------------------------------------------------------------------

	Method: resetRotationInterval
	Set rotation interval to the initial value
	
	Returned Value:
	None

---------------------------------------------------------------------------------------*/
	resetRotationInterval: function() {
		this.setRotationInterval(this.options.interval);
	},
/*---------------------------------------------------------------------------------------

	Method: stop
	Stop carousel automation.
	
	Returned Value:
	None

---------------------------------------------------------------------------------------*/
	stop: function() {
		window.clearInterval(this.timer);
		this.playing = false;
	},
/*---------------------------------------------------------------------------------------

	Method: tearDownAutomation
	stop auto-rotating and remove event listeners for pausing on focus
	
	Returned Value:
	None

---------------------------------------------------------------------------------------*/
	tearDownAutomation: function() {
		if(this.playing) { this.stop(); }
		this.disableFocusPause();
	},
/*---------------------------------------------------------------------------------------

	Method: disableFocusPause
	Removes event listeners attached by <enableFocusPause>. 
	
	Returned Value:
	Boolean indicating a successful event removal

---------------------------------------------------------------------------------------*/
	disableFocusPause : function() {
		if(!this.stopMethod || !this.playMethod) { return false; }
		this.carousel.stopObserving('mouseover', this.stopMethod);
		this.carousel.stopObserving('mouseout', this.playMethod);
		return true;
	},
	/*---------------------------------------------------------------------------------------

	Method: enableFocusPause
	On mouseover, the carousel automation will stop and on mouseout, it will resume. 
	
	Returned Value:
	Boolean indicating a successful event attachment

---------------------------------------------------------------------------------------*/
	enableFocusPause : function() {
		if(!this.stopMethod || !this.playMethod) {return false;}
		this.carousel.observe("mouseover", this.stopMethod);
		this.carousel.observe("mouseout", this.playMethod);
		return true;
	}
});

adobe.CarouselSafari2Fix = Class.create({
	initialize: function(carousel) {
		this.carousel = carousel;
		this.patch;
		this.boundfix = this.fix.bind(this);
	},
	fix: function(event) {
		container = event.element();
		if(this.patch) {
			this.patch = container.removeChild(this.patch);
		}
		this.patch = container.appendChild(document.createTextNode("."))
	},
	attach: function() {
		this.carousel.observe("carousel:endrevolve", this.boundfix);
	},
	remove: function() {
		this.carousel.stopObserving("carousel:endrevolve", this.boundfix);
	}
	
	
});
