/*
	This file is part of chrisAdoyle's SmoothGallery v0.1.
	
	chrisAdoyle's SmoothGallery is essentially a port of JonDesign's SmoothGallery,
	using the Prototype javascript framework instead of Moo, and is based extensively on
	the excellent work of Jonathon Schemoul.
	
	chrisAdoyle's SmoothGallery is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.
	
	chrisAdoyle's SmoothGallery is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with chrisAdoyle's SmoothGallery; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
	
	chrisAdoyle's SmoothGallery Main Developer:  Chris Doyle (http://www.chrisAdoyle.com/)
	JonDesign's SmoothGallery Main Developer: Jonathan Schemoul (JonDesign: http://www.jondesign.net/)
	Contributed code by:
	- Christian Ehret (bugfix)
	- Nitrix (bugfix)
	- Valerio from Mad4Milk for his great help with the carousel scrolling and many other things.
	- Archie Cowan for helping me find a bugfix on carousel inner width problem.
	Many thanks to:
	- The mootools team for the great mootools lib, and it's help and support throughout the project.
*/

var Gallery = Class.create({
	initialize: function(element, options) {
	
		if (!options) options = {};
		this.setOptions({
			showArrows: true,
			showCarousel: true,
			showInfopane: true,
			embedLinks: true,
			thumbHeight: 75,
			thumbWidth: 100,
			thumbSpacing: 10,
			preloader: true,
			/* Data retrieval */
			manualData: [],
			destroyAfterPopulate: true,
			elementSelector: "div.imageElement",
			titleSelector: "h3",
			subtitleSelector: "p",
			linkSelector: "a.open",
			imageSelector: "img.full",
			thumbnailSelector: "img.thumbnail",
			/* InfoPane options */
			slideInfoZoneOpacity: 0.7,
			slideInfoZoneSlide: true,
			/* Carousel options */
			carouselMinimizedOpacity: 0.7,
			carouselMinimizedHeight: 20,
			carouselMaximizedOpacity: 0.9,
			textShowCarousel: 'Bilder',
			showCarouselLabel: true,
			useThumbGenerator: false,
			thumbGenerator: 'resizer.php',
			useExternalCarousel: false,
			carouselElement: false,
			activateCarouselScroller: true,
			/* CSS Classes */			
			baseClass: 'jdGallery',
			withArrowsClass: 'withArrows',
		timed: false,
		delay: 5000
		}, options);
		
//		this.fireEvent('onInit');
		this.timer = null;
		this.waitingForEffect = 0;
		this.currentIter = 0;
		this.lastIter = 0;
		this.maxIter = 0;
		this.galleryElement = element;
		this.galleryData = this.options.manualData;
		this.galleryInit = 1;
		this.galleryElements = Array();
		this.thumbnailElements = Array();
		this.galleryElement.addClassName(this.options.baseClass);
		
		this.populateFrom = element;
		this.populateData();
		element.style.display = "block";
		
		if (this.options.embedLinks)
		{
			this.currentLink = new Element('a').addClassName('open');
			this.currentLink.setAttribute( 'href', '#' );
			this.currentLink.setAttribute( 'title', '' );
			this.currentLink.setAttribute( 'rel', 'lightbox' );
			element.insert(this.currentLink);
			if ((!this.options.showArrows) && (!this.options.showCarousel))
				this.galleryElement = element = this.currentLink;
			else
				this.currentLink.setStyle('display', 'none');
		}
		
		this.constructElements();
		if ((this.galleryData.length>1)&&(this.options.showArrows)) {
			this.leftArrow = new Element('a').addClassName('left');
			element.insert(this.leftArrow);
			this.leftArrow.observe('click', this.prevItem.bind(this));
			
			this.rightArrow = new Element('a').addClassName('right');
			element.insert(this.rightArrow);
			this.rightArrow.observe('click', this.nextItem.bind(this));
			this.galleryElement.addClassName(this.options.withArrowsClass);
		}
		
		this.loadingElement = new Element('div').addClassName('loadingElement');
		element.insert(this.loadingElement);
		if (this.options.showInfopane) this.initInfoSlideshow();
		if ((this.galleryData.length>1)&&(this.options.showCarousel)) this.initCarousel();
		this.doSlideShow(1);
	},

	nextItem: function() {
//		this.fireEvent('onNextCalled');
		this.nextIter = this.currentIter+1;
		if (this.nextIter >= this.maxIter)
			this.nextIter = 0;
		this.galleryInit = 0;
		this.goTo(this.nextIter);
	},

	prevItem: function() {
//		this.fireEvent('onPreviousCalled');
		this.nextIter = this.currentIter-1;
		if (this.nextIter <= -1)
			this.nextIter = this.maxIter - 1;
		this.galleryInit = 0;
		this.goTo(this.nextIter);
	},

	goTo: function(num) {
		this.clearTimer.bind(this)();
		if (this.options.embedLinks)
			this.clearLink();
		if (this.options.showInfopane)
		{
			this.hideInfoSlideShow();
			this.changeItem(num);
		} else
			this.changeItem.delay(500, this, num);
		if (this.options.embedLinks)
			this.makeLink(num);
		if (this.options.showCarousel)
			this.hideCarousel();
			
		this.prepareTimer.bind(this)();
	},

	changeItem: function(num) {
//		this.fireEvent('onStartChanging');
		this.galleryInit = 0;
		if (this.currentIter != num)
		{
			for(i=0;i<this.maxIter;i++)
			{
				if ((i != this.currentIter)) {
					this.galleryElements[i].setStyle({'opacity': 0});
				}
			}
			// This is where Jon had support for different forward/backward transitions
			this.addEffectWaiter();
			new Effect.Parallel([
				new Effect.Appear(this.galleryElements[num], {sync: true}),
				new Effect.Fade(this.galleryElements[this.currentIter], {sync: true})], {afterFinish: this.removeEffectWaiter.bind(this)});
			this.currentIter = num;
		}
		this.doSlideShow.bind(this)();
//		this.fireEvent('onChanged');
	},

	startSlideShow: function() {
		//~ this.fireEvent('onStart');
		this.loadingElement.style.display = "none";
		this.lastIter = this.maxIter - 1;
		this.currentIter = 0;
		this.galleryInit = 0;
		this.galleryElements[parseInt(this.currentIter)].setStyle({'opacity': 1});
		if (this.options.showInfopane)
			this.showInfoSlideShow();
		if (this.options.embedLinks)
			this.makeLink(this.currentIter);
		
		this.prepareTimer.bind(this)();
	},
	
	clearTimer: function() {
		if( this.options.timed )
			clearTimeout(this.timer);
	},
	
	prepareTimer: function() {
		if( this.options.timed )
			this.timer = setTimeout( this.nextItem.bind(this), this.options.delay );
	},
	
	initInfoSlideshow: function() {
		if (this.slideInfoZone)
			this.slideInfoZone.remove();
		this.slideInfoZone = new Element('div').addClassName('slideInfoZone');
		this.galleryElement.insert(this.slideInfoZone);
		var slideInfoZoneTitle = new Element('h2');
		this.slideInfoZone.insert(slideInfoZoneTitle);
		var slideInfoZoneDescription = new Element('p');
		this.slideInfoZone.insert(slideInfoZoneDescription);
		this.slideInfoZone.normalHeight = this.slideInfoZone.offsetHeight;
		var offset = this.galleryElement.getHeight() - this.slideInfoZone.offsetHeight;
		this.slideInfoZone.setStyle({opacity: 0, top: offset});
	},

	showInfoSlideShow: function() {
//		this.fireEvent('onShowInfopane');
		element = this.slideInfoZone;
		element.select('h2')[0].innerHTML = this.galleryData[this.currentIter].title;
		element.select('p')[0].innerHTML = this.galleryData[this.currentIter].description;
		
		this.addEffectWaiter();
		this.slideInfoZone.setStyle({'height': this.slideInfoZone.normalHeight});
		new Effect.Parallel([
			new Effect.Opacity(this.slideInfoZone, {from: 0, to: this.options.slideInfoZoneOpacity}),
			new Effect.MoveBy(this.slideInfoZone, -this.slideInfoZone.normalHeight, 0)], {afterFinish: this.removeEffectWaiter.bind(this)});
		
		return this.slideInfoZone;
	},

	addEffectWaiter: function() {
		this.waitingForEffect += 1;
		if (this.waitingForEffect == 1)
			this.disableNavigation();
	},
	
	removeEffectWaiter: function() {
		this.waitingForEffect -= 1;
		if (this.waitingForEffect < 0)
			this.waitingForEffect = 0;
		if (this.waitingForEffect == 0)
			this.enableNavigation();
	},

	disableNavigation: function() {
		if (this.leftArrow)
			this.leftArrow.stopObserving('click');
		if (this.rightArrow)
			this.rightArrow.stopObserving('click');
	},

	enableNavigation: function() {
		if (this.leftArrow)
			this.leftArrow.observe('click', this.prevItem.bind(this));
		if (this.rightArrow)
			this.rightArrow.observe('click', this.nextItem.bind(this));
	},


	hideInfoSlideShow: function() {
//		this.fireEvent('onHideInfopane');
//		this.slideInfoZone.clearTimer();
		this.addEffectWaiter();
		new Effect.Parallel([
			new Effect.Opacity(this.slideInfoZone, {from: this.slideInfoZoneOpacity, to: 0}),
			new Effect.MoveBy(this.slideInfoZone, this.slideInfoZone.normalHeight, 0)], {afterFinish: this.removeEffectWaiter.bind(this)});
		return this.slideInfoZone;
	},

	doSlideShow: function(position) {
		if (this.galleryInit == 1)
		{
			imgPreloader = new Image();
			imgPreloader.onload=function(){
				this.startSlideShow.bind(this).delay(1);
			}.bind(this);
			imgPreloader.src = this.galleryData[0].image;
            new Effect.MoveBy(this.slideInfoZone, this.slideInfoZone.normalHeight, 0);
		} else {
			if (this.options.showInfopane)
			{
				if (this.options.showInfopane)
				{
					this.showInfoSlideShow.bind(this).delay(1);
				} else
					if ((this.options.showCarousel)&&(this.options.activateCarouselScroller))
						this.centerCarouselOn(position);
			}
		}
	},

	initCarousel: function () {
		var carouselElement;
		if (!this.options.useExternalCarousel)
		{
			var carouselContainerElement = new Element('div').addClassName('carouselContainer');
			this.carouselContainer = carouselContainerElement;
			this.galleryElement.insert(carouselContainerElement);
			this.carouselContainer.normalHeight = carouselContainerElement.offsetHeight;
			this.carouselContainer.setStyle({'opacity': this.options.carouselMinimizedOpacity, 'top': (this.options.carouselMinimizedHeight - this.carouselContainer.normalHeight) + "px"});
			this.carouselBtn = new Element('a').addClassName('carouselBtn');
			this.carouselBtn.title = this.options.textShowCarousel;
			this.carouselBtn.innerHTML = this.options.textShowCarousel;
			carouselContainerElement.insert(this.carouselBtn);
			this.carouselBtn.observe('click', 
				function () {
					this.toggleCarousel();
					this.clearTimer();
				}.bind(this)
			);
			this.carouselActive = false;
			carouselElement = new Element('div').addClassName('carousel');
			carouselContainerElement.insert(carouselElement);
			this.carousel = carouselElement;
		} else {
			carouselElement = this.options.carouselElement.addClass('jdExtCarousel');
		}
		this.carouselWrapper = new Element('div').addClassName('carouselWrapper');
		carouselElement.insert(this.carouselWrapper);
		this.carouselInner = new Element('div').addClassName('carouselInner');
		this.carouselWrapper.insert(this.carouselInner);
		if (this.options.activateCarouselScroller)
		{
			this.carousel.observe('mousemove', function(event) {this.scrollCarousel(event)}.bind(this));
			this.carousel.observe('mouseout', function(event) {this.stopScroll(event)}.bind(this));
		}
		this.constructThumbnails();
		
		this.carouselInner.style.width = ((this.maxIter * (this.options.thumbWidth + this.options.thumbSpacing + 2))+this.options.thumbSpacing) + "px";
	},

	showCarouselFn: function () {
//		this.fireEvent('onShowCarousel');
/*		this.carouselContainer.custom({
			'opacity': this.options.carouselMaximizedOpacity,
			'top': 0
		}).addEvent('onComplete', function() { this.carouselActive = true; this.carouselWrapper.scroller.start(); }.bind(this));
*/
		this.addEffectWaiter();
		this.slideInfoZone.setStyle({'height': this.slideInfoZone.normalHeight});
		new Effect.Parallel([
			new Effect.Opacity(this.carouselContainer, {from: this.options.carouselMinimizedOpacity, to: this.options.carouselMaximizedOpacity}),
			new Effect.MoveBy(this.carouselContainer, this.carousel.getHeight(), 0)], {afterFinish: this.removeEffectWaiter.bind(this)});
		//~ this.carouselContainer.setStyle({'opacity': this.options.carouselMaximizedOpacity, 'top': 0});
		this.carouselActive = true;
//        this.carouselWrapper.scroller.start();
	},

	toggleCarousel: function() {
		if (this.carouselActive) {
			this.hideCarousel();
		} else {
			this.showCarouselFn();
		}
	},

	hideCarousel: function () {
//		this.fireEvent('onHideCarousel');
		if (this.carouselActive) {
			this.addEffectWaiter();
			new Effect.Parallel([
			new Effect.Opacity(this.carouselContainer, {from: this.options.carouselMaximizedOpacity, to: this.options.carouselMinimizedOpacity}),
			new Effect.MoveBy(this.carouselContainer, -this.carousel.getHeight(), 0)], {afterFinish: this.removeEffectWaiter.bind(this)});
			//~ this.carouselContainer.setStyle({
			//~ 'opacity': this.options.carouselMinimizedOpacity,
			//~ 'top': (this.options.carouselMinimizedHeight - this.carouselContainer.normalHeight)
			//~ });
			this.carouselActive = false;
		}
	},

	scrollCarousel: function(event) {
		if (this.carouselInner.getWidth() > this.carousel.getWidth()) {
			scrollWidth = 100;
			inLeftScroll = false;
			inRightScroll = false;
			
			// Check if the mouse is in the left scroll area
			top_position = this.carousel.cumulativeOffset().top - this.carousel.cumulativeScrollOffset().top;
			left_position = this.carousel.cumulativeOffset().left - this.carousel.cumulativeScrollOffset().left;
			if (event.clientX > left_position && event.clientX < left_position + scrollWidth) {
				if (event.clientY > top_position && event.clientY < top_position + scrollWidth) {
					inLeftScroll = true;
				}
			}
			
			// Check if the mouse is in the right scroll area
			left_position += this.carousel.getWidth();
			if (event.clientX < left_position && event.clientX > left_position - scrollWidth) {
				if (event.clientY > top_position && event.clientY < top_position + scrollWidth) {
					inRightScroll = true;
				}
			}
			if (inLeftScroll) this.startLeftScroll();
			if (inRightScroll) this.startRightScroll();
			if (!inLeftScroll && !inRightScroll) this.stopScroll(event);
		}
	},

	startLeftScroll: function() {
		maxScroll = this.carousel.cumulativeOffset().left - this.carouselInner.cumulativeOffset().left;
		new Effect.Move(this.carouselInner, {x: maxScroll, queue: {scope: 'carouselScroll', limit: 1}});
	},

	startRightScroll: function() {
		maxScroll = this.carouselInner.cumulativeOffset().left + this.carouselInner.getWidth() - (this.carousel.cumulativeOffset().left + this.carousel.getWidth());
		new Effect.Move(this.carouselInner, {x: -maxScroll, queue: {scope: 'carouselScroll', limit: 1}});
	},

	stopScroll: function (event) {
		if (navigator.appName=="Microsoft Internet Explorer") {
			Effect.Queues.get('carouselScroll').each(function(e){e.cancel()});
		} else {
			if (event.type == 'mousemove') {
				Effect.Queues.get('carouselScroll').each(function(e){e.cancel()});
			}
			if (event.type == 'mouseout' && !event.rangeParent.hasClassName('carousel')) {
				if (!event.target.hasClassName('carouselWrapper')) {
					Effect.Queues.get('carouselScroll').each(function(e){e.cancel()});
				}
			}
		}
	},

	constructThumbnails: function () {
		element = this.carouselInner;
		for(i=0;i<this.galleryData.length;i++)
		{
			var currentImg = new Element('div').addClassName("thumbnail").setStyle({
					backgroundImage: "url('" + this.galleryData[i].thumbnail + "')",
					backgroundPosition: "center center",
					backgroundRepeat: 'no-repeat',
					marginLeft: this.options.thumbSpacing + "px",
					width: this.options.thumbWidth + "px",
					height: this.options.thumbHeight + "px",
					opacity: .4
				});
			element.insert(currentImg);
			currentImg.observe(
				'mouseover', function (myself) {
//					myself.clearTimer();
					myself.target.setStyle({'opacity': 0.99});
//					if (this.options.showCarouselLabel)
//					$(this.carouselLabel).innerHTML = '<span class="number">' + (myself.relatedImage.number + 1) + "/" + this.maxIter + ":</span> " + myself.relatedImage.title;
				});
			currentImg.observe(
				'mouseout', function (myself) {
//					myself.clearTimer();
					myself.target.setStyle({'opacity': 0.4});
			});
			currentImg.observe(
				'click', function (myself) {
					this.goTo(myself.target.relatedImage.number);
				}.bind(this)
			);
			currentImg.relatedImage = this.galleryData[i];
			this.thumbnailElements[parseInt(i)] = currentImg;
		}
	},

	clearThumbnailsHighlights: function()
	{
		for(i=0;i<this.galleryData.length;i++)
		{
			this.thumbnailElements[i].clearTimer();
			this.thumbnailElements[i].custom(0.2);
		}
	},

	centerCarouselOn: function(num) {
		var carouselElement = this.thumbnailElements[num];
		var position = carouselElement.element.offsetLeft + (carouselElement.element.offsetWidth / 2);
		var carouselWidth = this.carouselWrapper.offsetWidth;
		var carouselInnerWidth = this.carouselInner.offsetWidth;
		var diffWidth = carouselWidth / 2;
		var scrollPos = position-diffWidth;
		this.carouselWrapper.elementScroller.scrollTo(scrollPos,0);
	},

	populateData: function() {
		currentArrayPlace = this.galleryData.length;
		options = this.options;
		var data = this.galleryData;
		this.populateFrom.select(options.elementSelector).each(function(el) {
			elementDict = {
				image: el.select(options.imageSelector)[0].readAttribute('src'),
				number: currentArrayPlace
			};
			if ((options.showInfopane) | (options.showCarousel))
				Object.extend(elementDict, {
					title: el.select(options.titleSelector)[0].innerHTML,
					description: el.select(options.subtitleSelector)[0].innerHTML
				});
			if ((!options.useThumbGenerator) && (options.showCarousel))
				Object.extend(elementDict, {
					thumbnail: el.select(options.thumbnailSelector)[0].src
				});
			if (options.embedLinks)
				Object.extend(elementDict, {
					link: el.select(options.linkSelector)[0].href||false,
					linkTitle: el.select(options.linkSelector)[0].title||false
				});
			
			data[currentArrayPlace] = elementDict;
			currentArrayPlace++;
			if (this.options.destroyAfterPopulate)
				el.remove();
		});
		this.galleryData = data;
//		this.fireEvent('onPopulated');
	},

	constructElements: function() {
		el = this.galleryElement;
		this.maxIter = this.galleryData.length;
		var currentImg;
		for(i=0;i<this.galleryData.length;i++)
		{
			var currentImg = new Element('div');
			currentImg.addClassName('slideElement');
			currentImg.setStyle({
					'position':'absolute',
					'left':'0px',
					'right':'0px',
					'margin':'0px',
					'padding':'0px',
					'backgroundImage':"url('" + this.galleryData[i].image + "')",
					'backgroundPosition':"center center",
					'opacity':'0'
			});
			el.insert(currentImg);
			this.galleryElements[parseInt(i)] = currentImg;
		}
	},
	
	makeLink: function(num) {
		this.currentLink.setAttribute( 'href', this.galleryData[num].link );
		this.currentLink.setAttribute( 'title', this.galleryData[num].linkTitle );
		this.currentLink.setAttribute( 'rel', 'lightbox' );
		if (!((this.options.embedLinks) && (!this.options.showArrows) && (!this.options.showCarousel)))
			this.currentLink.setStyle('display', 'block');
		
		this.currentLink.onclick = function () {myLightbox.start(this); return false;}
	},
	
	clearLink: function() {
		this.currentLink.setAttribute( 'href', '#' );
		this.currentLink.setAttribute( 'title', '' );
		this.currentLink.setAttribute( 'rel', 'lightbox' );
		if (!((this.options.embedLinks) && (!this.options.showArrows) && (!this.options.showCarousel)))
			this.currentLink.setStyle('display', 'none');
	},

	// Set default options, and override with specified options if applicable
	//     I assume Moo does this automatically, maybe prototype does too?  Eh.
	setOptions: function(defaults, options) {
		this.options = defaults;
		for (var option in defaults) {
			if (typeof(options[option]) != 'undefined') {
				this.options[option] = options[option];

			}
		}
		//for (var option in this.options) {
		//  document.write(option+":"+this.options[option]+"<br />");
		//}
	}
});

