/**

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: {/*

		onComplete: $empty,

		onEnd: $empty,

		onStart: $empty,*/

		captions: false,

		center: true,

		classes: [],

		controller: false,

		delay: 5000,

		duration: 1000,

		fast: false,

		height: false,

		href: '',

		hu: '',

		linked: false,

		loader: {'animate': ['css/loader-#.png', 12]},

		loop: true,

		match: /\?slide=(\d+)$/,

		overlap: false,

		paused: false,

		random: false,

		replace: [/(\.[^\.]+)$/, 't$1'],

		resize: 'width',

		slide: 0,

		thumbnails: false,

		titles: true,

		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.test(/\/$/)) 

			this.options.hu += '/';

		if (this.options.fast === true)

			this.options.fast = 2;

			

		// 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]) ? 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

			

		var 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');

		if (Browser.Engine.trident && Browser.Engine.version > 4)

			this.a.style.msInterpolationMode = 'bicubic';

		this.a.set('styles', {'display': 'none', 'position': 'absolute', 'zIndex': 1});

		this.b = this.a.clone();

		[this.a, this.b].each(function(img){

			anchor.clone().cloneEvents(anchor).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 == 2 || (this.options.fast == 1 && 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.map(function(image, i){ return image + '?' + i })); 

		}

		this.data = {'images': [], 'captions': [], 'hrefs': [], 'thumbnails': []};

		for (var 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);

		}

		if (this.options.random)

			this.slide = $random(0, this.data.images.length - 1);

		

		// 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'));

		this.slideshow.uid = Native.UID++;

		if (p)

			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);

				if (this.end)

					this.fireEvent('end');

				this.stopped = this.end = 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');

			var text = (this.data.captions[this.slide])

				? this.data.captions[this.slide].replace(/<.+?>/gm, '').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, "'") 

				: '';

			this.image.set('alt', text);		

			if (this.options.titles)

				anchor.set('title', text);

			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.setStyle('visibility', 'visible').get('morph').cancel().set(visible); 			

		} 

		else {

			if (this.options.overlap){	

				img.get('morph').set(visible);

				this.image.get('morph').set(hidden);

				this.image.setStyle('visibility', 'visible').get('morph').start(visible);

			} 

			else	{

				var fn = function(hidden, visible){

					this.image.get('morph').set(hidden);

					this.image.setStyle('visibility', 'visible').get('morph').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.options.fast == 2 || (this.options.fast == 1 && this.paused)) ? 0 : $time() + this.options.duration;			

		if (this.slide + 1 == this.data.images.length && !this.options.loop && !this.options.random)

			this.stopped = this.end = 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, pos = props[1], 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'})

			}).inject(li);

			if (this.data.captions[i] && this.options.titles)

				a.set('title', this.data.captions[i].replace(/<.+?>/gm, '').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, "'"));

			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'), 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);

	}

});