/** Script: Slideshow.js
Slideshow - A javascript class for Mootools to stream and animate the presentation of images on your website.
License:	MIT-style license.
Copyright: Copyright (c) 2008 [Aeron Glemann](http://www.electricprism.com/aeron/).
Dependencies:
	Mootools 1.2 Core: Fx.Morph, Fx.Tween, Selectors, Element.Dimensions.
	Mootools 1.2 More: Assets.
*/
Slideshow = new Class({
	Implements: [Chain, Events, Options],
	options: {
		captions: true,	center: true,	classes: [], controller: true,	delay: 3000, duration: 1000,	fast: false,	height: false,	href: '',	hu: '',	linked: false,	loader: {'animate': ['../css/loader-#.png', 12]},	loop: true,	match: /\?slide=(\d+)$/,	/*	onComplete: $empty,	onStart: $empty,	*/	overlap: true,	paused: false,	random: false,	replace: [/\.(.{3})$/, 't.$1'],	resize: 'width',	slide: 0,	thumbnails: false,	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.
	data - (array or object) The images and optional thumbnails, captions and links for the show.
	options - (object) The options below.
Syntax:
	var myShow = new Slideshow(element, data, options);
*/
	initialize: function(el, data, 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 = 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', 'captions', '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
if (!data){this.options.hu = '';data = {};var thumbnails = this.slideshow.getElements(this.classes.get('thumbnails') + ' img');this.slideshow.getElements(this.classes.get('images') + ' img').each(function(img, i){var src = img.get('src');var caption = img.get('alt') || img.get('title') || '';var href = img.getParent().get('href') || '';var thumbnail = thumbnails[i].get('src') || '';data[src] = {'caption': caption, 'href': href, 'thumbnail': thumbnail};});}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
el = this.slideshow.getElement(this.classes.get('images'));var images = (el) ? el.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();if (this.options.loader)this._loader();if (this.options.thumbnails)this._thumbnails();
// 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': [], 'captions': [], 'hrefs': [], 'thumbnails': []};for (image in data){var obj = data[image] || {};var caption = (obj.caption) ? obj.caption.trim() : '';var href = (obj.href) ? obj.href.trim() : ((this.options.linked) ? this.options.hu + image : this.options.href);var thumbnail = (obj.thumbnail) ? obj.thumbnail.trim() : image.replace(this.options.replace[0], this.options.replace[1]);this.data.images.push(image);this.data.captions.push(caption);this.data.hrefs.push(href);this.data.thumbnails.push(thumbnail);}
// 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);		if (this.options.loader)$clear(this.slideshow.retrieve('loader').retrieve('timer'));if (this.options.thumbnails)$clear(this.slideshow.retrieve('thumbnails').retrieve('timer'));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){if (this.options.captions)this.slideshow.retrieve('captions').get('morph').cancel().start(this.classes.get('captions', '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');if (this.data.captions[this.slide])anchor.set('title', this.data.captions[this.slide].replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, "'"));else anchor.erase('title');if (this.options.loader)this.slideshow.retrieve('loader').fireEvent('hide');		if (this.options.captions) this.slideshow.retrieve('captions').fireEvent('update', fast);if (this.options.thumbnails)	this.slideshow.retrieve('thumbnails').fireEvent('update', fast);	this._show(fast);	this._loaded();}else {if ($time() > this.delay && this.options.loader)	this.slideshow.retrieve('loader').fireEvent('show');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: center - Center an image. */
_center: function(img){if (this.options.center){var size = img.getSize();img.set('styles', {'left': (size.x - this.width) / -2, 'top': (size.y - this.height) / -2});}},
/** Private method: resize - Resizes an image. */
_resize: function(img){	if (this.options.resize){var h = this.preloader.get('height'), w = this.preloader.get('width');var dh = this.height / h, dw = this.width / w, d;if (this.options.resize == 'length')	d = (dh > dw) ? dw : dh;else d = (dh > dw) ? dh : dw;img.set('styles', {height: Math.ceil(h * d), width: Math.ceil(w * d)});}},
/** 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 el = this.slideshow.getElement(this.classes.get('captions'));var captions = (el) ? el.empty() : new Element('div', {'class': this.classes.get('captions').substr(1)}).inject(this.slideshow);captions.set({'events': {'update': function(fast){var captions = this.slideshow.retrieve('captions');var empty = (this.data.captions[this.slide] === '');if (fast){var p = (empty) ? 'hidden' : 'visible';captions.set('html', this.data.captions[this.slide]).get('morph').cancel().set(this.classes.get('captions', p));}else{var fn = (empty) ? $empty : function(n){this.slideshow.retrieve('captions').set('html', this.data.captions[n]).morph(this.classes.get('captions', 'visible'))}.pass(this.slide, this);captions.get('morph').cancel().start(this.classes.get('captions', 'hidden')).chain(fn);}}.bind(this)},'morph': $merge(this.options.captions, {'link': 'chain'})});this.slideshow.store('captions', captions);},
/** Private method: controller - Builds the optional controller element, adds interactivity.This method can safely be removed if the controller option is not enabled. */
_controller: function(){if (this.options.controller === true)this.options.controller = {};var el = this.slideshow.getElement(this.classes.get('controller'));var controller = (el) ? el.empty() : new Element('div', {'class': this.classes.get('controller').substr(1)}).inject(this.slideshow);var ul = new Element('ul').inject(controller);$H({'first': 'Shift + Leftwards Arrow', 'prev': 'Leftwards Arrow', 'pause': 'P', 'next': 'Rightwards Arrow', 'last': 'Shift + Rightwards Arrow'}).each(function(accesskey, action){var li = new Element('li', {'class': (action == 'pause' && this.options.paused) ? this.classes.play + ' ' + this.classes[action] : this.classes[action]}).inject(ul);var a = this.slideshow.retrieve(action, new Element('a', {'title': ((action == 'pause') ? this.classes.play.capitalize() + ' / ' : '') + this.classes[action].capitalize() + ' [' + accesskey + ']'}).inject(li));a.set('events', {'click': function(action){this[action]();}.pass(action, this),'mouseenter': function(active){this.addClass(active);}.pass(this.classes.active, a),'mouseleave': function(active){this.removeClass(active);}.pass(this.classes.active, a)});}, this);controller.set({'events': {			'hide': function(hidden){if (!this.retrieve('hidden'))this.store('hidden', true).morph(hidden);}.pass(this.classes.get('controller', 'hidden'), controller),'show': function(visible){if (this.retrieve('hidden'))this.store('hidden', false).morph(visible);}.pass(this.classes.get('controller', 'visible'), controller)},'morph': $merge(this.options.controller, {'link': 'cancel'})}).store('hidden', false);var keydown = function(e){if (['left', 'right', 'p'].contains(e.key)){var controller = this.slideshow.retrieve('controller');			if (controller.retrieve('hidden'))controller.get('morph').set(this.classes.get('controller', 'visible'));switch(e.key){case 'left':						this.slideshow.retrieve((e.shift) ? 'first' : 'prev').fireEvent('mouseenter'); break;case 'right':this.slideshow.retrieve((e.shift) ? 'last' : 'next').fireEvent('mouseenter'); break;default:this.slideshow.retrieve('pause').fireEvent('mouseenter'); break;}}}.bind(this);this.events.keydown.push(keydown);var keyup = function(e){if (['left', 'right', 'p'].contains(e.key)){var controller = this.slideshow.retrieve('controller');if (controller.retrieve('hidden'))controller.store('hidden', false).fireEvent('hide');switch(e.key){case 'left':this.slideshow.retrieve((e.shift) ? 'first' : 'prev').fireEvent('mouseleave'); break;case 'right':this.slideshow.retrieve((e.shift) ? 'last' : 'next').fireEvent('mouseleave'); break;default:this.slideshow.retrieve('pause').fireEvent('mouseleave'); break;}}}.bind(this);this.events.keyup.push(keyup);var mousemove = function(e){var images = this.slideshow.retrieve('images').getCoordinates();if (e.page.x > images.left && e.page.x < images.right && e.page.y > images.top && e.page.y < images.bottom)this.slideshow.retrieve('controller').fireEvent('show');else this.slideshow.retrieve('controller').fireEvent('hide');}.bind(this);this.events.mousemove.push(mousemove);document.addEvents({'keydown': keydown, 'keyup': keyup, 'mousemove': mousemove});this.slideshow.retrieve('controller', controller).fireEvent('hide');},
/** Private method: loader - Builds the optional loader element, adds interactivity.This method can safely be removed if the loader option is not enabled. */
_loader: function(){if (this.options.loader === true)this.options.loader = {};var loader = new Element('div', {'class': this.classes.get('loader').substr(1),'morph': $merge(this.options.loader, {'link': 'cancel'})}).store('hidden', false).store('i', 1).inject(this.slideshow.retrieve('images'));if (this.options.loader.animate){for (var i = 0; i < this.options.loader.animate[1]; i++)img = new Asset.image(this.options.loader.animate[0].replace(/#/, i));if (Browser.Engine.trident4 && this.options.loader.animate[0].contains('png'))			loader.setStyle('backgroundImage', 'none');}loader.set('events', {'animate': function(){var loader = this.slideshow.retrieve('loader');	var i = (loader.retrieve('i').toInt() + 1) % this.options.loader.animate[1];loader.store('i', i);var img = this.options.loader.animate[0].replace(/#/, i);if (Browser.Engine.trident4 && this.options.loader.animate[0].contains('png'))loader.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + img + '", sizingMethod="scale")';else loader.setStyle('backgroundImage', 'url(' + img + ')');}.bind(this),'hide': function(){var loader = this.slideshow.retrieve('loader');if (!loader.retrieve('hidden')){		loader.store('hidden', true).morph(this.classes.get('loader', 'hidden'));if (this.options.loader.animate)$clear(loader.retrieve('timer'));}}.bind(this),'show': function(){var loader = this.slideshow.retrieve('loader');if (loader.retrieve('hidden')){loader.store('hidden', false).morph(this.classes.get('loader', 'visible'));if (this.options.loader.animate)loader.store('timer', function(){this.fireEvent('animate');}.periodical(50, loader));}}.bind(this)});this.slideshow.retrieve('loader', loader).fireEvent('hide');},
/** Private method: thumbnails - Builds the optional thumbnails element, adds interactivity.This method can safely be removed if the thumbnails option is not enabled. */
	_thumbnails: function(){ if (this.options.thumbnails === true) this.options.thumbnails = {}; var el = this.slideshow.getElement(this.classes.get('thumbnails')); var thumbnails = (el) ? el.empty() : new Element('div', {'class': this.classes.get('thumbnails').substr(1)}).inject(this.slideshow);	thumbnails.setStyle('overflow', 'hidden'); var ul = new Element('ul', {'tween': {'link': 'cancel'}}).inject(thumbnails);	this.data.thumbnails.each(function(thumbnail, i){	var li = new Element('li').inject(ul); var a = new Element('a', {	'events': { 'click': function(i){ this.go(i); return false; }.pass(i, this), 'loaded': function(){ this.data.thumbnails.pop(); if (!this.data.thumbnails.length){	var div = thumbnails.getCoordinates(); var props = thumbnails.retrieve('props'); var limit = 0; var pos = props[1]; var size = props[2]; thumbnails.getElements('li').each(function(li){ var li = li.getCoordinates(); if (li[pos] > limit) limit = li[pos]; }, this); thumbnails.store('limit', div[size] + div[props[0]] - limit); } }.bind(this) }, 'href': this.options.hu + this.data.images[i], 'morph': $merge(this.options.thumbnails, {'link': 'cancel'}), 'title': this.data.captions[i] }).inject(li); var img = new Asset.image(this.options.hu + thumbnail, { 'onload': function(){this.fireEvent('loaded');}.bind(a) }).inject(a); }, this); thumbnails.set('events', {'scroll': function(n, fast){ var div = this.getCoordinates();	var ul = this.getElement('ul').getPosition();	var props = this.retrieve('props'); var axis = props[3], delta, pos = props[0], size = props[2], value;	var tween = this.getElement('ul').get('tween', {'property': pos});	if ($chk(n)){	var li = this.getElements('li')[n].getCoordinates();	delta = div[pos] + (div[size] / 2) - (li[size] / 2) - li[pos];	value = (ul[axis] - div[pos] + delta).limit(this.retrieve('limit'), 0); if (fast)	tween.set(value);	else	tween.start(value);	}	else {	var area = div[props[2]] / 3, page = this.retrieve('page'); var velocity = -0.2; 	if (page[axis] < (div[pos] + area))		delta = (page[axis] - div[pos] - area) * velocity;	else if (page[axis] > (div[pos] + div[size] - area)) 	delta = (page[axis] - div[pos] - div[size] + area) * velocity; if (delta){	value = (ul[axis] - div[pos] + delta).limit(this.retrieve('limit'), 0);	tween.set(value);	}	} }.bind(thumbnails), 'update': function(fast){ var thumbnails = this.slideshow.retrieve('thumbnails'); thumbnails.getElements('a').each(function(a, i){	if (i == this.slide){	if (!a.retrieve('active', false)){	a.store('active', true);	var active = this.classes.get('thumbnails', 'active'); if (fast) a.get('morph').set(active);	else a.morph(active);	}	}	else {	if (a.retrieve('active', true)){	a.store('active', false);	var inactive = this.classes.get('thumbnails', 'inactive'); if (fast) a.get('morph').set(inactive);	else a.morph(inactive);	}	}	}, this); if (!thumbnails.retrieve('mouseover'))	thumbnails.fireEvent('scroll', [this.slide, fast]);	}.bind(this) })	
var div = thumbnails.getCoordinates(); thumbnails.store('props', (div.height > div.width) ? ['top', 'bottom', 'height', 'y'] : ['left', 'right', 'width', 'x']);	var mousemove = function(e){	var div = this.getCoordinates();	if (e.page.x > div.left && e.page.x < div.right && e.page.y > div.top && e.page.y < div.bottom){	this.store('page', e.page);	if (!this.retrieve('mouseover')){	this.store('mouseover', true);	this.store('timer', function(){this.fireEvent('scroll');}.periodical(50, this));	}	}	else {	if (this.retrieve('mouseover')){	this.store('mouseover', false);	$clear(this.retrieve('timer'));	}	} }.bind(thumbnails);	this.events.mousemove.push(mousemove);	document.addEvent('mousemove', mousemove);	this.slideshow.store('thumbnails', thumbnails);	} });