/* -----------------------------------------------------------------
Script: 
	MooFlow.js v.0.2dev
	2008-03-26

Copyright:
	Copyright (c) 2007 Tobias Wetzel (ToBSn), <http://outcut.de/>

License:
	MIT-style license
	
ChangeLog:
	Added {
		Reflection via JS
		Load Images via JSON
		Load Images form HTML-Soure with Filter
		Skinable UI ['better support in progress ;)']
		onClickView Callback - returns a object obj{'coordinates', 'src','alt','...'} all image attributes and parent a href, rel and target
	}
	Changed {
		Class Initialization
		Slider inside click now works correctly
		blockt key input
		fullscreen
	}
	
Probs:
	Safari 1/2 canvas must be added to body before can paint the reflection :(

Tested:
	Safari 3 / Safari 2(no reflection)
	Firefox
	Opera 9
	IE 6
----------------------------------------------------------------- */
var SliderEx = new Class({
	Extends: Slider,
	setEx: function(step){
		this.step = step.limit(0, this.options.steps);
		this.fireEvent('onTick', this.toPosition(this.step));
		return this;
    },
	clickedElement: function(event){
		var dir = this.range < 0 ? -1 : 1;
		var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
		position = position.limit(-this.options.offset, this.full -this.options.offset);
		this.step = Math.round(this.min + dir * this.toStep(position));
		this.checkStep();
		this.end();
		this.fireEvent('onTick', position);
	}
});

Fx.TweenEx = new Class({
	Extends: Fx.Tween,
	render: function(element, property, value){
		this.fireEvent('onMotionChange', value[0].value);
		element.setStyle(property, this.serve(value, this.options.unit));
	}
});

Element.implement({
	reflect: function(arg){
		var i = new Element('img').setProperty('src', arg.src);
		if (Browser.Engine.trident) {
			i.style.filter = 'flipv progid:DXImageTransform.Microsoft.Alpha(opacity=30, style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy='+arg.height*0.3+')';
			i.setStyles({'width':'100%', 'height':'100%'});
			return new Element('div').adopt(i);
		} else {
			var can = new Element('canvas').setProperties({'width':arg.width, 'height':arg.height});
			if (can.getContext && !Browser.Engine.webkit419 ) {
				var ctx = can.getContext("2d");
				ctx.save();
				ctx.translate(0,arg.height-1);
				ctx.scale(1,-1);
				ctx.drawImage(i, 0, 0, arg.width, arg.height);
				ctx.restore();
				ctx.globalCompositeOperation = "destination-out";
				ctx.fillStyle = '#000';
				ctx.fillRect(0, arg.height*0.5, arg.width, arg.height);
				var gra = ctx.createLinearGradient(0, 0, 0, arg.height*0.5);					
				gra.addColorStop(1, "rgba(255, 255, 255, 1.0)");
				gra.addColorStop(0, "rgba(255, 255, 255, "+(1-0.3)+")");
				ctx.fillStyle = gra;
				ctx.fillRect(0, 0, arg.width, arg.height);
			}
			return can;
		}
	}
});

var MooFlow = new Class({

	Implements: [Events, Options],
	
	options: {
		onStart: Class.empty,
		onComplete: Class.empty,
		onCancel: Class.empty,
		onClickView: Class.empty,
		onAutoPlay: Class.empty,
		onAutoStop: Class.empty,
		reflection: 0.5,
		heightRatio: 0.6,
		startIndex: 0,
		interval: 3000,
		factor: 115,
		bgColor: '#000000',
		stylePath: 'mooflow.css',
		useCaption: false,
		useResize: false,
		useSlider: false,
		useWindowResize: false,
		useMouseWheel: false,
		useKeyInput: false,
		useViewer: false
	},
	
	initialize: function(element, options){
		this.MooFlow = element;
		this.setOptions(options);
		this.foc = 150;
		this.curI = this.options.startIndex;
		this.sli = null;
		this.interval = this.options.interval;
		this.factor = this.options.factor;
		this.isFull = false;
		this.isAutoPlay = false;
		this.loadIdle = false;
		this.enableShow = false;
		this.shower = null;

		if(!$chk($('MooFlowCSS'))){new Asset.css(this.options.stylePath,{id:'MooFlowCSS'});}
		if(this.options.useWindowResize){window.addEvent('resize', this.update.bind(this));}
		if(this.options.useMouseWheel && this.options.useSlider){this.MooFlow.addEvent('mousewheel', this.wheelTo.bind(this));}
		if(this.options.useKeyInput){document.addEvent('keydown', this.keyTo.bind(this));}
		
		this.getElements(this.MooFlow);
	},
	
	getElements: function(el){
		this.master = {'images':[]};
		this.els = el.getChildren();	
		if(this.els.length<=0) return;
		$$(this.els).each(function(e){
			var hash = $H(e.getElement('img').getProperties('src','title','alt','longdesc'));
			if(e.get('tag') == 'a'){hash.merge(e.getProperties('href','rel','target'));}
			this.master['images'].push(hash);
		}, this);
		this.clearMain();
	},
	
	loadJSON: function(url){
		if(!url || this.loadIdle) return;
		this.loadIdle = true;
		new Request.JSON({
			onComplete: function(data){
				if($chk(data)){
				this.master = data;
				this.curI = 0;
				this.clearMain();
				}else {this.fireEvent('onFailure');}
			}.bind(this),
			onFailure: function(){
				this.loadIdle = false;
				this.fireEvent('onChancel', 'Can not load JSON-Data!');
			}.bind(this)
		}, this).get(url);
	},
	
	remoteImg: function(url, filter){
		if(!url || !filter || this.loadIdle) return;
		this.loadIdle = true;
		new Request.HTML({
			onSuccess: function(tree, els, htm){
				this.getElements(new Element('div', {html: htm}).getChildren(filter));
				this.curI = 0;
			}.bind(this),
			onFailure: function(){
				this.loadIdle = false;
				this.fireEvent('onChancel', 'Can not load Remote Images!');
			}.bind(this)
		}, this).get(url);
	},
	
	clearMain: function(){
		if($chk(this.nav)){
			new Fx.Tween(this.nav, 'bottom', {
				onComplete: function(){
					this.nav.dispose();
					this.cap.dispose();
					this.MooFlow.empty();
					this.createAniObj();
				}.bind(this)
			}).start(-50);
		}
		if($chk(this.cap)){this.cap.fade(0);}
		if(!$chk(this.nav) && !$chk(this.cap)){
			this.MooFlow.empty();
			this.createAniObj();
		}
	},
	
	createAniObj: function(){
		this.aniObj = new Element('div').inject(this.MooFlow);
		this.aniFx = new Fx.TweenEx(this.aniObj, 'left', {
			transition: Fx.Transitions.Expo.easeOut,
			duration: 750,
			onMotionChange: function(value){
				this.process(value);
			}.bind(this),
			onStart: function(){
				this.enableShow = false;
				if(this.options.useViewer) this.hideLink();
			}.bind(this),
			onChancel: function(){			
				this.enableShow = false;
			}.bind(this),
			onComplete: function(){
				this.enableShow = true;
				if(this.options.useViewer) this.showLink();
			}.bind(this)
		});
		this.addLoader();
	},
	
	addLoader: function(){
		this.MooFlow.store('height', this.MooFlow.getSize().x*this.options.heightRatio);
		this.loader = new Element('div').addClass('loader').inject(this.MooFlow);
		this.MooFlow.setStyle('visibility', 'visible').addClass('load');
		new Fx.Tween(this.MooFlow, 'height', {
					duration: 800,
					onComplete: function (){
						this.preloadImg();
					}.bind(this)
				}).start(this.MooFlow.retrieve('height'));
	},
	
	preloadImg: function(){
		var imgs = [];
		this.master['images'].each(function(src){
			imgs.push(src['src']);
		});
		var loadedImages = new Asset.images(imgs, {
		    onComplete: function(){
				new Fx.Tween(this.loader, 'opacity', {
					duration : 1000,
					onComplete : function (){
						this.MooFlow.removeClass('load');
						this.loader.dispose();
						this.init();
						delete imgs;
					}.bind(this)
				}).start(0);		
		    }.bind(this),
			onProgress: function(counter, i){
				this.master['images'][i]['width'] = loadedImages[i].width;
				this.master['images'][i]['height'] = loadedImages[i].height;
				this.loader.set('html', (counter+1) + ' / ' + imgs.length);
			}.bind(this)
		}, this);
	},
	
	init: function(){
		this.iL = this.master['images'].length-1;
		this.master['images'].each(function(image, i){
			var div = new Element('div').addClass('imgDiv').setStyles({
				'position':'absolute',
				'display':'none',
				'height':'100%'
			});
			var img = new Element('img', {
				'src': image.src,
				'styles':{
					'vertical-align':'bottom',
					'width':'100%'
				}
			}).inject(div);
			
			var ref = new Element('img').reflect({
				'src': image.src,
				'ref': this.options.reflection,
				'height': image.height,
				'width': image.width
			}).inject(div).setStyles({'width':'100%','background-color': this.options.bgColor});
			
			var sho = new Element('div').addClass('show').setStyles({'display':'none','opacity':'0'}).inject(div);
			
			img.addEvent('click', this.clickTo.bind(this,[i]));
			img.addEvent('dblclick', this.showImage.bind(this, [this.master['images'][i], i]));
			sho.addEvent('click', this.showViewer.bind(this, [this.master['images'][i], i]));
			
			div.inject(this.MooFlow);
			
			this.master['images'][i]['div'] = div;
			this.master['images'][i]['img'] = img;
			this.master['images'][i]['ref'] = ref;
			this.master['images'][i]['sho'] = sho;
			
		}, this);
		this.createElements();
		return true;
	},
	
	createElements: function(){
		if(this.options.useCaption){
			this.cap = new Element('div').addClass('caption').set('opacity',0);
			this.MooFlow.adopt(this.cap);
			this.cap.fade(1);
		}
		
		this.nav = new Element('div').addClass('MooFlowNav').setStyle('bottom','-50px');
		var autoPlayCon = new Element('div').addClass('autoPlayCon');
		var sliderCon = new Element('div').addClass('sliderCon');
		var resizeCon = new Element('div').addClass('resizeCon');		
		if(this.options.useAutoPlay){
			var play = new Element('a').addClass('play').addEvent('click', this.play.bind(this));
			var stop = new Element('a').addClass('stop').addEvent('click', this.stop.bind(this));
			autoPlayCon.adopt(stop, play);
		}
		if(this.options.useSlider){
			this.sliPrev = new Element('a').addClass('sliderNext');
			this.sliNext = new Element('a').addClass('sliderPrev');
			this.slider = new Element('div').addClass('slider');
			this.knob = new Element('div').addClass('knob');
			this.knob.adopt(new Element('div').addClass('knobleft'));
			this.slider.adopt(this.knob);
			sliderCon.adopt(this.sliPrev,this.slider,this.sliNext);
			this.slider.store('parentWidth', sliderCon.getSize().x-this.sliPrev.getSize().x-this.sliNext.getSize().x);
		}
		if(this.options.useResize){
			var resize = new Element('a').addClass('resize');
			resize.addEvent('click', this.setScreen.bind(this));
			resizeCon.adopt(resize);
		}		
		this.nav.adopt(autoPlayCon,sliderCon,resizeCon);
		this.MooFlow.adopt(this.nav);
		
		this.nav.tween('bottom', 20);
		
		this.fireEvent('onStart');
		this.update();
	},
	update: function(){
		this.oW = this.MooFlow.getSize().x;
		this.sz = this.oW * 0.5;
		if(this.options.useSlider){	
			this.slider.setStyle('width',this.slider.getParent().getSize().x-this.sliPrev.getSize().x-this.sliNext.getSize().x-1);
			this.knob.setStyle('width',(this.slider.getSize().x/this.iL));
			this.sli = new SliderEx(this.slider, this.knob,{
				steps: this.iL
			}).set(this.curI);
			this.sli.addEvent('onChange', this.glideTo.bind(this));
			this.sliNext.addEvent('click', this.next.bind(this));
			this.sliPrev.addEvent('click', this.prev.bind(this));
		}
		this.glideTo(this.curI);
		this.loadIdle = false;
	},
	setScreen: function(){
		this.isFull = !this.isFull;
		if(this.isFull){
			this.holder = new Element('div').inject(this.MooFlow,'after');
			this.MooFlow.wraps(new Element('div').inject(document.body));
			this.MooFlow.setStyles({'position':'absolute','z-index':'30000','top':'0','left':'0','width':'100%','height':'100%'});
			if(this.options.useWindowResize){
				window.addEvent('resize', this.initResize.bind(this));
			}
		} else {
			this.MooFlow.wraps(this.holder);
			window.removeEvent('resize', this.initResize);
			this.MooFlow.setStyles({'position':'relative','top':'','left':'','width':'','height':this.MooFlow.retrieve('height')});
			this.slider.setStyle('width',this.slider.retrieve('parentWidth'));
		}
		this.update();
	},
	initResize: function(){
		this.update();
	},
	showImage: function(el, index){
		if(this.curI != index) return;
		var imageObject = {};
		imageObject['coords'] = el.img.getCoordinates();
		el.each(function(v, k){
			if($type(v) == 'number' || $type(v) == 'string')
			imageObject[k] = v;
		}, this);
		this.fireEvent('onClickView', imageObject);
	},
	showLink: function(){
		if(!$chk(this.master['images'][this.curI].rel)) return;
		this.master['images'][this.curI].sho.setStyle('top',this.master['images'][this.curI].img.getCoordinates().height+20)
		this.shower = this.master['images'][this.curI].sho.setStyle('display','block').fade(0.6);
	},
	hideLink: function(){
		if(!this.shower) return;
		this.shower.setStyles({'display':'none','opacity':'0'});
	},
	showViewer: function(el, index){
		if(this.curI != index && el.rel == 'image') return;
		var c = el.img.getCoordinates();
		var bigImage = new Asset.image(el.href ,{
			onload: function(){
				this.inject(document.body);
				this.addEvent('click', function(){this.dispose()});
				//this.setStyles({'left':c.left,'top':c.top,'width':c.width,'height':c.height,'position':'absolute'}); //changed by karl to keep resized images on top
				this.setStyles({'left':c.left,'top':c.top,'width':c.width,'height':c.height,'position':'absolute','z-index':'999999'});		
				var myEffect = new Fx.Morph(this, {duration: 'short', transition: Fx.Transitions.Sine.easeOut});
				myEffect.start({
				    'height': this.get('height'),
				    'width': this.get('width'),
					'left': window.getSize().x/2-this.get('width')/2
				});
			}
		});
	},
	prev: function(){
		if(this.curI > 0){
			this.clickTo(this.curI-1);
		}
	},
	stop: function(){
		$clear(this.autoPlay);
		this.isAutoPlay = false;
		this.fireEvent('onAutoStop');
		return true;
	},
	play: function(){
		this.autoPlay = this.auto.periodical(this.interval, this);
		this.isAutoPlay = true;
		this.fireEvent('onAutoPlay');
		return true;
	},
	auto: function(){
		if(this.curI < this.iL)
		this.next();
		else if(this.curI == this.iL)
		this.clickTo(0);
	},
	next: function(){
		if(this.curI < this.iL){
			this.clickTo(this.curI+1);
		}
	},
	keyTo: function(e){
		switch (e.code){
			case 37:
				this.prev();
				break;
			case 39:
				this.next();
				break;
			default:
				break;
		}
	},
	wheelTo: function(e){
		var d = e.wheel;
		if(e.preventDefault) e.preventDefault();		
		if(d > 0) this.prev();
		if(d < 0) this.next();
	},
	clickTo: function(index){
		if(this.curI == index) return;	
		if(this.options.useSlider){
			this.sli.setEx(index);
		}
		this.glideTo(index);
	},
	glideTo: function(i){
		this.aniFx.cancel();
		this.curI = i;
		if(this.cap){
			this.cap.set('html', this.master['images'][this.curI]['title']);
		}
		this.aniFx.start(i*-this.foc);
	},
	process: function(x){
		var zI = this.iL, z, newW, newH, newT;
		this.master['images'].each(function(el){
			if(x<-this.foc*5||x>this.foc*5)
			el.div.setStyle('display', 'none');	
			else el.div.setStyle('display', 'block');
			
			z = Math.sqrt(10000 + x * x) + 100;
			newH = Math.round((el.height / el.width * this.factor) / z * this.sz);
			newT = Math.round(this.oW * 0.4 - newH);
			if(newH >= el.width * 0.5)
			newW = Math.round(this.factor / z * this.sz);
			else newW = Math.round(el.width * newH / el.height);
			
			el.img.setStyle('height', newH);
			el.ref.setStyle('height', newH);		
			el.div.setStyle('width', newW);
			el.div.setStyle('left', Math.round(((x / z * this.sz) + this.sz) - (this.factor * 0.5) / z * this.sz));
			el.div.setStyle('top', newT);							
			el.div.setStyle('zIndex', x < 0 ? zI++ : zI--);
			x += this.foc;
		}, this);
	}
});

window.addEvent('domready', function(){
	$$('.MooFlowieze').each(function(mooflow){
		mooflow.addClass('MooFlow');
		new MooFlow(mooflow);
	});
});
