var JSGallery2 = new Class({
	Implements: Options,
	options: {
		'borderWidthDeselected': 0,	//the width of the border of an deselected image (should be the same as in css)
		'borderWidthSelected': 0,	//border width of the selected image
		'borderColor': '#ffffff',
		'borderColorSelected': '#ffffff',
		'borderColorDeselected': '#87BDDA',		//the color of the borders
		'loadingMask': '#87BDDA',		//color of the mask overlay during loading of the big images
		'loadingOpacity': 0.6,		//opacity of the border div during loading (including the mask)
		'loadingImage': null,		//you may specify a loading image which is displayed while the big images are being loaded
		'selectSpeed': 200,			//the duration of the select effect in ms (high values will make it look ugly)
		'fadeSpeed': 250,			//the duration of the image fading effect in ms
		'pageSpeed': 1000,			//the duration of the change page effect in ms
		'prevHandle': null,			//if you pass a previous page handle in here, it will be hidden if it's not needed
		'nextHandle': null,			//like above, but for next page
		'titleTarget': 'caption',		//target HTML element where image texts are copied into
		'initialIndex': -1,			//which thumb to select after init. you could create deep links with it.
		'maxOpacity': 0.8			//maximum opacity before cursor reaches prev/next control, then it will be set to 1 instantly.
	},
	/**
	 *	Constructor. Starts up the whole thing :-)
	 *
	 *	This script is free to use. It has been created by http://www.aplusmedia.de and
	 *	can be downloaded on http://www.esteak.net.
	 *	License: GNU GPL 2.0: http://creativecommons.org/licenses/GPL/2.0/
	 *	Example on: http://blog.aplusmedia.de/moo-gallery2
	 *	Known issues:
	 *	- preloading does not care about initialIndex param
	 *	- hovering to a control over the border of the big image will make the other one flickering
	 *	- if you enter and leave the control area very quickly, the control flickers sometimes
	 *	- does not work in IE6
	 *
	 * 	@param {Array} thumbs, An array of HTML elements
	 *	@param {HTMLelement} bigImageContainer, the full size image
	 *	@param {HTMLelement} pageContainer, If you have several pages, put them in this container
	 *	@param {Object} options, You have to pass imagesPerPage if you have more than one!
	 */
	initialize: function(thumbs, bigImageContainer, pageContainer, options) {
		this.currentPageNumber = 0;
		this.loadedImages = 0;
		this.blockKeys = false;
		this.pageContainer = pageContainer;
		
/*--------------------------------------------------------------------*/		
/*this.imagesPerPage = pageContainer.getFirst().getChildren().length;*/
		var imgWin = this.pageContainer.getFirst();
		var imgDiv = imgWin.getFirst();
		//var imgH = imgDiv.offsetHeight + parseInt(imgDiv.getStyle('margin-bottom')) + parseInt(imgDiv.getStyle('margin-top'));
		//var imgW = imgDiv.offsetWidth + parseInt(imgDiv.getStyle('margin-right')) + parseInt(imgDiv.getStyle('margin-left'));
		var imgH = imgDiv.getSize().y; 
		var imgW = imgDiv.getSize().x;
		var winH = imgWin.getSize().y; 
		var winW = imgWin.getSize().x;
		var columns = Math.floor(winW / imgW);
		var rows = Math.floor(winH / imgH);
		var imgVis = columns * rows; 
		this.imagesPerPage = imgVis;
/*--------------------------------------------------------------------*/
		
		this.setOptions(options);
		this.thumbs = thumbs;
		this.bigImage = bigImageContainer;
		if ($defined(this.bigImage)){
			this.convertThumbs();
		}
this.createPages();
		var pageURL = location.href;
		if (pageURL.indexOf('img=') != -1){
			this.options.initialIndex = parseInt(pageURL.slice(pageURL.indexOf('img=')+4));
		}
		if(this.options.initialIndex != -1) {
			this.selectByIndex(this.options.initialIndex -1);
		} else {
			this.gotoPage(0);
		}
		//this.createControls();
		if($defined(this.options.loadingImage)) {
			new Asset.image(this.options.loadingImage);
		}
		this.loadNextImage();
	},
	createPages: function() {
		var page = this.pageContainer.getFirst();
		var pageW = page.getStyle('width');
		var imgAmt = this.thumbs.length;
		var pageAmt = Math.ceil(imgAmt / this.imagesPerPage);
		if (imgAmt % this.imagesPerPage > 0){
			var lastPageW = (imgAmt % this.imagesPerPage) * this.thumbs[0].getSize().x + 10;
		}else{
			var lastPageW = this.pageContainer.getFirst().getSize().x;
		}
		
		for (x=0; x<pageAmt-1; x++){
			var num = x+1;
			if (x == pageAmt -2){
				var pagex = new Element('div',{
					'id': 'page'+num,
					'class': 'page',
					'styles': {'width':lastPageW} 
				});
			}else{
				var pagex = new Element('div',{
					'id': 'page'+num,
					'class': 'page',
					'styles': {'width':pageW}
				});
			}
			pagex.injectInside(this.pageContainer);
			var startSlice = (this.imagesPerPage*num);
			var endSlice =   (this.imagesPerPage*num)+this.imagesPerPage;
			
			if (imgAmt < endSlice){
				var contents = this.thumbs.slice(startSlice);
			}else{
				var contents = this.thumbs.slice(startSlice,endSlice);
			}
			for (i=0; i<contents.length; i++){
				contents[i].injectInside(pagex); 
			}
			
		}
	},
	/**
	 *	Creates the previous and next links over the big image.
	
	createControls: function() {
		if ($defined(this.prevLink) && $defined(this.nextLink) && $defined(this.bigImage)){
			this.prevLink = new Element('a', {
				events: {
					'click': this.prevImage.bindWithEvent(this),
					'mouseleave': this.mouseLeaveHandler.bindWithEvent(this)
				},
				styles: {
					'width': '46px',
					'display': 'block',
					'position': 'absolute',
					'top': 0,
					'height' : '98%',
					'background': 'url(prev_image.png) no-repeat 0 50%',
					'outline': 'none'
				},
				'href': '#'
			}).injectInside(this.bigImage.getParent());
	
			this.prevLink.addEvent('mouseover', this.focusControl.bindWithEvent(this, this.prevLink));
			this.nextLink = this.prevLink.clone().injectAfter(this.prevLink).set({
				'events': {
					'click': this.nextImage.bindWithEvent(this),
					'mouseleave': this.mouseLeaveHandler.bindWithEvent(this)
				},
				'styles': {
					'right': 0, 
					'background-image': 'url(next_image.png)'
				}
			});
			this.prevLink.setStyle('left', 0);
			this.nextLink.addEvent('mouseover', this.focusControl.bindWithEvent(this, this.nextLink));
			this.bigImage.addEvents({
				'mousemove': this.mouseOverHandler.bindWithEvent(this),
				'mouseleave': this.mouseLeaveHandler.bindWithEvent(this)
			});
			document.addEvent('keydown', this.keyboardHandler.bindWithEvent(this));
			this.mouseLeaveHandler();
		}
	},*/
	/**
	 * Focuses one control
	 * 
	 * @param {Event} event
	 * @param {HTMLElement} control
	
	focusControl: function(event, control) {
		control.set('opacity', 1);
	},  */
	/**
	 *	Handles mouse movement over the big image.
	 * @param {Event} event
	 */
	mouseOverHandler: function(event) {
		var currentIndex = this.thumbs.indexOf(this.selectedContainer);
		//this makes the control on the other side fade out in just the moment when you reach one
		var activeRange = this.bigImage.getSize().x;
		var op = 0;
		if(currentIndex < this.thumbs.length - 1) {
			op = this.options.maxOpacity - this.getDistanceToMouse(this.nextLink, event) / activeRange;
		}
		this.nextLink.set('opacity', op);
		op = 0;
		if(currentIndex > 0) {
			op = this.options.maxOpacity - this.getDistanceToMouse(this.prevLink, event) / activeRange;
		}
		this.prevLink.set('opacity', op);
	},
	/**
	 * Hides the controls.
	 */
	mouseLeaveHandler: function() {
		if (this.prevLink != null && this.nextLink!= null){
			this.nextLink.set('opacity', 0);
			this.prevLink.set('opacity', 0);
		}
	},
	/**
	 * Handles keyboard interactions.
	 * @param {Event} event
	 */
	keyboardHandler: function(event){
		if(!this.blockKeys) {
			if(event.code >= 49 && event.code <= 57) {
				this.gotoPage(event.key - 1);
			} else if (event.key == "left") {
				this.prevImage(event); 					
			} else if (event.key == "right") {
				this.nextImage(event);
			}
		}
	},
	/**
	 *	Returns the distance to the mouse from the middle of a given element.
	 *	@param {HTMLelement} element
	 *	@param {Event} event
	 * 	@return integer
	 */
	getDistanceToMouse: function(element, event) {
		var s = element.getSize();
		var xDiff = Math.abs(event.client.x - (element.getLeft() + s.x / 2));
		var yDiff = Math.abs(event.client.y - (element.getTop() + s.y / 2));
		return Math.sqrt( Math.pow(xDiff, 2) + Math.pow(yDiff, 2) );
	},
	/**
	 * 	Adds the border to the thumbs and so on. (conversion of static thumbs)
	 */
	convertThumbs: function() {
		this.thumbs.each(function(thumbContainer, count) {
			this.convertThumb(thumbContainer, count);
		}, this);
	},
	/**
	 * Converts one single thumb.
	 * @param {HTMLelement} thumbContainer
	 * @param {Integer} count
	 */
	convertThumb: function(thumbContainer, count) {
		if(!$defined(thumbContainer)) {
			return;
		}
		if ($defined(this.bigImage)){
			thumbContainer.addEvent('click', this.select.bind(this, thumbContainer)).setStyle('position', 'relative').set('counter', count);
		}
		/*var bigImage = thumbContainer.getFirst().set('href', 'javascript: void(0);').get('rel');*/
		var bigImage = thumbContainer.getFirst().get('rel');
		var border = new Element('div', {
			styles: {
				//'border': this.options.borderWidthDeselected + 'px solid ' + this.options.borderColor,
				'width': thumbContainer.getSize().x,
				'height': thumbContainer.getSize().y,
				'position': 'absolute',
				'background-color': this.options.loadingMask,
				'top': 0,
				'left': 0
			},
			'rel': bigImage,
			'description': thumbContainer.getFirst().innerHTML,
			'link': thumbContainer.getFirst().href
		}).injectTop(thumbContainer).set('opacity', this.options.loadingOpacity);
	},
	/**
	 * 	Removes key blocking.
	 */
	unBlockKeys: function() {
		this.blockKeys = false;
	},
	/**
	 *	Selects a certain image. (You have to pass the outer container of the image)
	 *	@param {HTMLelement} container
	 */
	select: function(container) {
		if(this.blockKeys || !$defined(container)) {
			return false;
		}
		this.blockKeys = true;
		if($defined(this.selectedContainer)) {
			//this prevents an ugly effect if you click on the currently selected item
			if(container == this.selectedContainer) {
				this.unBlockKeys();
				return false;
			}
			this.deselect(this.selectedContainer);
		}
		//if target image is not on current page, we have to go there first 
		var targetPage = Math.floor(container.get('counter') / this.imagesPerPage);
		if(this.currentPageNumber != targetPage) {
			this.gotoPage(targetPage, container);
		}
		this.selectedContainer = container;
		//make calculations a bit more handy
		//var s = container.getSize();
		//var des = this.options.borderWidthDeselected;
		//var sel = this.options.borderWidthSelected;
		
		if ($defined(this.bigImage)){
			new Fx.Morph(container, {
				duration: this.options.selectSpeed, 
				transition: Fx.Transitions.Quad.easeOut
			}).start({'border-color':[this.options.borderColorDeselected,this.options.borderColorSelected]});
			var source = container.getFirst();
			if (source.get('link') != '' && source.get('link') != 'javascript:void(0);'){
				this.setImage(source.get('rel'),source.get('description'),source.get('link'));
			}else{
				this.setImage(source.get('rel'),source.get('description'));
			}
		}
	},
	/**
	 * Preloads one big image
	 */
	loadNextImage: function() {
		var thumbContainer = this.thumbs[this.loadedImages].getFirst();
		if($defined(this.options.loadingImage)) {
			new Element('img', {'src': this.options.loadingImage}).injectTop(thumbContainer);
		}
		var imageToLoad = thumbContainer.get('rel');
		new Asset.image(imageToLoad, {
			onload: this.imageLoaded.bind(this, this.thumbs[this.loadedImages])
		});
	},
	/**
	 * Callback after an image has been successfully preloaded.
	 * Removes the loading effects from the border div.
	 * @param {HTMLElement} thumbContainer the thumb wrapper div
	 */
	imageLoaded: function(thumbContainer) {
		this.loadedImages++;
		if($defined(this.options.loadingImage)) {
			//remove loading gif
			thumbContainer.getFirst().getFirst().destroy();
		}
		//remove loading styles
		thumbContainer.getFirst().setStyle('background-color', 'transparent').setOpacity(1);
		if(this.loadedImages < this.thumbs.length) {
			this.loadNextImage();
		}
	},
	/**
	 * Selects an image by its thumbnail index.
	 * @param {integer} index of the thumbnail, starting with 0
	 */
	selectByIndex: function(index) {
		this.mouseLeaveHandler();
		if(index < 0 || this.thumbs.length <= index) {
			index = 0;
		}
		this.select(this.thumbs[index]);
	},
	/**
	 *	Opposite to method above.
	 *	@param {HTMLelement} container
	 */
	deselect: function(container) {
		new Fx.Morph(container, {duration: this.options.selectSpeed, transition: Fx.Transitions.Quad.easeOut}).start({'border-color':[this.options.borderColorSelected,this.options.borderColorDeselected]});
	},
	/**
	 *	Changes the full size image to given one.
	 *	@param {String} newSrc, new target of the full size image
	 *	@param {String} newText, new text for the info element
	 */
	setImage: function(newSrc,newText,newLink) {
		var effect = new Fx.Tween(this.bigImage, {duration: this.options.fadeSpeed, transition: Fx.Transitions.Quad.easeOut});
		effect.start('opacity', 0).chain(function(){									  
			this.bigImage.set('src', newSrc);
			this.bigImage.setStyle('width', $('bigimageContainer').getSize().x);
			this.bigImage.setStyle('height', 'auto');
			if($('bigImage').getSize().y < $('bigimageContainer').getSize().y){$('bigImage').setStyle('height',$('bigimageContainer').getSize().y)}
			if (newLink != ''){this.bigImage.getParent().set('href',newLink)};
			this.mouseLeaveHandler();
			effect.start('opacity', 1).chain(this.unBlockKeys.bind(this));
		}.bind(this));
		
		var caption = $(this.options.titleTarget);
		var captionEffect = new Fx.Tween(caption, {duration: this.options.fadeSpeed, transition: Fx.Transitions.Quad.easeOut});
		
		captionEffect.start('opacity', 0).chain(function(){
			new Asset.image(newSrc, {
				onload: function(){
					captionEffect.start('opacity', '0.7').chain(caption.set('styles', {'width':$('bigimageContainer').getSize().x} ));
					if($('bigImage').getSize().x < $('bigimageContainer').getSize().x){$('bigImage').setStyle('width',$('bigimageContainer').getSize().x)}
					else if($('bigImage').getSize().y < $('bigimageContainer').getSize().y){$('bigImage').setStyle('height',$('bigimageContainer').getSize().y)}
				}
			});
		}.bind(this));
		captionEffect.chain(this.unBlockKeys.bind(this));
		
		var captionText = $(this.options.titleTarget).getNext();
		var captionTextEffect = new Fx.Tween(captionText, {duration: this.options.fadeSpeed, transition: Fx.Transitions.Quad.easeOut});
		
		captionTextEffect.start('opacity', 0).chain(function(){
			if($defined($(this.options.titleTarget))) {
				captionText.set('html', newText);
			}
			new Asset.image(newSrc, {
				onload: function(){
					captionEffect.chain(captionText.set('styles',{'opacity':'1'}));
				}
			});
		}.bind(this));
		captionEffect.chain(this.unBlockKeys.bind(this));
	},
	/**
	 *	Navigates to previous page.
	 */
	prevPage: function() {
		this.gotoPage(this.currentPageNumber - 1);
	},
	/**
	 *	Navigates to next page.
	 */
	nextPage: function() {
		this.gotoPage(this.currentPageNumber + 1);
	},
	/**
	 *	Selects the previous image.
	 */
	prevImage: function(e) {
		e = new Event(e).stop();
		this.selectByIndex(this.thumbs.indexOf(this.selectedContainer) - 1);
	},
	/**
	 *	Selects the next image.
	 */
	nextImage: function(e) {
		e = new Event(e).stop();
		this.selectByIndex(this.thumbs.indexOf(this.selectedContainer) + 1);
	},
	/**
	 *	Navigates to given page and selects the first image of it.
	 *	Also hides the handles (if set).
	 *	@param {Integer} pageNumber, index of the target page (0-n)
	 *  @param {HTMLElement} selectImage, optionally receives a particular image to select
	 */
	gotoPage: function(pageNumber, selectImage) {
		//if we like to select another image on that page than the first one
		selectImage = $pick(selectImage, this.thumbs[pageNumber * this.imagesPerPage]);
		var lastPage = Math.ceil(this.thumbs.length / this.imagesPerPage);
		var movement = 0;
		for (x=0; x< pageNumber; x++){
			movement += this.pageContainer.getElements('.page')[x+1].getSize().x;

		}
		if(pageNumber >= 0 && pageNumber < lastPage) {
			this.pageContainer.set('tween', {
				duration: this.options.pageSpeed, 
				transition: Fx.Transitions.Quint.easeInOut
			});
			this.pageContainer.tween('margin-left', movement * -1);
			this.currentPageNumber = pageNumber;
			this.select(selectImage);
			
			//update handles
			if($defined(this.options.prevHandle)) {
				new Fx.Tween(this.options.prevHandle, {duration:this.options.fadeSpeed * 2}).start('opacity', pageNumber == 0 ? 0 : 1);
			}
			if($defined(this.options.nextHandle)) {
				new Fx.Tween(this.options.nextHandle, {duration:this.options.fadeSpeed * 2}).start('opacity', pageNumber == lastPage - 1 ? 0 : 1);
			}
		}
	}
});
