/**
Script: Slideshow_bob.js

Chopped and hacked up script based on:	
	Slideshow - A javascript class for Mootools to stream and animate the presentation of images on your website.
	Copyright (c) 2008 [Aeron Glemann](http://www.electricprism.com/aeron/).

License:
	MIT-style license.

Dependencies:
	Mootools 1.2 Core: Fx.Morph, Fx.Tween, Selectors, Element.Dimensions.
	Mootools 1.2 More: Assets.
	
Changes:
	Shows 2 captions per image.
	Parses data from html structure only; no longer pass data on initialize
	Changed defaults:
		captions: true
		controller: false
	Removed thumbnails, loader, resize, center functions
	
*/

var Slideshow_bob = new Class({
	Implements: [Chain, Events, Options],
	
	options: {
		captions: true,
		center: true,
		classes: [],
		controller: false,
		delay: 2000,
		duration: 750,
		fast: false,
		height: false,
		href: '',
		hu: '',
		linked: false,
		loop: true,
		match: /\?slide=(\d+)$/,
		/*
		onComplete: $empty,
		onStart: $empty,
		*/
		overlap: true,
		paused: false,
		random: false,
		replace: [/\.(.{3})$/, 't.$1'],
		resize: 'width',
		slide: 0,
		transition: function(p){return -(Math.cos(Math.PI * p) - 1) / 2;},
		width: false
	},
	
/**
Constructor: initialize
	Creates an instance of the Slideshow class.
	
Arguments:
	element - (element) The wrapper element.
	dataElement - (element) The element of data structure containing images and captions for the show.
	options - (object) The options below.
	
Syntax:
	var myShow = new Slideshow(element, dataElement, options);
*/

	initialize: function(el, elData, options){	
		this.setOptions(options);
		this.slideshow = $(el);
		if (!this.slideshow) 
			return;
		this.slideshow.set('styles', {'display': 'block', 'position': 'relative', 'z-index': 0});
		var match = window.location.href.match(this.options.match);
		this.slide = (this.options.match && match) ? match[1].toInt() : this.options.slide;
		this.counter = this.delay = this.transition = 0;
		this.direction = 'left';
		this.paused = false;
		if (!this.options.overlap)
			this.options.duration *= 2;


		var anchor = new Element('span'); //this.slideshow.getElement('a') || new Element('a');
//		if (!this.options.href)
//			this.options.href = anchor.get('href') || '';

			
		if (this.options.hu.length && this.options.hu.substr(-1) != '/') 
			this.options.hu += '/';
		
		// styles
		
		var keys = ['slideshow', 'first', 'prev', 'play', 'pause', 'next', 'last', 'images', 'captions1', 'captions2', 'controller', 'thumbnails', 'hidden', 'visible', 'inactive', 'active', 'loader'];
		var values = keys.map(function(key, i){
			return this.options.classes[i] || key;
		}, this);
		this.classes = values.associate(keys);
		this.classes.get = function(){
			var str = '.' + this.slideshow;
			for (var i = 0, l = arguments.length; i < l; i++)
				str += ('-' + this[arguments[i]]);
			return str;
		}.bind(this.classes);
			
		// data	
		var data = {};	
		
		if (elData){
			this.options.hu = '';
		
				
				
			//parse it from our data structure
				
  		$(elData).getElements('div.message').each(function(el,i){
  			var msgs = el.getElements('div.content');
  			var caption1 = msgs[0].innerHTML;
  			var caption2 = msgs[1].innerHTML;
  			data[el.getElement('img').get('src')] = {'caption1': caption1, 'caption2' : caption2}
  		})				
				
		}
	
		var loaded = this.load(data);
		if (!loaded)
			return; 
		
		// events
		
//		this.events = $H({'keydown': [], 'keyup': [], 'mousemove': []});
//		var keyup = function(e){
//			switch(e.key){
//				case 'left': 
//					this.prev(e.shift); break;
//				case 'right': 
//					this.next(e.shift); break;
//				case 'p': 
//					this.pause(); break;
//			}
//		}.bind(this);		
//		this.events.keyup.push(keyup);
//		document.addEvent('keyup', keyup);

		// required elements
			
		var imgs = this.slideshow.getElement(this.classes.get('images'));
		var images = (imgs) ? imgs.empty() : new Element('div', {'class': this.classes.get('images').substr(1)}).inject(this.slideshow);
		var div = images.getSize();
		this.height = this.options.height || div.y;		
		this.width = this.options.width || div.x;
		images.set({'styles': {'display': 'block', 'height': this.height, 'overflow': 'hidden', 'position': 'relative', 'width': this.width}});
		this.slideshow.store('images', images);
		this.a = this.image = this.slideshow.getElement('img') || new Element('img');
		this.a.set('styles', {'display': 'none', 'position': 'absolute', 'zIndex': 1});
		this.b = this.a.clone();
		[this.a, this.b].each(function(img){
			anchor.clone().grab(img).inject(images);
		});
		
		// optional elements
		
		if (this.options.captions)
 			this._captions();
 			
		if (this.options.controller)
			this._controller();
			
		// begin show
		
		this._preload();
	},
	
/**
Public method: go
	Jump directly to a slide in the show.

Arguments:
	n - (integer) The index number of the image to jump to, 0 being the first image in the show.
	
Syntax:
	myShow.go(n);	
*/

	go: function(n, direction){
		if ((this.slide - 1 + this.data.images.length) % this.data.images.length == n || $time() < this.transition)
			return;		
		$clear(this.timer);
		this.delay = 0;		
		this.direction = (direction) ? direction : ((n < this.slide) ? 'right' : 'left');
		this.slide = n;
		if (this.preloader) 
			this.preloader = this.preloader.destroy();
		this._preload(this.options.fast || this.paused);
	},

/**
Public method: first
	Goes to the first image in the show.

Syntax:
	myShow.first();	
*/

	first: function(){
		this.prev(true); 
	},

/**
Public method: prev
	Goes to the previous image in the show.

Syntax:
	myShow.prev();	
*/

	prev: function(first){
		var n = 0;
		if (!first){
			if (this.options.random){
				
				// if it's a random show get the previous slide from the showed array

				if (this.showed.i < 2)
					return;
				this.showed.i -= 2;
				n = this.showed.array[this.showed.i];
			}
			else
				n = (this.slide - 2 + this.data.images.length) % this.data.images.length;									
		}
		this.go(n, 'right');
	},

/**
Public method: pause
	Toggles play / pause state of the show.

Arguments:
	p - (undefined, 1 or 0) Call pause with no arguments to toggle the pause state. Call pause(1) to force pause, or pause(0) to force play.

Syntax:
	myShow.pause(p);	
*/

	pause: function(p){
		if ($chk(p))
			this.paused = (p) ? false : true;
		if (this.paused){
			this.paused = false;
			this.delay = this.transition = 0;		
			this.timer = this._preload.delay(100, this);
			[this.a, this.b].each(function(img){
				['morph', 'tween'].each(function(p){
					if (this.retrieve(p)) this.get(p).resume();
				}, img);
			});
			if (this.options.controller)
				this.slideshow.getElement('.' + this.classes.pause).removeClass(this.classes.play);
		} 
		else {
			this.paused = true;
			this.delay = Number.MAX_VALUE;
			this.transition = 0;
			$clear(this.timer);
			[this.a, this.b].each(function(img){
				['morph', 'tween'].each(function(p){
					if (this.retrieve(p)) this.get(p).pause();
				}, img);
			});
//			if (this.options.controller)
//				this.slideshow.getElement('.' + this.classes.pause).addClass(this.classes.play);
		}
	},
	
/**
Public method: next
	Goes to the next image in the show.

Syntax:
	myShow.next();	
*/

	next: function(last){
		var n = (last) ? this.data.images.length - 1 : this.slide;
		this.go(n, 'left');
	},

/**
Public method: last
	Goes to the last image in the show.

Syntax:
	myShow.last();	
*/

	last: function(){
		this.next(true); 
	},

/**
Public method: load
	Loads a new data set into the show: will stop the current show, rewind and rebuild thumbnails if applicable.

Arguments:
	data - (array or object) The images and optional thumbnails, captions and links for the show.

Syntax:
	myShow.load(data);
*/

	load: function(data){
		this.firstrun = true;
		this.showed = {'array': [], 'i': 0};
		if ($type(data) == 'array'){
			this.options.captions = false;			
			data = new Array(data.length).associate(data); 
		}
		this.data = {'images': [], 'captions1': [],  'captions2': []};
		for (image in data){
			var obj = data[image] || {};
			var caption1 = (obj.caption1) ? obj.caption1.trim() : '';
			var caption2 = (obj.caption2) ? obj.caption2.trim() : '';
			//var href = (obj.href) ? obj.href.trim() : ((this.options.linked) ? this.options.hu + image : this.options.href);
			this.data.images.push(image);
			this.data.captions1.push(caption1);
			this.data.captions2.push(caption2);
			//this.data.hrefs.push(href);
		}
		
		// only run when data is loaded dynamically into an existing slideshow instance
		
		if (this.options.thumbnails && this.slideshow.retrieve('thumbnails'))
			this._thumbnails();
		if (this.slideshow.retrieve('images')){
			[this.a, this.b].each(function(img){
				['morph', 'tween'].each(function(p){
					if (this.retrieve(p)) this.get(p).cancel();
				}, img);
			});
			this.slide = this.transition = 0;
			this.go(0);		
		}
		return this.data.images.length;
	},
	
/**
Public method: destroy
	Destroys a Slideshow instance.

Arguments:
	p - (string) The images and optional thumbnails, captions and links for the show.

Syntax:
	myShow.destroy(p);
*/

	destroy: function(p){
		this.events.each(function(array, e){
			array.each(function(fn){ document.removeEvent(e, fn); });
		});
		this.pause(1);
		Element.Storage[this.slideshow.uid] = {};
		if (p)
			$try(this.slideshow[p]());
	},
	
/**
Private method: preload
	Preloads the next slide in the show, once loaded triggers the show, updates captions, thumbnails, etc.
*/

	_preload: function(fast){
		if (!this.preloader)
		 	this.preloader = new Asset.image(this.options.hu + this.data.images[this.slide], {'onload': function(){
				this.store('loaded', true);
			}});	
		if (this.preloader.retrieve('loaded') && $time() > this.delay && $time() > this.transition){
			if (this.stopped){
									
				this.slideshow.retrieve('captions1').get('morph').cancel().start(this.classes.get('captions1', 'hidden'));
				this.slideshow.retrieve('captions2').get('morph').cancel().start(this.classes.get('captions2', 'hidden'));
					
				this.pause(1);
				this.stopped = false;
				return;				
			}					
			this.image = (this.counter % 2) ? this.b : this.a;
			this.image.set('styles', {'display': 'block', 'height': 'auto', 'visibility': 'hidden', 'width': 'auto', 'zIndex': this.counter});
			['src', 'height', 'width'].each(function(prop){
				this.image.set(prop, this.preloader.get(prop));
			}, this);
			
//			this._resize(this.image);
//			
//			this._center(this.image);
			
//			var anchor = this.image.getParent();
//			
//			if (this.data.hrefs[this.slide])
//				anchor.set('href', this.data.hrefs[this.slide]);			
//			else
//				anchor.erase('href');
				

			this.slideshow.retrieve('captions1').fireEvent('update', fast);
			this.slideshow.retrieve('captions2').fireEvent('update', fast);
				
			this._show(fast);
			this._loaded();
		} 
		else {
			this.timer = (this.paused && this.preloader.retrieve('loaded')) ? null : this._preload.delay(100, this, fast); 
		}
	},

/**
Private method: show
	Does the slideshow effect.
*/

	_show: function(fast){
		if (!this.image.retrieve('morph')){
			var options = (this.options.overlap) ? {'duration': this.options.duration, 'link': 'cancel'} : {'duration': this.options.duration / 2, 'link': 'chain'};
			$$(this.a, this.b).set('morph', $merge(options, {'onStart': this._start.bind(this), 'onComplete': this._complete.bind(this), 'transition': this.options.transition}));
		}
		var hidden = this.classes.get('images', ((this.direction == 'left') ? 'next' : 'prev'));
		var visible = this.classes.get('images', 'visible');
		var img = (this.counter % 2) ? this.a : this.b;
		if (fast){			
			img.get('morph').cancel().set(hidden);
			this.image.get('morph').cancel().set(visible); 			
		} 
		else {
			if (this.options.overlap){	
				img.get('morph').set(visible);
				this.image.get('morph').set(hidden).start(visible);
			} 
			else	{
				var fn = function(hidden, visible){
					this.image.get('morph').set(hidden).start(visible);
				}.pass([hidden, visible], this);
				hidden = this.classes.get('images', ((this.direction == 'left') ? 'prev' : 'next'));
				img.get('morph').set(visible).start(hidden).chain(fn);
			}
		}
	},
	
/**
Private method: loaded
	Run after the current image has been loaded, sets up the next image to be shown.
*/

	_loaded: function(){
		this.counter++;
		this.delay = (this.paused) ? Number.MAX_VALUE : $time() + this.options.duration + this.options.delay;
		this.direction = 'left';
		this.transition = (this.paused || this.options.fast) ? 0 : $time() + this.options.duration;			
		if (this.slide + 1 == this.data.images.length && !this.options.loop && !this.options.random)
			this.stopped = true;			
		if (this.options.random){
			this.showed.i++;
			if (this.showed.i >= this.showed.array.length){
				var n = this.slide;
				if (this.showed.array.getLast() != n) this.showed.array.push(n);
				while (this.slide == n)
					this.slide = $random(0, this.data.images.length - 1);				
			}
			else
				this.slide = this.showed.array[this.showed.i];
		}
		else
			this.slide = (this.slide + 1) % this.data.images.length;
		if (this.preloader) 
			this.preloader = this.preloader.destroy();
		this._preload();
	},

/**
Private method: start
	Callback on start of slide change.
*/

	_start: function(){		
		this.fireEvent('start');
	},

/**
Private method: complete
	Callback on start of slide change.
*/

	_complete: function(){
		if (this.firstrun && this.options.paused){
			this.firstrun = false;
			this.pause(1);
		}
		this.fireEvent('complete');
	},

/**
Private method: captions
	Builds the optional caption element, adds interactivity.
	This method can safely be removed if the captions option is not enabled.
*/

	_captions: function(){
 		if (this.options.captions === true) 
 			this.options.captions = {};
		
		var el1 = this.slideshow.getElement(this.classes.get('captions1'));
		var el2 = this.slideshow.getElement(this.classes.get('captions2'));
		
		var captions1 = (el1) ? el1.empty() : new Element('div', {'class': this.classes.get('captions1').substr(1)}).inject(this.slideshow);
		var captions2 = (el2) ? el2.empty() : new Element('div', {'class': this.classes.get('captions2').substr(1)}).inject(this.slideshow);
		
		captions1.set({
			'events': {
				'update': function(fast){	
					
					var captions1 = this.slideshow.retrieve('captions1');
					var empty1 = (this.data.captions1[this.slide] === '');
					
					if (fast){
						var p1 = (empty1) ? 'hidden' : 'visible';
						captions1.set('html', this.data.captions1[this.slide]).get('morph').cancel().set(this.classes.get('captions1', p1));
					}
					else {
						var fn1 = (empty1) ? $empty : function(n){
							this.slideshow.retrieve('captions1').set('html', this.data.captions1[n]).morph(this.classes.get('captions1', 'visible'))
						}.pass(this.slide, this);		
						captions1.get('morph').cancel().start(this.classes.get('captions1', 'hidden')).chain(fn1);
					}
				}.bind(this)
			},
			'morph': $merge(this.options.captions, {'link': 'chain'})
		});
		this.slideshow.store('captions1', captions1);
		
		captions2.set({
			'events': {
				'update': function(fast){	
					
					var captions2 = this.slideshow.retrieve('captions2');
					
					var empty2 = (this.data.captions2[this.slide] === '');
					
					if (fast){
						var p2 = (empty2) ? 'hidden' : 'visible';
						captions2.set('html', this.data.captions2[this.slide]).get('morph').cancel().set(this.classes.get('captions2', p2));
					}
					else {
						var fn2 = (empty2) ? $empty : function(n){
							this.slideshow.retrieve('captions2').set('html', this.data.captions2[n]).morph(this.classes.get('captions2', 'visible'))
						}.pass(this.slide, this);		
						captions2.get('morph').cancel().start(this.classes.get('captions2', 'hidden')).chain(fn2);
					}
				}.bind(this)
			},
			'morph': $merge(this.options.captions, {'link': 'chain'})
		});
		this.slideshow.store('captions2', captions2);		
		
		
		
	}

	

});