// Slideshow: a Javascript class for Mootools to stream and animate the presentation of images on your website <http://electricprism.com/aeron/slideshow>
// Slideshow 2b1, Copyright (c) 2008 Aeron Glemann <http://electricprism.com/aeron>, MIT Style License.

Slideshow = new Class({
	Implements: [Chain, Events, Options],
	
	options: {
		captions: true, // show captions
		center: true, // center images if applicable
		classes: [], // [ 'first', 'prev', 'play', 'pause', 'next', 'last', 'controller', 'thumbnails', 'captions', 'images', 'hidden', 'visible', 'hover', 'active', 'loading' ]
		controller: true, // show controller
		delay: 2000, // the delay between slide changes in milliseconds (1000 = 1 second)
		duration: 750, // the duration of the effect in milliseconds (1000 = 1 second)
		fast: false, // fast mode navigation: the slideshow will not wait until the current transition completes, but update the slide change instantly
		height: false, // optional height value for the slideshow as a whole integer. If a height value is not given the height of the default image will be used
		href: '', // a single link for the slideshow, inherited from the href of the slideshow html
		hu: '/', // path to the image directory(relative or absolute) default is the same directory as the web page
		linked: false, // link each image to fullsize picture
		loop: true, // looping slideshow?
		overlap: true, // whether images overalp in the basic slideshow or if the first image morphs out before the second morphs in
		paused: false, // start paused
		random: false, // random show
		replace: [/\.(.{3})$/, 't.$1'], // find regexp, replace str for thumbnails
		resize: true, // resize images if applicable
		thumbnails: true, // show thumbnails
		transition: Fx.Transitions.Back.easeInOut, // name of Robert Penner transition to use with base and push type slideshows
		width: false // optional width value for the slideshow as a whole integer. If a width value is not given the width of the default image will be used
	},
	

	/**
	 * Constructor
	 *
	 * @param mixed $el An element id or node reference for the slideshow wrapper
	 * @param mixed $data An array ['image1.jpg', 'image2.jpg', ... ] or hash { image1.jpg: { caption: 'string', href: 'string', thumbnail: 'image1t.jpg' }, ... }
	 * @param hash $options A hash containing any of the above options
	 */

	initialize: function(el, data, options){	
		this.setOptions(options);

		if (this.options.hu.substr(-1) != '/'){ this.options.hu += '/'; } // make sure image path has a trailing slash

		// get slideshow element
		this.slideshow = $(el);

		if (!this.slideshow){ return; } // test 1: no slideshow element

		this.slideshow.set('styles', { float: 'left', position: 'relative' });
    /* this.slideshow.set('styles', { display: 'block', position: 'relative' }); */
		
		// prepare our css classes
		var keys = [ 'first', 'prev', 'play', 'pause', 'next', 'last', 'controller', 'thumbnails', 'captions', 'images', 'hidden', 'visible', 'hover', 'active', 'loading' ];

		var values = keys.map(function(key, i){
			return (this.options.classes[i]) ? this.options.classes[i] : key;
		}, this);

		this.classes = values.associate(keys);

		// see if the slideshow has a default url
		var a = this.slideshow.getElement('a[href].' + this.classes.images);

		if (a){ this.options.href = a.get('href'); }

		// compile data for slideshow
		if ($type(data) == 'array'){ 
			this.options.captions = false; // force disable captions
			
			data = new Array(data.length).associate(data); 
		}

		this.data = { images: [], captions: [], hrefs: [], thumbnails: [] };

		for (image in data){
			this.data.images.push(image);

			var obj = data[image] || {};

			this.data.captions.push(obj.caption || '');
			this.data.hrefs.push(obj.href || ((this.options.linked) ? image : this.options.href));
			this.data.thumbnails.push(obj.thumbnail || image.replace(this.options.replace[0], this.options.replace[1]));
		}

		if (!this.data.images.length){ return; } // test 2: no image array

		// get the slideshow image
		this.a = this.image = this.slideshow.getElement('img');

		if (!this.image){ return; } // test 3: no image element
		
		this.a.set('styles', { display: 'block', position: 'absolute', zIndex: 1 }).store('timer', null);

		var img = this.a.getCoordinates();

		this.height = (this.options.height || img.height);
		this.width = (this.options.width || img.width);
		
		if (this.options.height || this.options.width){		
			this._resize(this.a, img.width, img.height);	
		}

		this.counter = 0; // this increments for each transition and is used to determine the z-index
		this.slide = 0; // this marks the current slide in the show
		this.paused = false; // paused state

		// images appear within a bounding element inside of the slideshow
		var el = this.slideshow.getElement('.' + this.classes.images);

		var images = (el) ? el.empty() : new Element('a', { 'class': this.classes.images }).inject(this.slideshow);

		images.set({
			'events': {
				'update': function(){ 
					if (this.data.hrefs[this.slide]){ this.slideshow.retrieve('images').set('href', this.data.hrefs[this.slide]); }
					else { this.slideshow.retrieve('images').erase('href'); }		
				}.bind(this)
			},
			'styles': { display: 'block', height: this.height, overflow: 'hidden', position: 'relative', width: this.width }
		}).adopt(this.a);

		this.slideshow.retrieve('images', images).fireEvent('update');

		// our transitional image
		this.b = this.a.clone().addClass(this.classes.hidden).inject(images);

		// loader
		this.loading = new Element('div', { 'class': this.classes.loading, 'styles': { display: 'none' }}).inject(images);

		if (this.options.captions){
 			this.captions();
		}

		if (this.options.controller){
			this.controller();
		}

		if (this.options.thumbnails){
			this.thumbnails();
		}

		// keyboard control
		document.addEvent('keyup', function(e){
			switch(e.key){
				case 'left': 
					this.prev(e.shift);
					break;
				case 'right': 
					this.next(e.shift);
					break;
				case 'space': 
					this.pause();
					break;
			}
		}.bind(this));

		if (!this.options.overlap){ 
			this.options.delay += this.options.duration;  // account for the extra time of the image fading out
			this.show();		
		}
		
		this.loaded(true);
	},


	/**
	 * Preloads the next slide in the show
	 * Once loaded triggers the show, updates captions, thumbnails, etc
	 *
	 * @param bool $fast Whether fast mode is active or not, this is set in the options and triggered by navigational click
	 */

	preload: function(fast){
		if (this.loader.complete && $time() > this.delay){
			this.loading.setStyle('display', 'none'); 
			
			this.image = (this.counter % 2) ? this.b : this.a;
			this.image.set({ 
				'src': this.loader.src,
				'styles': { height: 'auto', opacity: 0, width: 'auto', zIndex: this.counter }
			});	

			this._resize(this.image, this.loader.width, this.loader.height);

			this.slideshow.retrieve('images').fireEvent('update'); // updates linkage

			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.loading.setStyle('display', 'block'); }

			this.timer = (this.paused) ? null : this.preload.delay(100, this); 
		}
	},


	/**
	 * Does the slideshow effect
	 *
	 * @param bool $fast Whether fast mode is active or not, this is set in the options and triggered by navigation
	 */

	show: function(fast){
		this._center(this.image);

		if (!this.image.retrieve('morph')){
			this.image.set('morph', { duration: this.options.duration, link: 'cancel', transition: this.options.transition });
		}

		if (fast){
			this.image.get('morph').set('.' + this.classes.images + ' img.' + this.classes.visible); 			
		} else {
			this.image.get('morph').set('.' + this.classes.images + ' img.' + this.classes.hidden).start('.' + this.classes.images + ' img.' + this.classes.visible); 
		}
	
		if (!this.options.overlap){ 
			$clear(this.image.retrieve('timer'));

			var fn = (function(image){ 
				image.get('morph').start('.' + this.classes.images + ' img.' + this.classes.hidden); 
			}).create({ arguments: this.image, bind: this });
			
			this.image.store('timer', fn.delay(this.options.delay - this.options.duration));
		}
	},


	/**
	 * Run after the current image has been loaded
	 * Sets up the next image to be shown
	 */

	loaded: function(firstrun){
		this.counter++; // increment counter
		this.delay = (this.paused) ? Number.MAX_VALUE : $time() + this.options.duration + this.options.delay; // time until next transition begins
		this.direction = 'left'; // reset direction since the slideshow goes forward normally
		this.slide = this.options.random ? $random(0, this.data.images.length - 1) : (this.slide + 1) % this.data.images.length; // determine current slide

		if (this.slide == 0 && !this.options.loop){ return; } // don't loop

		if (this.loader){ Garbage.kill(this.loader); }
		this.loader = new Asset.image(this.options.hu + this.data.images[this.slide]);
		
		this.preload();
	},


	/**
	 * Go to the first image in the show
	 */

	first: function(){ 
		this.prev(true); 
	},


	/**
	 * Jump to any image in the show
	 *
	 * @param int $n Number of the slide to jump to
	 */

	go: function(n){
		if ((this.slide - 1 + this.data.images.length) % this.data.images.length == n){ return; }
		
		$clear(this.timer);

		this.delay = 0;		
		this.direction = (n < this.slide) ? 'right' : 'left';
		this.slide = n;

		Garbage.kill(this.loader);
		this.loader = new Asset.image(this.options.hu + this.data.images[this.slide]);

		this.preload(this.options.fast);
	},


	/**
	 * Go to the last image in the show
	 */

	last: function(){ 
		this.next(true); 
	},


	/**
	 * Go to the next image in the show
	 *
	 * @param bool $last Whether to jump to the last or not
	 */

	next: function(last){
		var n = (last === true) ? this.data.images.length - 1 : this.slide;

		this.go(n);
	},


	/**
	 * Toggle whether the slideshow is paused or not
	 */

	pause: function(){
		if (this.paused){
			this.paused = false;
			this.delay = 0;
			this.timer = this.preload.delay(100, this);
		} else {
			this.paused = true;
			this.delay = Number.MAX_VALUE;
			$clear(this.timer);
			$clear(this.image.retrieve('timer'));
		}

		if (this.options.controller){ this.slideshow.getElement('.' + this.classes.pause).toggleClass(this.classes.play); }
	},


	/**
	 * Go to the previous image in the show
	 *
	 * @param bool $last Whether to jump to the first or not
	 */

	prev: function(first){
		var n = (first === true) ? 0 : (this.slide - 2 + this.data.images.length) % this.data.images.length;
		
		this.go(n);
	},


	/**
	 * Builds captions element, adds interactivity
	 * Captions includes the following events: update
	 * Define FX options as an object set to the controller key in the class options
	 */

	captions: function(){
 		if (this.options.captions === true){ this.options.captions = {}; }
 		
		// build the captions element
		var el = this.slideshow.getElement('.' + this.classes.captions);

		var captions = (el) ? el.empty() : new Element('div', { 'class': this.classes.captions }).inject(this.slideshow);

		// add captions interactivity
		captions.set({
			'events': {
				'update': function(fast){	
					if (fast){
						this.slideshow.retrieve('captions').set('html', this.data.captions[this.slide]).get('morph').set('.' + this.classes.captions + '.' + this.classes.visible);
					} else {
						var fn = function(n){
							this.slideshow.retrieve('captions').set('html', this.data.captions[n]).morph('.' + this.classes.captions + '.' + this.classes.visible)
						}.pass(this.slide, this);
					
						this.slideshow.retrieve('captions').get('morph').start('.' + this.classes.captions + '.' + this.classes.hidden).chain(fn);
					}
				}.bind(this)
			},
			'morph': $merge(this.options.captions, { link: 'chain' })
		});
		
		// initialize
		this.slideshow.retrieve('captions', captions).fireEvent('update');
	},


	/**
	 * Builds controller element, adds interactivity
	 * Controller includes the following events: hide, show
	 * Define FX options as an object set to the controller key in the class options
	 */

	controller: function(){
 		if (this.options.controller === true){ this.options.controller = {}; }

		// build the controller element
		var el = $E('.' + this.classes.controller, this.slideshow);

		var controller = (el) ? el.empty() : new Element('div', { 'class': this.classes.controller }).inject(this.slideshow);

		var ul = new Element('ul').inject(controller);

		['first', 'prev', 'pause', 'next', 'last'].each(function(action){
			var li = new Element('li', { 'class': this.classes[action] }).inject(ul);
			
			this.slideshow.retrieve(action, new Element('a')).set({
				'events': { 
					'click': function(action){ this[action](); }.pass(action, this),
					'mouseenter': function(){ this.slideshow.retrieve(action).addClass(this.classes.hover); }.bind(this),
					'mouseleave': function(){ this.slideshow.retrieve(action).removeClass(this.classes.hover); }.bind(this)
				},
				'title': ((action == 'pause') ? this.classes.play.capitalize() + ' / ' : '') + this.classes[action].capitalize() 
			}).inject(li);
		}, this);

		if (this.options.paused){ this.pause(); }

		controller.set({
			'events': {
				'hide': function(hidden){   
					if (!this.retrieve('hidden')){
						this.store('hidden', true).morph(hidden);
					}
				}.pass('.' + this.classes.controller + '.' + this.classes.hidden, controller),
				'show': function(visible){   
					if (this.retrieve('hidden')){
						this.store('hidden', false).morph(visible);
					}
				}.pass('.' + this.classes.controller + '.' + this.classes.visible, controller)
			},
			'morph': $merge(this.options.controller, { link: 'cancel' })
		}).store('hidden', false);

		// add keyboard control
		document.addEvents({
			'keydown': function(e){
				if (e.key.test(/left|right|space/)){
					var controller = this.slideshow.retrieve('controller');

					if (controller.retrieve('hidden')){ controller.get('morph').set('.' + this.classes.controller + '.' + this.classes.visible); } // force show
					
					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;
						case 'space':
							this.slideshow.retrieve('pause').fireEvent('mouseenter');
					}
				}
			}.bind(this),
			'keyup': function(e){
				if (e.key.test(/left|right|space/)){
					var controller = this.slideshow.retrieve('controller');

					if (controller.retrieve('hidden')){ controller.store('hidden', false).fireEvent('hide'); } // force 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;
						case 'space': 
							this.slideshow.retrieve('pause').fireEvent('mouseleave');
							break;
					}
				}
			}.bind(this),
			'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.slideshow.retrieve('controller', controller).fireEvent('hide');
	},


	/**
	 * Builds thumbnails element, adds interactivity
	 * Thumbnails includes the following events: scroll, update
	 * Define FX options as an object set to the controller key in the class options
	 */

	thumbnails: function(){
 		if (this.options.thumbnails === true){ this.options.thumbnails = {}; }

		// build the thumbnails element
		var el = $E('.' + this.classes.thumbnails, this.slideshow);

		var thumbnails = (el) ? el.empty() : new Element('div', { 'class': this.classes.thumbnails }).inject(this.slideshow);

		var ul = new Element('ul').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)
				},
				'href': (this.options.hu + this.data.images[i]),
				'title': this.data.captions[i]
			}).store('active', false).set('morph', $merge(this.options.thumbnails, { link: 'cancel' })).inject(li);

			var img = new Asset.image(this.options.hu + thumbnail).inject(a);
		}, this);

		// get the scroll limit
		var div = thumbnails.getCoordinates();

		var props = (div.height > div.width) ? ['top', 'bottom', 'height', 'y', ['n', 's']] : ['left', 'right', 'width', 'x', ['w', 'e']];

		// add thumbnails interactivity
		thumbnails.set({ 
			'events': {
				'scroll': function(){
					var thumbnails = this.slideshow.retrieve('thumbnails');

					var div = thumbnails.getCoordinates(); // div pos
					var ul = thumbnails.getElement('ul').getPosition(); // ul pos
					var props = thumbnails.retrieve('props'); // top, bottom, height, y, n, s

					var area = div[props[2]] / 3, axis = props[3], cursors = props[4], delta, page =  thumbnails.retrieve('page'), pos = props[0], size = props[2], velocity = -0.2;
			
					if (page[axis] < (div[pos] + area)){ // scroll back
						thumbnails.setStyle('cursor', (cursors[0] + '-resize'));
						
						delta = (page[axis] - div[pos] - area) * velocity;
					} else if (page[axis] > (div[pos] + div[size] - area)){ // scroll fwd
						thumbnails.setStyle('cursor', (cursors[1] + '-resize'));
						
						delta = (page[axis] - div[pos] - div[size] + area) * velocity;
					} else {
						thumbnails.setStyle('cursor', 'auto');
					}			
			
					if (delta){			
						var value = (ul[axis] - div[pos] + delta).limit(thumbnails.retrieve('limit'), 0); // new value
								
						thumbnails.getElement('ul').get('tween', pos).set(value);
					}
				}.bind(this),
				'update': function(fast){
					var thumbnails = this.slideshow.retrieve('thumbnails');
					
					// de + activate thumbnail
					thumbnails.getElements('a').each(function(a, i){	
							if (i == this.slide){
								if (!a.retrieve('active')){ 
									a.store('active', true);
									
									if (fast){ a.get('morph').set('.' + this.classes.thumbnails + ' a.' + this.classes.active); }
									else { a.morph('.' + this.classes.thumbnails + ' a.' + this.classes.active); }
								}
							} else {
								if (a.retrieve('active')){
									a.store('active', false);
									
									if (fast){ a.get('morph').set('.' + this.classes.thumbnails + ' a'); }
									else { a.morph('.' + this.classes.thumbnails + ' a'); }
								}
							}
					}, this);
		
					// scroll all thumbs but only if mouse is not over
					if (!thumbnails.retrieve('hover')){
						var div = thumbnails.getCoordinates(); // div pos
						var ul = thumbnails.getElement('ul').getPosition(); // ul pos
						var li = thumbnails.getElements('li')[this.slide].getCoordinates(); // li pos
						var props = thumbnails.retrieve('props'); // top, bottom, height, y
			
						var axis = props[3], delta, pos = props[0], size = props[2];

						var delta = div[pos] + (div[size] / 2) - (li[size] / 2) - li[pos]
			
						var value = (ul[axis] - div[pos] + delta).limit(thumbnails.retrieve('limit'), 0); // new value
			
						if (!thumbnails.getElement('ul').retrieve('tween')){ 
							thumbnails.getElement('ul').set('tween', { link: 'cancel' });
						}
			
						if (fast){ thumbnails.getElement('ul').get('tween', pos).set(value); }
						else { thumbnails.getElement('ul').tween(pos, value); }
					}
				}.bind(this)
			},
			'morph': { link: 'cancel' }
		}).store('hover', false).store('timer', null).store('props', props);

		// add mouseover scrolling
		document.addEvent('mousemove', function(e){
			var thumbnails = this.slideshow.retrieve('thumbnails');

			var div = thumbnails.getCoordinates();

			if (e.page.x > div.left && e.page.x < div.right && e.page.y > div.top && e.page.y < div.bottom){
				thumbnails.store('page', e.page); // update mouse coords
				
				if (!thumbnails.retrieve('hover')){
					thumbnails.store('hover', true).store('timer', function(){ this.slideshow.retrieve('thumbnails').fireEvent('scroll'); }.periodical(50, this));
				}
			}
			else {
				if (thumbnails.retrieve('hover')){
					thumbnails.store('hover', false);				
					$clear(thumbnails.retrieve('timer'));
				}
			}
		}.bind(this));

		(function(){
			var thumbnails = this.slideshow.retrieve('thumbnails');
			
			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);
		}).delay(100, this);

		// initialize
		this.slideshow.retrieve('thumbnails', thumbnails).fireEvent('update');
	},


	/**
	 * Helper function to 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 });
		}
	},


	/**
	 * Helper function to resize an image
	 */

	_resize: function(img, w, h){
		if (this.options.resize){
			var dh = this.height / h;
			var dw = this.width / w;
	
			var delta = (dw > dh) ? dw : dh;
	
			img.set('styles', { height: Math.ceil(h * delta), width: Math.ceil(w * delta) });
		}	
	}
});


// Slideshow.KenBurns: panning, zooming and fading slideshow in the Ken Burns style
// Slideshow.KenBurns, Copyright (c) 2008 Aeron Glemann <http://electricprism.com/aeron>, MIT Style License.

Slideshow.KenBurns = new Class({
	Extends: Slideshow,
	
	options: {
		pan: 100, // whole integers between 1 and 100 or the keyword 'rand' which will generate a random value for each slide
		zoom: 50 // whole integers between 1 and 100 or the keyword 'rand' which will generate a random value for each slide
	},

	initialize: function(el, data, options){
		options.overlap = true; // force overlapping
		
		['pan', 'zoom'].each(function(p){
			if (this.options[p] != 'rand'){
				this.options[p] = (this.options[p].toInt() || 0).limit(0, 100);
			}
		}, this);
		
		this.parent(el, data, options);
	},


	/**
	 * Does the slideshow effect
	 *
	 * @param bool $fast Whether fast mode is active or not, this is set in the options and triggered by navigation
	 */

	show: function(fast){
		this.image.set('styles', { bottom: 'auto', left: 'auto', right: 'auto', top: 'auto' });

		var props = ['top left', 'top right', 'bottom left', 'bottom right'][this.counter % 4].split(' ');
		props.each(function(prop){ this.image.setStyle(prop, 0); }, this);

		dh = this.height / this.loader.height;
		dw = this.width / this.loader.width;

		delta = (dw > dh) ? dw : dh;

		var values = {};

		var zoom = (this.options.zoom == 'rand') ? Math.random() + 1 : (this.options.zoom / 100.0) + 1;
		var pan = (this.options.pan == 'rand') ? Math.random() : Math.abs((this.options.pan / 100.0) - 1);

		['height', 'width'].each(function(prop, i){
			var e = Math.ceil(this.loader[prop] * delta);
			var s = (e * zoom).toInt();
			
			values[prop] = [s, e];

			if (dw > dh || i){
				var e = (this[prop] - this.image[prop]);
				var s = (e * pan).toInt();
				
				values[props[i]] = [s, e];
			}
		}, this);

		if (!this.image.retrieve('tween')){ 
			this.image.set('tween', { duration: this.options.duration, link: 'cancel' });
		}
		
		if (fast){ this.image.fade('show'); } 
		else { this.image.fade('in'); }

		if (!this.image.retrieve('morph')){ 
			this.image.set('morph', { duration: (this.options.duration + this.options.delay * 2), link: 'cancel', transition: Fx.Transitions.linear });
		}
		
		if (fast){
			for (var prop in values){ values[prop] = values[prop][1]; }
			
			this.image.get('morph').set(values);
		} else {
			this.image.morph(values);
		}
	}
});


// Slideshow.Push: pushing slideshow
// Slideshow.Push, Copyright (c) 2008 Aeron Glemann <http://electricprism.com/aeron>, MIT Style License.

Slideshow.Push = new Class({
	Extends: Slideshow,
	
	initialize: function(el, data, options){
		options.overlap = true; // force overlapping
		
		this.parent(el, data, options);
	},


	/**
	 * Does the slideshow effect
	 *
	 * @param bool $fast Whether fast mode is active or not, this is set in the options and triggered by navigation
	 */

	show: function(fast){
		// first unanchor left and right positioning, then move image out of view, and make visible		
		this.image.set('styles', { left: 'auto', right: 'auto' }).setStyle(this.direction, this.width).setStyle('opacity', 1);

		var images = [this.image, ((this.counter % 2) ? this.a : this.b)];

		var values = [{}, {}];

		values[0][this.direction] = [this.width, 0];
		values[1][this.direction] = [0, -this.width];

		// navigation has changed direction causing an image shift which well need to correct
		if (images[1].getStyle(this.direction) == 'auto'){
			var width = this.width - images[1].width;
		
			images[1].set('styles', { left: 'auto', right: 'auto' }).setStyle(this.direction, width);
			 
			values[1][this.direction] = [width, -this.width];
		}

		if (!this.image.retrieve('fx')){
			this.image.store('fx', new Fx.Elements(images, { duration: this.options.duration, link: 'cancel', transition: this.options.transition }));
		}
		
		this.image.retrieve('fx').start({ '0': values[0], '1': values[1] });
	}
});
