/**
 * Liteshow JavaScript library, version 1.0beta3
 * (c) 2006-2007 Benjamin Mack <www.xnos.org>
 *
 * Liteshow is freely distributable under the terms of an MIT-style license.
 * For details, see the Liteshow web site at http://www.xnos.org/
 */

// ==== Extending the window object ==== //
Object.extend(window, {
	getInnerWidth: function() {
		// Non-IE
		if (typeof(window.innerWidth) == 'number') return window.innerWidth;
		// IE 6+ in 'standards compliant mode
		if (document.documentElement && document.documentElement.clientWidth) return document.documentElement.clientWidth;
		// IE 4 compatible
		if (document.body && document.body.clientWidth) return document.body.clientWidth;
	},

	getInnerHeight: function() {
		if (typeof(window.innerHeight) == 'number') return window.innerHeight;
		if (document.documentElement && document.documentElement.clientHeight) return document.documentElement.clientHeight;
		if (document.body && document.body.clientHeight) return document.body.clientHeight;
	}
});

Event.KEY_SPACE = 32;


Object.extend(Element, {
	// get maximum dimensions with considering maxHeight / maxWidth option
	getMaxDimensions: function(el) {
		var dims = Element.getDimensions(el);
		if (LiteConfig.maxSize) {
			while (dims.width > window.getInnerWidth() || dims.height > window.getInnerHeight()*LiteConfig.maxSize) {
				dims.width = dims.width*0.95;
				dims.height = dims.height*0.95;
			}
		}
		return { width: dims.width, height: dims.height };
	}
});


// ==== Global config object ==== //
var LiteConfig = {
	dom: {},	// information about the state of the DOM will be saved in there
	view: 'HorizontalSlide',	// the running slideshow, will be overwritten with every init() of every slideshow

	maxSize: 1,	// screen height in percent that the image can fil out max
	cacheNum: 4,	// number of (previous / next) images that should be loaded into cache

	useBookmarks: false,	// whether to use the bookmarking system or not
	
	xmlPath: 'fileadmin/_lightshow/galleries/',	// not used when displaying a single image

	throbber: 'fileadmin/_lightshow/g/loading.gif',
	
  isLoading: false,	// internal state variable

	// div markers
	idContainer: 'liteShow',
	idCacheContainer: 'liteCache',
	idOverlay: 'liteOverlay',
	idThrobber: 'liteLoading',
	idBrowser: 'liteBrowser'
}


// ==== Main Liteshow object ==== //
var Liteshow = {
	browser: {},
	pos: 0,
	
	// == XML / resource functions == //
	xml: null,
	loadXML: function(file) {
		new Ajax.Request(LiteConfig.xmlPath + file, { method: 'get',
			onComplete: function(xhr) {
				this.xml = xhr.responseXML;
				LiteEvent.run('postXML');
			}.bind(this)
		});
	},

	// returns the information from the XML file of a image in an array with two elements
	getImageInfo: function(pos) {
		if (this.xml == null) {	// single image mode
			var src = this.options.resourceId;
			var title = this.options.singleImageTitle;
		} else {
			if (!this.isValidImagePos(pos)) return [];
			var node  = this.xml.getElementsByTagName('image')[pos];
			var src   = node.getAttribute('src');
			var title = node.getElementsByTagName('title')[0].firstChild.nodeValue;
		}
		return [src, title.replace(/"/g, '\'')];
	},

	isLastImage:     function(pos) { return (pos == this.numImages()-1)          ? true : false; },
	isValidImagePos: function(pos) { return (pos < 0 || pos >= this.numImages()) ? false : true; },
	numImages:       function()    { return (this.xml == null) ? 1 : (this.xml.getElementsByTagName('image').length || 0); },


	// instantiates the object with the xml, creates the DOM elements, loads the XML and then starts the slide show
	init: function(resourceId) {
		var defaultOptions = {
			resourceId: resourceId,
			startPos: 0,
			transitionTime: 2,
			timer: false,
			timerDelay: 5,
			automaticRewind: false
		};
		this.options = Object.extend(defaultOptions, (arguments[1] || {}) );
		this.pos = this.options.startPos;

		if (this.localInit) this.localInit();

		// initialize timer
		LiteTimer.setOptions(this.options.timer, this.options.timerDelay, this.options.transitionTime);

		// -- DOM manipulation --

		// set the HTML and BODY tag overflow to "hidden"
		LiteConfig.dom.htmlObj = document.body.parentNode;
		LiteConfig.dom.bodyObj = document.body;
		LiteConfig.dom.htmlOverflow = LiteConfig.dom.htmlObj.style.overflow;
		LiteConfig.dom.bodyOverflow = LiteConfig.dom.bodyObj.style.overflow;
		LiteConfig.dom.htmlObj.style.overflow = 'hidden';
		LiteConfig.dom.bodyObj.style.overflow = 'hidden';
		
		// creating the container, the overlay and the cacheContainer, then show the overlay
		if (!$(LiteConfig.idOverlay))   new Insertion.Top(LiteConfig.dom.bodyObj, '<div id="' + LiteConfig.idOverlay + '" style="display: none;"/>');
		if (!$(LiteConfig.idContainer)) new Insertion.Top(LiteConfig.dom.bodyObj, '<div id="' + LiteConfig.idContainer + '"/>');
		else {
			// reset to default styles when starting a new show
			$(LiteConfig.idContainer).innerHTML = '';
			$(LiteConfig.idContainer).setStyle({ display: 'none', top: '0px', left: '0px', width: '100%', height: '100%' });
		}
		if (!$(LiteConfig.idCacheContainer)) new Insertion.Bottom(LiteConfig.idContainer, '<div id="' + LiteConfig.idCacheContainer + '"/>');
		new Effect.Appear(LiteConfig.idOverlay, { duration: 0.2, from: 0.0, to: 0.7 });
		LiteThrobber.show();

		// load resource file or XML file via AJAX
		var ext = resourceId.substring(resourceId.length-4).toLowerCase();
		if (ext != '.jpg' && ext != '.png' && ext != '.gif') {
			if (ext != '.xml') { resourceId += '.xml'; }
			LiteEvent.add('postXML', 'setupShow');
			this.loadXML(resourceId);
		} else {
			// run the setup without delay
			this.setupShow();
		}
	},
	
	setupShow: function() {
		// start in browser mode
		if (this.pos == 'browser') {
			this.browserInit();
		}
		// start with caching and rendering single images (default)
		else {
			var waitUntilCached = this.pos + LiteConfig.cacheNum - 1;
			while (waitUntilCached >= this.numImages()) waitUntilCached--;
			if (this.isImageCached(waitUntilCached)) {
				this.firstImage();
			} else {
				LiteEvent.add(waitUntilCached, 'firstImage');
				this.cacheImages(this.pos);
			}
		}

		// adding control mechanisms
		Event.observe(document, 'keypress', this.keyboardControl.bindAsEventListener(this), true);
	},

	// clean up show elements
	endShow: function() {
		LiteThrobber.hide();
		LiteTimer.stop();
		new Effect.SlideUp(LiteConfig.idContainer, { delay: 0.2, duration: 0.7 } );
		new Effect.Fade(LiteConfig.idOverlay, { duration: 0.8, delay: 1, afterFinish: function() {
			LiteConfig.dom.htmlObj.style.overflow = LiteConfig.dom.htmlOverflow; 
			LiteConfig.dom.bodyObj.style.overflow = LiteConfig.dom.bodyOverflow;
		} });
		Event.stopObserving(document, 'keypress', this.keyboardControl.bindAsEventListener(this), true);
		LiteBookmark.clear();
	},


	rewindShow: function() {
		var timer = LiteTimer.state;
		if (timer) LiteTimer.stop();
		if (this._rewindShowHook)
			this._rewindShowHook();
		else {
			this.pos = 0;
			this.viewImage(this.pos);
		}
		if (timer) LiteTimer.start();
	},


	// function that checks if an arrow key is pressed, then slides the images
	keyboardControl: function(e) {
		var key = (e.which) ? e.which : e.keyCode;
		if (e.altKey || e.shiftKey || e.ctrlKey)  { return true;  }  // passthrough
		Event.stop(e);

		LiteTimer.stop();
		if (key == Event.KEY_ESC) { this.endShow(); return false; }  // end show when pressing escape

		if (LiteConfig.isLoading) return true; // do nothing when loading
		if (key == Event.KEY_RIGHT || key == Event.KEY_DOWN || key == Event.KEY_SPACE) { this.nextImage(); return false; }
		if (key == Event.KEY_LEFT  || key == Event.KEY_UP) { this.previousImage(); return false; }
		return true;
	},


	// ============== Image Caching Functions =========== //

	isImageCached: function(pos) { return (this.imageCached(pos) && this.imageCached(pos).complete) ? true : false; },

	// caches the next X and the previous X image(s)
	cacheImages: function(pos) {
		if (!this.isValidImagePos(pos)) return false;
		var startPreloadPos = (pos-LiteConfig.cacheNum > 0) ? pos-LiteConfig.cacheNum : 0;
		var stopPreloadPos  = pos+LiteConfig.cacheNum;
		for (var i = startPreloadPos; i < stopPreloadPos; i++) {
			this._cacheImage(i);
			if (this.isLastImage(i)) break;
		}
	},

	// internal function to create a cache image and set an event when loaded
	_cacheImage: function(pos) {
		var id = 'ls_cache_img_' + pos;
		// no need to load an already cached image
		if ($(id)) return;
		var info = this.getImageInfo(pos);
		new Insertion.Bottom(LiteConfig.idCacheContainer, '<img src="' + info[0] + '" id="' + id + '"/>');
		$(id).onload = function() { LiteEvent.run(pos); };
	},


	// == Image Loading Functions == // 

	// takes the preloaded image and uses it as a real image
	_createImage: function(pos) {
		if (!this.isValidImagePos(pos)) return false;
		if (this.image(pos)) 			return true;
		if (!this.isImageCached(pos))   this._cacheImage(pos);
		var info = this.getImageInfo(pos);
		var browserButton = '';
		if (this.numImages() > 1) {
			browserButton = '<a class="browse" href="javascript:void(0);" onclick="Liteshow[LiteConfig.view].browserInit();"></a>';
		}
		new Insertion.Bottom(LiteConfig.idContainer, '<div id="ls_'+pos+'" style="display: none;" class="liteItem"><img src="'+info[0]+'" alt="'+info[1]+'" id="ls_img_'+pos+'" /><div class="liteControls" style="display: none;"><h4>'+info[1]+'</h4><a class="close" href="javascript:void(0);" onclick="Liteshow[LiteConfig.view].endShow();"></a>'+browserButton+'</div></div>');
	},


	// the next four functions are general functions that take care 
	// of the loading images. Usually this is called from
	// the "firstImage"... function of the implementing views
	_firstImage: function() {
		Element.show(LiteConfig.idContainer);
		this.__addDefaultImageActions(this.pos);
		LiteBookmark.set(this, this.pos);
		if (LiteTimer.state) LiteTimer.start();
		LiteThrobber.hide();
	},

	_previousImage: function() {
		this.pos = this.pos-1;

		this.__addDefaultImageActions(this.pos);
		LiteBookmark.set(this, this.pos);
	},

	_nextImage: function() {
		this.pos = this.pos+1;
		if (this.isLastImage(this.pos)) {
			if (this.options.automaticRewind)
				LiteTimer.rewindShow();
			else
				LiteTimer.stop();
		}
		this.__addDefaultImageActions(this.pos);
		LiteBookmark.set(this, this.pos);
	},
	
	_viewImage: function(pos) {
		if (!pos) var pos = this.pos;
		this.__addDefaultImageActions(pos);

		LiteBookmark.set(this, this.pos);
	},

	__addDefaultImageActions: function(pos) {
		this.cacheImages(pos);
		if (!this.image(pos))   this._createImage(pos);
		if (!this.image(pos-1)) this._createImage(pos-1);
		if (!this.image(pos+1)) this._createImage(pos+1);

		this.image(pos).onclick                          = this.nextImage.bind(this);
		if (this.image(pos+1)) this.image(pos+1).onclick = this.nextImage.bind(this);
		if (this.image(pos-1)) this.image(pos-1).onclick = this.previousImage.bind(this);
	},




	// == Peripheral Functions == //
	lockNavigation: function(pos) {
		LiteConfig.isLoading = true;
		this.image(pos).onclick = null;
		if (this.image(pos+1)) this.image(pos+1).onclick = null;
		if (this.image(pos-1)) this.image(pos-1).onclick = null;
	},

	unlockNavigation: function(pos) {
		LiteConfig.isLoading = false;
		this.__addDefaultImageActions(pos);
	},


	// -- default title functions, can be overridden by any view --
	showInfo: function(pos) {
		if (this.pos != pos) return;
		Element.hide(this.controls(pos));
		this.controls(pos).style.width = (this.image(pos).getWidth()) + 'px';
		new Effect.BlindDown(this.controls(pos), { duration: 0.2 } );
	},

	hideInfo: function(pos) {
		if (Element.visible(this.controls(pos)))
			new Effect.SlideUp(this.controls(pos), { duration: 0.2 } );
	},


	// == Liteshow Browser == //
	browserInit: function() {
		this.browser.images = 0;
		this.browser.tnSize = 250;
		this.browser.tnPadding = 20;
		this.browser.wasTimerOn = LiteTimer.state;
		LiteTimer.stop();
		if (this.item(this.pos)) this.hideInfo(this.pos);
		

		if (!$(LiteConfig.idBrowser)) new Insertion.After(LiteConfig.idContainer, '<div id="' + LiteConfig.idBrowser + '" style="display: none;"></div>');
		else $(LiteConfig.idBrowser).innerHTML = '';

		var styles = { top: '0px', left: '0px',
			width:  window.getInnerWidth() + 'px',
			height: window.getInnerHeight() + 'px',
			display: 'block', opacity: '1' };
		$(LiteConfig.idBrowser).setStyle(styles);
		LiteThrobber.show();
		
		$(LiteConfig.idContainer).style.zIndex = 5;

		// calculate how many images we can bring on the screen
		var numHorz = -1;
		var maxHorzSize = window.getInnerWidth();
		var tmp = maxHorzSize;
		while (tmp > numHorz) {
			numHorz += 1;
			tmp -= this.browser.tnSize+(this.browser.tnPadding*2);
		}
		var horzSize = this.browser.tnSize*numHorz + this.browser.tnPadding*numHorz*2;

		// creating the table datas
		var tbldata = "";
		for (var td = 0; td < this.numImages(); td++) {
			if (td % numHorz == 0) tbldata += '<tr>';
			tbldata += '<td id="browse_td_' + td + '"></td>';
			if (td % numHorz == numHorz-1) tbldata += '</tr>';
		}
		if (td % numHorz != numHorz-1) tbldata += '</tr>';
		$(LiteConfig.idBrowser).innerHTML = '<table cellspacing="0" cellpadding="' + this.browser.tnPadding + '">' + tbldata + '</table>';

		// putting the images in the tds
		for (var pos = 0; pos < this.numImages(); pos++) {
			if (!this.isImageCached(pos)) this._cacheImage(pos);
			this._preloadBrowserImage(pos);
		}
	},


	_preloadBrowserImage: function(pos) {
		var info = this.getImageInfo(pos);
		// TODO: replace this with DOM internal functions so we can get rid of the Builder dependency
		var browseImg  = Builder.node('img', { id: 'ls_browse_img_' + pos, style: 'display: none;', src: info[0], alt: info[1] });
		$('browse_td_' + pos).appendChild(browseImg);
		var reqId = 'browse_' + pos;
		if (!browseImg.complete) {
			LiteEvent.add(reqId, '_renderBrowserImage');
			browseImg.onload = function() { LiteEvent.run(reqId); };
		} else {
			this._renderBrowserImage(reqId);
		}
	},

	_renderBrowserImage: function(pos) {
		pos = pos.split('_')[1];
		var dims = Element.getDimensions(this.imageCached(pos));
		if (dims.width > dims.height) this.imageBrowser(pos).style.width  = this.browser.tnSize + 'px';
		else                          this.imageBrowser(pos).style.height = this.browser.tnSize + 'px';

		Event.observe(this.imageBrowser(pos), 'click', this.selectBrowserImage.bind(this, pos), false);
		this.browser.images += 1;
		
		var finishUp = function(pos) {
			var imgWidth = this.imageBrowser(pos).style.width;
			if (!imgWidth) {
				var dims = Element.getDimensions(this.imageCached(pos));
				imgWidth = Math.round(dims.width/dims.height * parseInt(this.imageBrowser(pos).style.height)) + 'px';
			}

			new Insertion.After(this.imageBrowser(pos), '<span style="width: '+imgWidth+';">' + this.imageBrowser(pos).alt + '</span>');
			this._checkBrowserImages();
		};
		new Effect.Appear(this.imageBrowser(pos), { duration: 0.4, queue: { position: 'end', scope: 'browserimages'}, afterFinish: finishUp.bind(this, pos) } );
	},
	
	_checkBrowserImages: function() {
		if (this.browser.images < this.numImages()) return;
		LiteThrobber.hide();
	},
	
	selectBrowserImage: function(pos) {
		if (this.item(this.pos)) Element.hide(this.item(this.pos));
		pos = parseInt(pos);
		LiteThrobber.hide();

		// hook after selecting an image from the browser
		if (this._selectBrowserImageHook)
			this._selectBrowserImageHook(pos);

		if (!this.image(pos)) this._createImage(pos);
		Position.clone(this.imageBrowser(pos), this.item(pos), { setWidth: false, setHeight: false } );

		this.pos = pos;
		this.lockNavigation(pos);
		$(LiteConfig.idContainer).style.zIndex = 20;
		this.item(pos).style.zIndex = 50;
		this.image(pos).style.width  = this.imageBrowser(pos).style.width;
		this.image(pos).style.height = this.imageBrowser(pos).style.height;

		// IE width bug
		if(/MSIE/.test(navigator.userAgent)) this.item(pos).style.width = this.imageBrowser(pos).style.width;

		Element.show(this.item(pos));
		Element.hide(LiteConfig.idBrowser);
		if (this.browser.wasTimerOn) LiteTimer.start();
		this.viewImage(pos);
	},


	// == helper functions for accessing the images, titles and items   == //
	item:			function(pos) { return $('ls_' + pos); },
	controls:       function(pos) { return this.item(pos).lastChild; },
	title:          function(pos) { return this.item(pos).lastChild.firstChild.firstChild; },
	image:			function(pos) { return $('ls_img_' + pos); },
	imageCached: 	function(pos) { return $('ls_cache_img_' + pos); },
	imageBrowser: 	function(pos) { return $('ls_browse_img_' + pos); },


	// == stub functions - these need to be implemented by the extension classes == //
	firstImage:    function() { this._firstImage();    },
	previousImage: function() { this._previousImage(); },
	nextImage:     function() { this._nextImage();     },
	viewImage:     function(pos) {
		if (!pos) var pos = this.pos;
		this._viewImage(pos);
		var dims    = Element.getDimensions(this.imageCached(pos));
		var dimsMax = Element.getMaxDimensions(this.imageCached(pos));
		var dimsNow = Element.getDimensions(this.image(pos));

		var scaleFrom = (100 * dimsNow.width / dims.width);
		var scaleTo = 100;
		if (dims.width  > dimsMax.width)  scaleTo = dimsMax.width  / dims.width * 100;
		if (dims.height > dimsMax.height) scaleTo = dimsMax.height / dims.height * 100;

		var x = Math.floor((window.getInnerWidth() - dimsMax.width)/2);
		var y = Math.floor((window.getInnerHeight() - dimsMax.height)/2 - 20);

		new Effect.Scale(this.image(pos), scaleTo, {
			delay: 0.3,
			duration: 0.5,
			scaleFrom: scaleFrom,
			mode: 'absolute',
			scaleFromCenter: true,
			scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
			afterFinish: function () {
				this.showInfo(pos);
				this.unlockNavigation(pos);
				this._createImage(pos+2); 
			}.bind(this)
		});
		new Effect.Move(this.item(pos), { delay: 0.3, duration: 0.5, x: x, y: y, mode: 'absolute' });
	}
};




// ==== Timer class ==== //
var LiteTimer = {
	state: null,
	delay: null,
	duration: null,

	id: null,
	clickOff: null,

	setOptions: function(state, delay, duration) {
		this.state = state;
		this.delay = delay;
		this.duration = duration;
	},
	
	start: function(delay) {
		if (!delay) var delay = this.delay;
		else this.delay = parseInt(delay);
		this.state = true;
		this.reset();
	},

	reset: function() {
		if (!this.state) return;
		window.clearTimeout(this.id);
		this.id = window.setTimeout('Liteshow.' + LiteConfig.view + '.nextImage(); LiteTimer.reset();', (this.delay+this.duration) * 1000);
		this.clickOff = this.stop.bind(this);
		window.onmousedown = this.clickOff;	
	},

	stop: function() {
		this.state = false;
		window.clearTimeout(this.id);
		if (this.clickOff) {
			window.onmousedown = '';
			this.clickOff = null;
		}
	},
	
	rewindShow: function() {
		this.id = window.setTimeout('Liteshow.' + LiteConfig.view + '.rewindShow(); LiteTimer.reset();', this.delay * 2 * 1000);
	}
};



// ==== Bookmarker class ==== //
// 0: View, 1: GalleryID, 2: Position

var LiteBookmark = {
	recover: function() {
		var hash = window.location.hash;
		if (hash) {
			hash = hash.substring(1, hash.length);
			var details = hash.split('_');
			if (details.length != 3) return;
			Liteshow[details[0]].init(details[1], { startPos: parseInt(details[2]) });
		}
	},
	set:   function(show, pos) {
		if (LiteConfig.useBookmarks) window.location.hash = '#' + LiteConfig.view + '_' + show.options.resourceId + '_' + pos;
	},
	clear: function() {
		if (LiteConfig.useBookmarks) window.location.hash = '#';
	}
};

Event.observe(window, 'load', LiteBookmark.recover, false);



// == Event framework class == //
var LiteEvent = {
	events: {},
	add: function(key, func) { this.events[key] = func; },
	run: function(key) {
		if (this.events[key]) {
			Liteshow[LiteConfig.view][this.events[key]](key)
			this.events[key] = null
		}
	}
};
	
var LiteThrobber = {
	show: function() {
		LiteConfig.isLoading = true;
		if (!$(LiteConfig.idThrobber)) new Insertion.Top(document.body, '<img id="'+LiteConfig.idThrobber+'" src="'+LiteConfig.throbber+'" style="display: none;"/>');
		if (!Element.visible(LiteConfig.idThrobber)) {
			$(LiteConfig.idThrobber).setStyle({
				top: Math.round((window.getInnerHeight()/2)-40) + 'px',
				left: Math.round((window.getInnerWidth()/2)-40) + 'px'
			});
			new Effect.Appear(LiteConfig.idThrobber, { duration: 0.2, queue: 'throbber' } );
		}
	},
	hide: function() {
		new Effect.Fade(LiteConfig.idThrobber, { duration: 0.2, queue: 'throbber' } );
		LiteConfig.isLoading = true;
	}
};

// ==== VIEW IMPLEMENTATIONS ==== //

// == Object for view that slides images from the right to the left == //
Liteshow.HorizontalSlide = Class.create();
Object.extend(Object.extend(Liteshow.HorizontalSlide, Liteshow), {

	// implementing default methods
	localInit: function() {
		LiteConfig.view = 'HorizontalSlide';
	},

	firstImage: function() {
		this._firstImage();
		
		// fix safari rendering bug
		var dimsMax = Element.getMaxDimensions(this.imageCached(this.pos));
		this.image(this.pos).style.width  = Math.floor(dimsMax.width/2)  + 'px';
		this.image(this.pos).style.height = Math.floor(dimsMax.height/2) + 'px';

		this.moveIn(this.pos);
		this.prepareNext(this.pos+1);	
		if (this.pos > 0) this.preparePrevious(this.pos-1);
	},

	previousImage: function() {
		if (!this.isValidImagePos(this.pos-1)) return;
		this._previousImage();

		this.moveOutToRight(this.pos+1);
		this.moveIn(this.pos);
		this.preparePrevious(this.pos-1);
	},

	nextImage: function() {
		if (!this.isValidImagePos(this.pos+1)) return;
		this._nextImage();

		this.moveOutToLeft(this.pos-1);
		this.moveIn(this.pos);
		this.prepareNext(this.pos+1);
	},


	// moves image from the center to the left border of the window
	moveOutToLeft: function(pos) {
		if (!this.item(pos)) return;
		this.hideInfo(pos);
		this.item(pos).style.zIndex = 900;

		var dims = Element.getMaxDimensions(this.imageCached(pos));
		var x = Math.floor(0 - dims.width/4);
		var y = Math.floor(window.getInnerHeight()/2 - dims.height/4);

		new Effect.Move(this.item(pos), { x: x, y: y, mode: 'absolute', duration: this.options.transitionTime });
		new Effect.Scale(this.image(pos), 50, {
			scaleFromCenter: true,
			duration: this.options.transitionTime
		});
		this.unpreparePrevious(pos-1);
	},

	// moves image from the center to the right border of the window
	moveOutToRight: function(pos) {
		if (!this.item(pos)) return;
		this.hideInfo(pos);
		this.item(pos).style.zIndex = 900;

		var dims = Element.getMaxDimensions(this.imageCached(pos));
		var x = Math.floor(window.getInnerWidth() - dims.width/4);
		var y = Math.floor(window.getInnerHeight()/2 - dims.height/4);

		new Effect.Move(this.item(pos), { x: x, y: y, mode: 'absolute', duration: this.options.transitionTime });
		new Effect.Scale(this.image(pos), 50, {
			scaleFromCenter: true,
			duration: this.options.transitionTime
		});
		this.unprepareNext(pos+1);
	},

	// moves image to the center
	moveIn: function(pos) {
		if (!this.item(pos)) return;
		this.item(pos).style.zIndex = 1000;

		var dims    = Element.getDimensions(this.imageCached(pos));
		var dimsMax = Element.getMaxDimensions(this.imageCached(pos));
		var dimsNow = Element.getDimensions(this.image(pos));

		var scaleFrom = Math.floor(100 * dimsNow.width / dims.width);
		var scaleTo   = 100;
		if (dims.width  > dimsMax.width)  scaleTo = dimsMax.width  / dims.width * 100;
		if (dims.height > dimsMax.height) scaleTo = dimsMax.height / dims.height * 100;
		if (isNaN(scaleFrom)) scaleFrom = 50;

		var x = Math.floor((window.getInnerWidth()  - dimsMax.width)/2);
		var y = Math.floor((window.getInnerHeight() - dimsMax.height)/2 - 20);

		if (!Element.visible(this.item(pos))) {
			this.item(pos).style.left = Math.floor(window.getInnerWidth() * 1.5) + 'px';
			this.item(pos).style.top  = Math.floor(window.getInnerHeight()/ 2 - dimsMax.height/4) + 'px';
			this.item(pos).style.display = 'block';
		}

		new Effect.Scale(this.image(pos), scaleTo, {
			duration: this.options.transitionTime,
			scaleFromCenter: true,
			scaleFrom: scaleFrom,
			mode: 'absolute',
			scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
			afterFinish: function () {
				this.showInfo(pos);
				this.unlockNavigation(pos);
				this._createImage(pos+2);
			}.bind(this)
		});
		new Effect.Move(this.item(pos), { x: x, y: y, mode: 'absolute', duration: this.options.transitionTime });
	},


	// slides image half into the window from the right
	prepareNext: function(pos) {
		if (!this.item(pos)) this._createImage(pos);
		if (!this.item(pos)) return false;

		var dimsMax = Element.getMaxDimensions(this.imageCached(pos));
		var x = Math.floor(window.getInnerWidth() - dimsMax.width*1/8);
		var y = Math.floor(window.getInnerHeight()/2 - dimsMax.height/4);

		this.image(pos).onclick = this.nextImage.bind(this);
		this.image(pos).style.width  = Math.floor(dimsMax.width/2)  + 'px';
		this.image(pos).style.height = Math.floor(dimsMax.height/2) + 'px';

		this.item(pos).style.left = window.getInnerWidth()*1.2 + 'px';
		this.item(pos).style.top  = y + 'px';
		this.item(pos).style.display = 'block';

		new Effect.Move(this.item(pos), { x: x, y: y, mode: 'absolute', duration: this.options.transitionTime });
	},

	// slides image half into the window from the left
	preparePrevious: function(pos) {
		if (!this.item(pos)) this._createImage(pos);
		if (!this.item(pos)) return false;

		var dimsMax = Element.getMaxDimensions(this.imageCached(pos));
		var x = Math.floor(0 - dimsMax.width*3/8);
		var y = Math.floor(window.getInnerHeight()/2 - dimsMax.height/4);

		this.image(pos).onclick = this.previousImage.bind(this);
		this.image(pos).style.width  = Math.floor(dimsMax.width/2)  + 'px';
		this.image(pos).style.height = Math.floor(dimsMax.height/2) + 'px';

		this.item(pos).style.left = '-' + (dimsMax.width*2) + 'px';
		this.item(pos).style.top  = y + 'px';
		this.item(pos).style.display = 'block';

		new Effect.Move(this.item(pos), { x: x, y: y, mode: 'absolute', duration: this.options.transitionTime });
	},
	
	// slides item out to the right
	unprepareNext: function(pos) {
		if (!this.item(pos)) return;
		var dims = Element.getDimensions(this.imageCached(pos));
		var x = Math.floor(window.getInnerWidth() + dims.width*1.2);
		var y = parseInt(this.item(pos).style.top);
		new Effect.Move(this.item(pos), { x: x, y: y, mode: 'absolute', duration: this.options.transitionTime });
	},
	
	// slides item out to the left
	unpreparePrevious: function(pos) {
		if (!this.item(pos)) return;
		var dims = Element.getDimensions(this.imageCached(pos));
		var x = -(dims.width*1.2);
		var y = parseInt(this.item(pos).style.top);
		new Effect.Move(this.item(pos), { x: x, y: y, mode: 'absolute', duration: this.options.transitionTime });
	},
	
	// adding hook functions
	_selectBrowserImageHook: function(pos) {
		// hide every visible image
		for (var i = 0; i < this.numImages(); i++) {
			if (this.item(i) && Element.visible(this.item(i)))
				Element.hide(this.item(i));
		}
		this.prepareNext(pos+1);
		this.preparePrevious(pos-1);
	},

	_rewindShowHook: function() {
		if (this.pos == 0) return;
		if (this.pos == 1) return this.previousImage();
		this.unprepareNext(this.pos+1);
		this.unprepareNext(this.pos);
		if (this.pos != 2) this.unprepareNext(this.pos-1);

		this.pos = 0;
		this.__addDefaultImageActions(this.pos);
		var dimsMax = Element.getMaxDimensions(this.imageCached(this.pos));
		var x = Math.floor(0 - dimsMax.width*2);
		var y = Math.floor(window.getInnerHeight()/2 - dimsMax.height/4);

		this.image(this.pos).style.width  = Math.floor(dimsMax.width/2)  + 'px';
		this.image(this.pos).style.height = Math.floor(dimsMax.height/2) + 'px';
		this.item(this.pos).style.left = x + 'px';
		this.item(this.pos).style.top  = y + 'px';
		this.item(this.pos).style.display = 'block';
		this.viewImage(this.pos);
		this.prepareNext(this.pos+1);
	}

});










Liteshow.Fader = Class.create();
Object.extend(Object.extend(Liteshow.Fader, Liteshow), {

	localInit: function() {
		LiteConfig.view = 'Fader';
	},

	firstImage: function() {
		this._firstImage();
		this.appear();
	},

	previousImage: function() {
		if (!this.isValidImagePos(this.pos-1)) return;
		this._previousImage();

		this.hideVisibleImage(this.pos+1);
		this.hideVisibleImage(this.pos+2);
		this.appear();
	},

	nextImage: function() {
		if (!this.isValidImagePos(this.pos+1)) return;
		this._nextImage();

		this.hideVisibleImage(this.pos-1);
		this.hideVisibleImage(this.pos-2);
		this.appear();
	},
	
	hideVisibleImage: function(pos) {
		if (!this.item(pos)) return false;
		if (Element.visible(this.item(pos))) {
			this.hideInfo(pos);
			new Effect.Fade(this.item(pos), { queue: { position: 'front', scope: 'image' }, transition: Effect.Transitions.linear, duration: 0.5 } );
		}
	},
	
	appear: function() {
		this.adjustItem(this.pos);
		new Effect.Appear(this.item(this.pos), { queue: { position: 'end', scope: 'image' }, duration: 0.5, afterFinish: function() { this.showInfo(this.pos); }.bind(this) } );
	},

	adjustItem: function(pos) {
		var dims = Element.getMaxDimensions(this.imageCached(pos));
		this.item(pos).style.left = Math.floor((window.getInnerWidth()-dims.width)/2) + 'px';
		this.item(pos).style.top  = Math.floor((window.getInnerHeight()-dims.height)/2)-20 + 'px';
		this.item(pos).style.width   = dims.width + 'px';
		this.image(pos).style.width  = dims.width + 'px';
		this.image(pos).style.height = dims.height + 'px';
	},
	
	_rewindShowHook: function() {
		this.hideVisibleImage(this.pos);
		this.pos = 1;
		this._previousImage();
		this.appear();
	}
});








Liteshow.SlideDown = Class.create();
Object.extend(Object.extend(Liteshow.SlideDown, Liteshow), {
	effectInProgress: null,

	localInit: function() {
		LiteConfig.view = 'SlideDown';
	},

	firstImage: function() {
		this._firstImage();
		this.appear();
	},

	previousImage: function() {
		if (!this.isValidImagePos(this.pos-1)) return;
		this._previousImage();

		this.hideVisibleImage(this.pos+1);
		this.hideVisibleImage(this.pos+2);
		this.appear();
	},

	nextImage: function() {
		if (!this.isValidImagePos(this.pos+1)) return;
		this._nextImage();

		this.hideVisibleImage(this.pos-1);
		this.hideVisibleImage(this.pos-2);
		this.appear();
	},
	
	hideVisibleImage: function(pos) {
		if (!this.item(pos)) return false;
		if (Element.visible(this.item(pos))) {
			this.hideInfo(pos);
			new Effect.SlideUp(this.item(pos), { queue: { position: 'front', scope: 'image' }, transition: Effect.Transitions.linear, duration: this.options.transitionTime, delay: 0.3 } );
		}
	},
	
	appear: function() {
		this.adjustItem(this.pos);
		if (this.effectInProgress) {
			this.effectInProgress.cancel();
		}
		this.effectInProgress = new Effect.SlideDown(this.item(this.pos), { queue: { position: 'end', scope: 'image' }, duration: this.options.transitionTime, delay: 0.3, afterFinish: this.showInfo.bind(this, this.pos) } );
	},

	adjustItem: function(pos) {
		var dims = Element.getMaxDimensions(this.imageCached(pos));
		this.item(pos).style.left = Math.floor((window.getInnerWidth()-dims.width)/2) + 'px';
		this.item(pos).style.top  = Math.floor((window.getInnerHeight()-dims.height)/2)-20 + 'px';
		this.item(pos).style.width   = dims.width + 'px';
		this.image(pos).style.width  = dims.width + 'px';
		this.image(pos).style.height = dims.height + 'px';
	},
	
	_rewindShowHook: function() {
		this.hideVisibleImage(this.pos);
		this.pos = 1;
		this._previousImage();
		this.appear();
	}
});
