var ODZ = {};
ODZ.Transitions = {
	linear:function(pos){ return pos; },
	exponential:function(pos) {	return 1-Math.pow(1-pos,2);	},
	elastic:function(pos) {
    	return -1*Math.pow(4,-8*pos) * Math.sin((pos*6-1)*(2*Math.PI)/2) + 1;
	}
};
ODZ.Slideshow = Class.create({
	playing : false,
	styling : {},
	initialize : function(element){
		this.element = $(element) || false;
		this.options = ODZ.Slideshow.defaults.update(arguments[1] || {});

		if( !this.element ) return false;

		/* Skin the Slideshow Presentation */
		this.skin = this.options.get('skin')
		this.element.addClassName( this.skin );

		this.width = this.options.get('dimensions').width;
		this.height = this.options.get('dimensions').height;

		/* Set Initial Index/Starting Slide */		
		this.start = this.options.get('start');
		this.index = this.start;

		/* Get our slides */
		if(this.options.get('items') == null){
			this.items = this.element.childElements();
		}else{
			if( Object.isString( this.options.get('items') ) ){
				this.items = this.element.select( this.options.get('items') );
			}else{
				this.options.get('items').each(function(src,i){
					that.items[i] = new Element('img',{'src':src});
				});
			}
		}
		
		this.items.invoke('setStyle','opacity:0');

		/* Sets up controls for different types of navigation */
		this._setupControls();

		this._setupListeners();
		/* Set up the transition effects */
		this.fx = this._setupTransition();
		/* Show first slide */
		this.show( this.current() );
		/* If Autoplay = true then start the show */
		if( this.options.get('autoplay') ){
			this.play();
		}
		
		return true;
		
	},

	size : function(){
		return this.items.size();
	},

	current : function(){
		return this.items[this.index];
	},

	next : function(){
		var newIndex = this.index + 1;
		if( newIndex == this.size() ){
			if(this.options.get('repeat')){
				this.setIndex(0);
			}
		}else{
			this.setIndex(newIndex);
		}
		return this.current();
	},

	previous : function(){
		var newIndex = this.index - 1;
		if( newIndex < 0 ){
			if(this.options.get('repeat')){
				this.setIndex(newIndex + this.size());
			}
		}else{
			this.setIndex(newIndex);
		}
		return this.current();
	},

	play: function() {
		this.playing = true;
    this.timer = setInterval(this.onTimerEvent.bind(this), this.options.get('delay') * 1000);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
		this.playing = false;
  },

	onNavigate: function(e){
		$(e).stop();
		var button = $(e).findElement(this.controlSelector);
		var action = button.readAttribute('href').split('#')[1];
		switch(action){
			case 'prev':
				this.movePrev();
				break;
			case 'next':
				this.moveNext();
				break;
			case 'play':
				this.play();
				break;
			case 'stop':
				this.stop();
				break;
			default:
				/* If it isn't a relative action, we go to particular slide */
				this.hide(this.current());
				this.setIndex(action);
				this.show(this.current());
				if(this.playing) this.resetTimer();
		};
	},

	onTimerEvent: function() {
		if (!this.currentlyExecuting) {
				try {
					this.currentlyExecuting = true;
					this.moveNext(this);
				}finally {
					this.currentlyExecuting = false;
				}
		}
	},

	resetTimer : function(){
		this.stop();
		this.play();
	},

	setIndex : function(value){
		this.index = parseInt(value);
	},

	moveNext : function(){
		this.hide( this.current() );
		this.show( this.next() );
		if(this.playing) this.resetTimer();
		//document.fire('image:changed');
	},

	movePrev : function(){
		this.hide(this.current());
		this.show(this.previous());
		if(this.playing) this.resetTimer();
		//document.fire('image:changed');
	},

	show : function(item){
		// Animates Item to style when item is shown
		item.morph(this.fx.show.style,this.fx.show.options);
		if( !Object.isUndefined( item._control ) ){
			item._control.addClassName('showing');
		}
	},

	hide : function(item){
		// Animates Item to Hidden Style
		item.morph(this.fx.out.style,this.fx.out.options);
		if(  !Object.isUndefined( item._control ) ){
			item._control.removeClassName('showing');
		}
	},

	_setupControls : function(){
		var type = this.options.get('navigation');

		switch(type){
			case 'paged':
			case 'numbered':
				/*page button for each slide*/
				var align = this.options.get('navAlign') || 'right';
				this.nav = new Element('div',{'class':this.options.get('classNames').navbar});
				this.nav.setStyle({'textAlign':align});
				this.controls = this._createPageControls();
				this.controls.each(function(ctrl){
					this.nav.insert(ctrl);
				}.bind(this));
				this.element.insert(this.nav);
				break;
			case 'slideshow':
				/*prev,next,play,stop*/
				var align = this.options.get('navAlign') || 'center';
				this.nav = new Element('div',{'class':this.options.get('classNames').navbar});
				this.nav.setStyle({'textAlign':align});
				this.controls = this._createShowControls();
				/*this.controls.invoke('observe','click',this.onNavigate.bindAsEventListener(this));*/
				this.controls.each(function(ctrl){
					this.nav.insert(ctrl);
				}.bind(this));
				this.nav.insert(new Element('div',{'style':'clear:both;'}));
				this.element.insert(this.nav);
				break;
			case 'normal':
			default:
				var align = this.options.get('navAlign') || 'center';
				this.nav = new Element('div',{'class':this.options.get('classNames').navbar});
				this.nav.setStyle({'textAlign':align});
				this.controls = this._createPrevNext();
				this.controls.each(function(ctrl){
					this.nav.insert(ctrl);
				}.bind(this));
				this.element.insert(this.nav);
		};

	},


	_createPrevNext : function(){
		var className = this.options.get('classNames').showControls;
		var prev = new Element('a',{'href':'#prev','class':className,'id':'showPrev','title':'Previous'});
		var next = new Element('a',{'href':'#next','class':className,'id':'showNext','title':'Next'});
		
		this.controlSelector = '.'+className;
		
		return $A([prev,next]);
		
	},

	_createPageControls : function(){
		var className = this.options.get('classNames').pageControls;
		
		var ctrls = $A();
		this.items.each(function(item,i){
			var a = new Element('a',{'href':'#'+i,'class':className,'id':'galleryItem'+i});
			a.update(i+1);
			item._control = a;
			a._item = item;
			ctrls.push(a);
		}.bind(this));
		
		this.controlSelector = '.'+className;
		return ctrls;		
	},

	_createShowControls : function(){
		var className = this.options.get('classNames').showControls;
		
		var play = new Element('a',{'href':'#play','class':className,'id':'movePlay','title':'Play'});
		var stop = new Element('a',{'href':'#stop','class':className,'id':'showStop','title':'Stop'});
		var prev = new Element('a',{'href':'#prev','class':className,'id':'showPrev','title':'Previous'});
		var next = new Element('a',{'href':'#next','class':className,'id':'showNext','title':'Next'});
		
		this.controlSelector = '.'+className;
		
		return $A([prev,stop,play,next]);
	},

	_setupListeners : function(){
		document.observe('move:next',this.moveNext.bindAsEventListener(this));
		document.observe('move:previous',this.movePrev.bindAsEventListener(this));
		this.controls.invoke('observe','click',this.onNavigate.bindAsEventListener(this));
		this.items.invoke('observe','click',this.onImageClick.bindAsEventListener(this));
	},
	
	onImageClick : function(e){
		var clickUrl = this.current().getElementsByTagName('a')[0].href
		if(clickUrl == 'undefined') return false;
		
		if(clickUrl.substring(clickUrl.length-1) != '#') {
			window.location.href=clickUrl;
		}
		return false;
	},

	_setupTransition : function(){
		// Convert our speed option to Effect duration
		this.duration = ((11 - this.options.get('speed'))*0.10);
		// Get our named transition to switch on 
		this.transition = this.options.get('transition');
		// Set All Slides to the Initial position of the current slide
		this.items.invoke('clonePosition', this.current());
		
		var effect = {};
		
		// cache some values for simplicity
		var po = this.current().positionedOffset();
		var t = po.top;
		var l = po.left;
		var w = this.current().getWidth();
		var h = this.current().getHeight();
		var l_toLeft = l - w;
		var l_fromRight = l + w;
		var t_fromBottom = t + h;
		var t_toTop = t - h;
		
		// switch on transition name
		// most effects are straigbt forward
		// but some need a little more
		// thus the individual options
		switch( this.transition ){
			case 'fade':
			default:
				this.styling.to = 'opacity:1.0';
				this.styling.from = 'opacity:0.0';
				this.items.invoke('setStyle',this.styling.from);
			
				effect = {
					'show': {
						'style': this.styling.to,
						'options': {
							duration: this.duration
						}
					},
					'out': {
						'style': this.styling.from,
						'options': {
							duration: this.duration
						}
					}
				};
				break;
			case 'fadeslide':
			case 'fadeslideleft':
				this.styling.to = 'left:'+l+'px;width:'+w+'px;opacity:1.0';
				this.styling.from = 'left:'+l_fromRight+'px;width:0px;opacity:0.0';
				
				effect = {
					'show':{
						'style':this.styling.to,
						'options':{duration:this.duration}
					},
					'out':{
						'style':'opacity:0.0',
						'options':{
							duration: this.duration,
							afterFinish: function(fx){
								fx.element.setStyle(this.styling.from);
							}.bind(this)
						}
					}
				};
				break;
			case 'fadeslideup':
				this.styling.to = 'top:'+t+'px;height:'+h+'px;opacity:1.0';
				this.styling.from = 'top:'+t_fromBottom+'px;height:0px;opacity:0.0';
				
				effect = {
					'show':{
						'style':this.styling.to,
						'options':{duration:this.duration}
					},
					'out':{
						'style':'opacity:0.0',
						'options':{
							duration: this.duration,
							afterFinish: function(fx){
								fx.element.setStyle(this.styling.from);
							}.bind(this)
						}
					}
				};
				break;
			case 'fadeslidedown':
				this.styling.to = 'height:'+h+'px;opacity:1.0';
				this.styling.from = 'height:0px;opacity:0.0';
				
				effect = {
					'show':{
						'style':this.styling.to,
						'options':{duration:this.duration}
					},
					'out':{
						'style':'opacity:0.0',
						'options':{
							duration: this.duration,
							afterFinish: function(fx){
								fx.element.setStyle(this.styling.from);
							}.bind(this)
						}
					}
				};
				break;
			case 'fadeslideright':
				this.styling.to = 'width:'+w+'px;opacity:1.0';
				this.styling.from = 'width:0px;opacity:0.0';
				
				effect = {
					'show':{
						'style':this.styling.to,
						'options':{duration:this.duration}
					},
					'out':{
						'style':'opacity:0.0',
						'options':{
							duration: this.duration,
							afterFinish: function(fx){
								fx.element.setStyle(this.styling.from);
							}.bind(this)
						}
					}
				};
				break;
			case 'slideleft':
				this.styling.to = 'left:'+l+'px;width:'+w+'px;opacity:1.0';
				this.styling.from = 'left:'+l_fromRight+'px;width:0px;opacity:0.0';
				
				effect = {
					'show':{
						'style':this.styling.to,
						'options':{duration:this.duration}
					},
					'out':{
						'style':'left:'+l_toLeft+'px;',
						'options':{
							duration: this.duration,
							afterFinish: function(fx){
								fx.element.setStyle(this.styling.from);
							}.bind(this)
						}
					}
				};
				break;
			case 'slideup':
				this.styling.to = 'top:'+t+'px;height:'+h+'px;opacity:1.0';
				this.styling.from = 'top:'+t_fromBottom+'px;height:0px;opacity:0.0';
				
				effect = {
					'show':{
						'style':this.styling.to,
						'options':{duration:this.duration}
					},
					'out':{
						'style':'top:'+t_toTop+'px;',
						'options':{
							duration: this.duration,
							afterFinish: function(fx){
								fx.element.setStyle(this.styling.from);
							}.bind(this)
						}
					}
				};
				break;
		};
		
		this.items.invoke('setStyle',this.styling.from);
		return effect;
	}
});

ODZ.Slideshow.defaults = $H({
	type : 'full',
	skin : 'gallery',
	dimensions: {width:'550px',height:'325px'},
	classNames:{
		items:'slide',
		navbar:'slideshow-nav',
		pageControls:'slide-control',
		showControls:'show-control'
	},
	items : null,
	start : 0, // which slide to start on
	autoplay : true, // start playing on instantiation
	repeat : true, // wrap from end to beginning for perpetual play
	delay : 3, // how long a slide shows before transitioning to next slide
	transition : 'fade', // Transition Effect
	speed : 1, //(slow)1 - 10(fast)
	navigation : 'normal', // normal | slideshow | paged
	navAlign: false // how the navigation is aligned in the navbar
});

ODZ.Carousel = Class.create(Abstract);
ODZ.Carousel.prototype = {
	initialize: function (scroller, slides, controls, options) {
		this.scrolling	= false;
		this.scroller	= scroller;
		this.slides		= slides;
		this.controls	= controls;

		this.options  = Object.extend({
			duration: 1.0,
			frequency: 3,
			itemsPerPage: 4,
			group:1,
			moveByPage: false,
			autoplay: false,
			controlClassName: 'carousel-control',
			jumperClassName: 'carousel-jumper' },
			options || {});

		this.slides.each(function(slide, index) {
			slide._index = index;
		});
		this.current = this.slides[0];
		if(this.slides.size() <= this.options.itemsPerPage){
			this.deactivateControls();
		}else{
			$('carouselContent').setStyle({'width':(this.slides.size()*100)+'px'});
			if( this.options.moveByPage ){
				this.options.group = this.options.itemsPerPage;
			}

			this.group = this.options.group;
			this.lastMoveIndex = this.slides.size() - this.options.itemsPerPage;
		}

		if (this.controls) {
			this.controls.invoke('observe', 'click', this.click.bind(this));
		}

		if (this.options.autoplay) {
			this.start();
			this.slides.invoke('observe', 'mouseover', this.pause.bind(this));
			this.slides.invoke('observe', 'mouseout', this.resume.bind(this));
		}

	},

	click: function (e) {

		$(e).stop();

		var element = e.findElement('a');

		if (!element.hasClassName('disabled')) {
			this.deactivateControls();
			if (element.hasClassName(this.options.controlClassName)) {
				eval("this." + element.rel + "()");
			} else {
				if (element.hasClassName(this.options.jumperClassName)) {
					this.moveTo(element, element.rel);
				}
			}
		}
    element.blur();
	},

	moveTo: function (trigger, element) {

		if (this.options.beforeMove && (typeof this.options.beforeMove == 'function')) {
			this.options.beforeMove();
		}

		if (this.controls && this.options.selectedClassName) {
			this.controls.each((function (elm) { elm.removeClassName(this.options.selectedClassName); }).bind(this));
			trigger.addClassName(this.options.selectedClassName);
		}

		this.previous= this.current ? this.current : this.slides[0];
		this.current = $(element);

		var scrollerOffset = this.scroller.cumulativeOffset();
		var elementOffset = this.current.cumulativeOffset();

		if (this.scrolling) {
			this.scrolling.cancel();
			}

		this.scrolling = new Effect.SmoothScroll(this.scroller, {
				duration: this.options.duration,
				x: (elementOffset[0] - scrollerOffset[0]),
				y: (elementOffset[1] - scrollerOffset[1]),
				queue: {position: 'end', limit: 1, scope: this.scroller.id},
				afterFinish: (function () {
					if (this.controls) {
						this.activateControls();
					}
				}).bind(this)});

		if (this.options.afterMove && (typeof this.options.afterMove == 'function')) {
			this.options.afterMove();
		}

		return false;

	},

	prev: function () {
		if (this.current) {
			var currentIndex = this.current._index;
			var prevIndex = (currentIndex == 0)
				? this.slides.length - this.options.itemsPerPage
				: currentIndex - this.group;
		} else {
			var prevIndex = this.slides.length - this.options.itemsPerPage;
		}
		if(prevIndex < 0){prevIndex = 0;}
		this.moveTo(this.controls ? this.controls[prevIndex] : false, this.slides[prevIndex]);
	},

	next: function () {
		if (this.current) {
			var currentIndex = this.current._index;
			var nextIndex = (this.slides.length - this.options.itemsPerPage == currentIndex)
				? 0
				: currentIndex + this.group;
		} else {
			var nextIndex = 0;
		}
		if(nextIndex > this.lastMoveIndex){ nextIndex = this.lastMoveIndex; }
		this.moveTo(this.controls ? this.controls[nextIndex] : false, this.slides[nextIndex]);
	},

	first: function () {
		var firstIndex = 0;
		if (this.current) {
			var currentIndex = this.current._index;
			}

		this.moveTo(this.controls[firstIndex], this.slides[firstIndex]);
	},

	last: function () {
		var lastIndex = (this.slides.length - this.options.itemsPerPage);
		if (this.current) {
			var currentIndex = this.current._index;
			}

		this.moveTo(this.controls[lastIndex], this.slides[lastIndex]);
		},

	toggle: function () {
		if (this.previous) {
			this.moveTo(this.controls[this.previous._index], this.slides[this.previous._index]);
			} else {
				return false;
				}
	},

	stop: function () { clearTimeout(this.timer); },

	start: function () { this.periodicallyUpdate(); },

	pause: function (event) {
		this.stop();
		this.activateControls();
	},

	resume: function (e) {
		if ($(e)) {
			var related = e.relatedTarget || e.toElement;
			if (!related || (!this.slides.include(related) && !this.slides.any(function (slide) { return related.descendantOf(slide); }))) {
				this.start();
				}
			} else {
				this.start();
				}
		},

	periodicallyUpdate: function () {
		if (this.timer != null) {
			clearTimeout(this.timer);
			this.next();
			}
		this.timer = setTimeout(this.periodicallyUpdate.bind(this), this.options.frequency * 1000);
	},

	deactivateControls: function () {
		this.controls.invoke('addClassName', 'disabled');
	},

	activateControls: function () {
		this.controls.invoke('removeClassName', 'disabled');
	}

};

ODZ.Accordion = Class.create({
	effect:{},
	fx:[],
	animation:{},
	active:null,
	busy: false,
	initialize:function(element){
		this.options = ODZ.Accordion.defaults.update(arguments[1] || {});
		this.element = $(element);
		this.accordion = this.options.get('accordion');
		this.duration = ((11 - this.options.get('speed'))*0.10);
		this.activeClass = this.options.get('activeClass');
		this.togglers = this.element.select(this.options.get('toggle'));
		var panels = this.element.select(this.options.get('panel'));
		if( this.togglers.size() == panels.size() ){
			panels.each(function(panel,i){
				this.togglers[i]._panel = panel.hide();
			}.bind(this));
			this.togglers.invoke(
				'observe',
				this.options.get('action'),
				this.onTogglerAction.bindAsEventListener(this)
			);
			this.togglers.invoke('setStyle',{'cursor':'pointer'});
		}else{
			return false;
		}
		this.animation = {
      'fade':{open:'appear',close:'fade'},
      'blind':{open:'blindDown',close:'blindUp'},
      'slide':{open:'slideDown',close:'slideUp'},
      'switch':{open:'',close:''},
      'grow':{open:'grow',close:'squish'},
      'drop':{open:'appear',close:'dropOut'},
      'options':{duration:this.duration,transition:this.options.get('transition')}
    };/*
		switch(this.options.get('effect')){
			case 'fade':
			default:
				this.effect = {open:'appear',close:'fade'};
				break;
			case 'blind':
				this.effect = {open:'blindDown',close:'blindUp'};
				break;
			case 'slide':
				this.effect = {open:'slideDown',close:'slideUp'};
				break;
			case 'switch':
				this.effect = {open:'',close:''};
				break;
			case 'grow':
				this.effect = {open:'grow',close:'squish'};
				break;
			case 'drop':
				this.effect = {open:'appear',close:'dropOut'};
				break;
		};*/
	},
	onTogglerAction:function(e){
		$(e).stop();
		if(this.busy) return;
		this.busy = true;
		var element = $(e).findElement(this.options.get('toggle'));
		if(element == this.active){
			this.deactivate(element);
		}else{
			if(this.active == null){
				this.activate(element);
			}else{
				if( this.accordion ){
					this.deactivate(this.active);
				}
				this.activate(element);
			}
		}
		this.busy = false;
	},
	open:function(element){
		var fn = this.animation[this.options.get('effect')].open || 'show';
		element._panel[fn](this.animation.options);
		if(this.options.get('effect') != 'fade'){
			element._panel.appear(this.animation.options);
		}
	},
	close:function(element){
		var fn = this.animation[this.options.get('effect')].close || 'hide';
		element._panel[fn](this.animation.options);
		if(this.options.get('effect') != 'fade'){
			element._panel.fade(this.animation.options);
		}
	},
	activate:function(element){
		this.active = element;
		element._panel.addClassName(this.options.get('activeClass'));
		if(this.busy){
			this.open(element);
		}else{
			element._panel.show();
		}
	},
	deactivate:function(element){
		this.active = null;
		element._panel.removeClassName(this.options.get('activeClass'));
		if(this.busy)
			this.close(element);
		else
			element._panel.hide();
	}
});
ODZ.Accordion.defaults = $H({
	accordion: true,
	speed: 7,
	action:'click',
	toggle:'dt',
	panel:'dd',
	direction:'vertical',
	width:null,
	height:null,
	effect:'blind',
	transition:ODZ.Transitions.exponential,
	activeClass:'open'
});