/*
    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({
			timed: true,						
			showArrows: true,
			showCarousel: true,
			showInfopane: true,
			thumbHeight: 75,
			thumbWidth: 125,
			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.4,
			carouselMinimizedHeight: 20,
			carouselMaximizedOpacity: 0.9,
			textShowCarousel: 'Pictures',
			showCarouselLabel: true,
			useThumbGenerator: false,
			thumbGenerator: 'resizer.php',
			useExternalCarousel: false,
			carouselElement: false,
			activateCarouselScroller: true,
			/* CSS Classes */			
            baseClass: 'jdGallery',
			withArrowsClass: 'withArrows'
		}, options);
        
//		this.fireEvent('onInit');		
		if(options.textShowCarousel != null) {
			this.options.textShowCarousel=options.textShowCarousel;
		}
        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";
        
		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);
		
		if(this.timed) {
			/*
			new PeriodicalExecuter(function(pe) {			
			if (!confirm('Want me to annoy you again later?'))				
				pe.stop();
			}, 5);
			*/
		}
    },

	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();
		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();
	},

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

	initInfoSlideshow: function() {
		if (this.slideInfoZone)
			this.slideInfoZone.remove();
		this.slideInfoZone = new Element('div').addClassName('slideInfoZone');
        this.galleryElement.insert(this.slideInfoZone);
		var slideInfoZoneTitle = new Element('h3',{'style':'float:left;margin-right:3px;','class':'h3_gallery'});
        this.slideInfoZone.insert(slideInfoZoneTitle);
		var slideInfoZoneDescription = new Element('div',{'class': 'gallery_description'});
        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('h3')[0].innerHTML = this.galleryData[this.currentIter].title;
		element.select('div')[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.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',{'style':'color:#fff;font-weight:normal;'}).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();
				}.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.carouselMinimizedOpacity, to: this.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.carouselMaximizedOpacity, to: this.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
				});
			
			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;
		}
	},

    // 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) {
        for (var option in defaults) {
            if (typeof(options[option]) != 'undefined') {
                this[option] = options[option];
            } else {
                this[option] = defaults[option];
            }
        }
        this.options = defaults;
    }
});

