/*
---
name: ElementSwap.js
description: Slide show interface to swap between any group of elements.
authors: Shaun Freeman
requires:
    core/1.2.4:
    - Class
    - Class.Extras
    - Element
    - Element.Event
    - Selectors
provides: [ElementSwap]
license: MIT-style license
version: 1.0.1
...
*/

var ElementSwap = new Class({
	
	Implements: [Options, Events, Chain],
	
	slides: [],
	
	isLooping: false,
	
 	options: {
		selectedClass: 'active',
		elementSwapDelay: 3,
		panelWrap: 'elementSwapWrap',
		panelWrapClass: 'elementSwap',
		events: true,
		activateOnLoad: 0,
		autoPlay: false,
		onActive: $empty,
		onClickView: $empty,
		onNext: $empty,
		onPrevious: $empty
	},
 
 	initialize: function(elements, options) {
		this.setOptions(options);
		
		this.slides = $$(elements);
		
		this.wrap = new Element('div', {
			'id': this.options.panelWrap,
			'class': this.options.panelWrapClass
		}).inject(this.slides[0], 'before').adopt(this.slides);
		
		this.activate(this.options.activateOnLoad);
		
		if (this.options.autoPlay) this.start();
	},
	
	attach: function(elements) {
		$$(elements).each(function(el) {
			var enter = el.retrieve('elementSwap:enter', this.elementEnter.bindWithEvent(this, el));
			var leave = el.retrieve('elementSwap:leave', this.elementLeave.bindWithEvent(this, el));
			var mouseclick = el.retrieve('elementSwap:click', this.elementClick.bindWithEvent(this, el));
			el.addEvents({
				mouseenter: enter,
				mouseleave: leave,
				click: mouseclick
			});
		}, this);
		return this;
	},
	
	detach: function(elements) {
		$$(elements).each(function(el) {
			el.removeEvent('mouseenter', el.retrieve('elementSwap:enter') || $empty);
			el.removeEvent('mouseleave', el.retrieve('elementSwap:leave') || $empty);
		});
	},
	
	activate: function(index) {
		if ($type(index) == 'string') index = this.slides.indexOf(this.slides.filter('[id='+index+']')[0]);
		if ($type(index) != 'number') return;
		this.show(index);
	},
	
 	show: function(index) {
		if ($type(index) != 'number') return;
		this.now = index;
		this.slides.removeClass(this.options.selectedClass);
		this.slides[this.now].addClass(this.options.selectedClass);
		this.fireEvent('onActive', [this.now, this.slides[this.now]]);
		//return this.now;
	},
	
	start: function() {
		if (this.options.events) this.attach(this.slides);
		this.isLooping = true;
		this.slideShow = this.next.periodical(this.options.elementSwapDelay * 1000, this);
	},
	
	stop: function(notPause) {
		this.clearChain();
		$clear(this.slideShow);
		this.isLooping = false;
		if (notPause) this.detach(this.slides);
	},
	
	next: function() {
		var el = this.slides[this.now].getNext();
		if (!el) el = this.slides[0];
		this.activate(this.slides.indexOf(el));
		this.fireEvent('onNext');
		return this;
	},
	
	previous: function() {
		var el = this.slides[this.now].getPrevious();
		if (!el) el = this.slides[this.slides.length - 1];
		this.activate(this.slides.indexOf(el));
		this.fireEvent('onPrevious');
		return this;
	},

	elementClick: function(e, el) {
		this.fireEvent('onClickView', [this.now, el]);
	},
	
	elementEnter: function(e, el) {
		this.stop();
	},
 
	elementLeave: function(e, el) {
		this.start();
	}
});









/*
---
name: Fx.MorphElement.js
description: Creates a morph effect on any element using predefined styles.
authors: Shaun Freeman
requires:
    core/1.2.4:
    - Fx.Morph
    - Fx.Transitions
provides: [Fx.MorphElement, Fx.MorphElement.Effects]
license: MIT-style license
version: 1.0.3
...
*/

Fx.MorphElement = new Class({
	
	Extends: Fx.Morph,
	
	options: {
		wrap: true,
		wrapClass: 'morphElementWrap',
		FxTransition : $empty,
		hideOnInitialize: false,
		width: null,
		height: null
	},
	
	initialize: function(el, options) {
		this.setOptions(options);
		this.parent(el, this.options.FxTransition);
		
		if (!this.options.width) this.setOptions({width: this.element.getWidth()});
		if (!this.options.height) this.setOptions({height: this.element.getHeight()});
		
		if (this.options.wrap) {
			this.wrap = new Element('div', {
				'id': this.element.get('id') + '_wrap',
				'class': this.options.wrapClass,
				'styles': {
					'height': this.options.height,
					'width': this.options.width,
					'overflow': 'hidden'
				}
			}).wraps(this.element);
		}
		
		this.element.setStyle('overflow', 'auto');
		
		if (this.options.hideOnInitialize) {
			this.element.store('fxEffect:flag', 'hide');
			this.start(this.options.hideOnInitialize);
		} else {
			this.element.store('fxEffect:flag', 'show');
			this.set({'opacity': [1,1]});
		}
	},
	
	start: function(fx) {
		
		var flag = this.element.retrieve('fxEffect:flag');
		
		var styles = {
			'margin-top': [0, 0],
			'margin-left': [0, 0],
			'width': [this.options.width, this.options.width],
			'height': [this.options.height, this.options.height],
			'opacity': [1, 1]
		};
		
		fx = fx.split(':');
		
		if (fx.length > 1) {
			fx = Fx.MorphElement.Effects[fx[0]][fx[1]][flag];
		} else {
			fx = Fx.MorphElement.Effects[fx[0]][flag];
		}
		
		$H(fx).each(function(hash, hashIndex){
			hash.each(function(item, index){
				if ($type(item) == 'string') {
					hash[index] = item.substitute({'width': this.options.width, 'height': this.options.height});
				}
			}.bind(this));
			styles[hashIndex] = hash
		}.bind(this));
		
		this.element.store('fxEffect:flag', (flag == 'hide') ? 'show' : 'hide');
		
		return this.parent(styles);
	}
});

Element.Properties.morphElement = {

	set: function(options){
		var morphElement = this.retrieve('morphElement');
		if (morphElement) morphElement.cancel();
		return this.eliminate('morphElement').store('morphElement:options', $extend({link: 'cancel'}, options));
	},

	get: function(options){
		if (options || !this.retrieve('morphElement')){
			if (options || !this.retrieve('morphElement:options')) this.set('morphElement', options);
			this.store('morphElement', new Fx.MorphElement(this, this.retrieve('morphElement:options')));
		}
		return this.retrieve('morphElement');
	}

};

Element.implement({

	morphElement: function(props){
		this.get('morphElement').start(props);
		return this;
	}

});

Fx.MorphElement.Effects = $H({
	blind: {
		up: {
			hide: {'height': ['{height}', 0]},
			show: {
				'margin-top': ['{height}', 0],
				'height': [0, '{height}']
			}
		},
		down: {
			hide: {
				'margin-top': ['{height}'],
				'height': [0]
			},
			show: {'height': [0, '{height}']}
		},
		left: {
			hide: {'width': ['{width}', 0]},
			show: {
				'margin-left': ['{width}', 0],
				'width': [0, '{width}']
			}
		},
		right: {
			hide: {
				'margin-left': ['{width}'],
				'width': [0]
			},
			show: {'width': [0, '{width}']}
		}
	},
	slide: {
		up: {
			hide: {
				'margin-top': [0, '-{height}'],
				'width': ['{width}'],
				'height': ['{height}']
			},
			show: {'margin-top': ['{height}', 0]}
		},
		down: {
			hide: {
				'margin-top': [0, '{height}'],
				'width': ['{width}'],
				'height': ['{height}']
			},
			show: {'margin-top': ['-{height}', 0]}
		},
		left: {
			hide: {
				'margin-left': [0, '-{width}'],
				'width': ['{width}'],
				'height': ['{height}']
			},
			show: {'margin-left': ['{width}', 0]}
		},
		right: {
			hide: {
				'margin-left': [0, '{width}'],
				'width': ['{width}'],
				'height': ['{height}']
			},
			show: {'margin-left': ['-{width}', 0]}
		}
	},
	fade: {
		hide: {'opacity': [1, 0]},
		show: {'opacity': [0, 1]}
	}
});














/*
---
name: Fx.ElementSwap.js
description: Slide show interface for classes.
authors: Shaun Freeman
requires:
    fx_morphelement/1.0.3:
    - Fx.MorphElement
    - Fx.MorphElement.Effects
    elementswap/1.0.1:
    - ElementSwap
provides: [Fx.ElementSwap]
license: MIT-style license
version: 1.0.2
...
*/

Fx.ElementSwap = new Class ({
	
	Extends: ElementSwap,
 
	options: {
		TransitionFx: {
			transition: 'linear',
			duration: 'long',
			onStart: function() {
				this.element.setStyle('overflow', 'hidden');
			},
			onComplete: function() {
				this.element.setStyle('overflow', 'auto');
			}
		},
		showFx: 'slide:right',
		hideFx: 'slide:left',
		wait: true
	},
	
	firstRun: true,
	
	initialize: function(elements, options) {
		this.setOptions(options);
		
		this.slides = $$(elements);
		
		this.wrap = new Element('div', {
			'id': this.options.panelWrap,
			'class': this.options.panelWrapClass
		}).inject(this.slides[0], 'before').adopt(this.slides);
		
		this.attachFx(this.slides);
		
		this.activate(this.options.activateOnLoad);
		
		if (this.options.autoPlay) this.start();
	},
	
	attachFx: function(elements) {
		$$(elements).each(function(el) {
			el.get('morphElement', {
				wrap: false,
				width: document.id(this.options.panelWrap).getStyle('width'),
				height: document.id(this.options.panelWrap).getStyle('height'),
				FxTransition: this.options.TransitionFx,
				hideOnInitialize: this.options.hideFx
			});
		}, this);
		return this;
	},
	
	activate: function(index) {
		if ($type(index) == 'string') index = this.slides.indexOf(this.slides.filter('[id='+index+']')[0]);
		if ($type(index) != 'number') return;
		// panel fx here..
		if (this.firstRun) {
			this.show(index);
			this.firstRun = false;
		} else {
			if (this.options.wait) {
				this.getFx('hideFx').chain(
					function() {
						this.show(index);
					}.bind(this)
				);
			} else {
				this.getFx('hideFx');
				this.show(index);
			}
		}
	},
	
	show: function(index) {
		this.parent(index);
		this.getFx('showFx');
	},
	
	getFx: function(fx) {
		this.effect = this.slides[this.now].retrieve('morphElement');
		return this.effect.start(this.options[fx]);
	},
	
	changeFx: function(elements, fx) {
		if (elements == 'all') elements = this.slides;
		fx = {
			FxTransition: fx,
			hideOnInitialize: false
		};
		$$(elements).each(function(el) {
			var flag = el.retrieve('fxEffect:flag');
			opts = el.retrieve('morphElement:options');
			el.eliminate('fxEffect:flag').eliminate('morphElement');
			el.get('morphElement', $merge(opts, fx));
		}, this);
		this.slides[this.now].store('fxEffect:flag', 'hide');
	}
	
});

Element.implement({

	fxSwap: function(options) {
		return new Fx.ElementSwap(this.getChildren(),options);
	}

});
