/*
 * jQuery Galleriffic plugin
 *
 * Copyright (c) 2008 Trent Foley (http://trentacular.com)
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Thanks to Taku Sano (Mikage Sawatari), whose history plugin I adapted to work with Galleriffic
 * Modified by Ghismo (ghismo.com) to disable the location rewrite 
 */
(function($){
	var ver='galleriffic-1.0';
	var galleryOffset=0;
	var galleries=[];
	var allImages=[];	
	var historyCurrentHash;
	var historyBackStack;
	var historyForwardStack;
	var isFirst=false;
	var dontCheck=false;
	var isInitialized=false;
	function getHashFromString(hash){
		if(!hash) return -1;
		hash=hash.replace(/^.*#/,'');
		if(isNaN(hash)) return -1;
		return(+hash);
	}
	function getHash(){
		var hash=location.hash;
		return getHashFromString(hash);
	}
	function registerGallery(gallery){
		galleries.push(gallery);
		// update the global offset value
		galleryOffset += gallery.data.length;
	}
	function getGallery(hash){
		for(i=0;i<galleries.length;i++){
			var gallery=galleries[i];
			if(hash<(gallery.data.length+gallery.offset))
				return gallery;
		}
		return 0;
	}
	function getIndex(gallery,hash){
		return hash-gallery.offset;
	}
	function clickHandler(e,gallery,link){
		gallery.pause();
		if(!gallery.settings.enableHistory){
			var hash=getHashFromString(link.href);
			if(hash>=0){
				var index=getIndex(gallery,hash);
				if(index>=0)
					gallery.goto(index);
			}
			e.preventDefault();
		}
	}
	function historyCallback(){
		// Using present location.hash always (seems to work, unlike the hash argument passed to this callback)
		var hash=getHash();
		if(hash<0) return;
		var gallery=getGallery(hash);
		if(!gallery) return;
		var index=hash-gallery.offset;
		gallery.goto(index);
	}
	function historyInit(){
		if(isInitialized) return;
		isInitialized=true; 
		var current_hash=location.hash; //(enableHistory) ? location.hash : currentIndexHash; // Ghismo
		historyCurrentHash=current_hash;
		if($.browser.msie){
			// To stop the callback firing twice during initilization if no hash present
			if(historyCurrentHash==''){
				historyCurrentHash='#';
			}
		}
		else if($.browser.safari){
			// etablish back/forward stacks
			historyBackStack=[];
			historyBackStack.length=history.length;
			historyForwardStack=[];
			isFirst=true;
		}
		setInterval(function(){historyCheck();},100);
	}
	function historyAddHistory(hash){
		// This makes the looping function do something
		historyBackStack.push(hash);
		historyForwardStack.length=0; // clear forwardStack (true click occured)
		isFirst=true;
	}
	function historyCheck(){
		if($.browser.safari){
			if(!dontCheck){
				var historyDelta=history.length - historyBackStack.length;
				if (historyDelta){ // back or forward button has been pushed
					isFirst=false;
					if(historyDelta<0){ // back button has been pushed
						// move items to forward stack
						for(var i=0;i<Math.abs(historyDelta);i++) historyForwardStack.unshift(historyBackStack.pop());
					}
					else{ // forward button has been pushed
						// move items to back stack
						for (var i=0;i<historyDelta;i++) historyBackStack.push(historyForwardStack.shift());
					}
					var cachedHash=historyBackStack[historyBackStack.length - 1];
					if(cachedHash!=undefined){
						historyCurrentHash=location.hash; // (enableHistory) ? location.hash : currentIndexHash; // Ghismo
						historyCallback();
					}
				}
				else if(historyBackStack[historyBackStack.length - 1]==undefined&&!isFirst){
					historyCallback();
					isFirst=true;
				}
			}
		}
		else {
			// otherwise, check for location.hash
			var current_hash=location.hash; // (enableHistory) ? location.hash : currentIndexHash; // Ghismo
			if(current_hash!=historyCurrentHash){
				historyCurrentHash=current_hash;
				historyCallback();
			}
		}
	}
	var defaults={
		delay:                  3000,
		numThumbs:              20,
		preloadAhead:           40, // Set to -1 to preload all images
		enableTopPager:         false,
		enableBottomPager:      true,
		imageContainerSel:      '',
		captionContainerSel:    '',
		controlsContainerSel:   '',
		loadingContainerSel:    '',
		renderSSControls:       true,
		renderNavControls:      true,
		playLinkText:           'Play',
		pauseLinkText:          'Pause',
		prevLinkText:           'Previous',
		nextLinkText:           'Next',
		nextPageLinkText:       'Next &rsaquo;',
		prevPageLinkText:       '&lsaquo; Prev',
		enableHistory:          false,
		autoStart:              false,
		onChange:               undefined, // accepts a delegate like such: function(prevIndex, nextIndex) { ... }
		onTransitionOut:        undefined, // accepts a delegate like such: function(callback) { ... }
		onTransitionIn:         undefined, // accepts a delegate like such: function() { ... }
		onPageTransitionOut:    undefined, // accepts a delegate like such: function(callback) { ... }
		onPageTransitionIn:     undefined  // accepts a delegate like such: function() { ... }
	};
	$.fn.galleriffic=function(thumbsContainerSel,settings){
		//  Extend Gallery Object
		$.extend(this,{
			ver:function(){
				return ver;
			},
			initializeThumbs:function(){
				this.data=[];
				var gallery=this;
				this.$thumbsContainer.find('ul.thumbs>li').each(function(i){
					var $li=$(this);
					var $aThumb=$li.find('a.thumb');
					var hash=gallery.offset+i;
					gallery.data.push({
						title:$aThumb.attr('title'),
						slideUrl:$aThumb.attr('href'),
						caption:$li.find('.caption').remove(),
						hash:hash
					});
					// Setup history
					$aThumb.attr('rel','history');
					$aThumb.attr('href','#'+hash);
					$aThumb.click(function(e){
						clickHandler(e,gallery,this);
					});
				});
				return this;
			},
			isPreloadComplete:false,
			preloadInit:function(){
				if(this.settings.preloadAhead==0) return this;
				
				this.preloadStartIndex=this.currentIndex;
				var nextIndex=this.getNextIndex(this.preloadStartIndex);
				return this.preloadRecursive(this.preloadStartIndex,nextIndex);
			},
			preloadRelocate:function(index){
				// By changing this startIndex, the current preload script will restart
				this.preloadStartIndex=index;
				return this;
			},
			preloadRecursive:function(startIndex,currentIndex){
				// Check if startIndex has been relocated
				if(startIndex!=this.preloadStartIndex){
					var nextIndex=this.getNextIndex(this.preloadStartIndex);
					return this.preloadRecursive(this.preloadStartIndex,nextIndex);
				}
				var gallery=this;
				// Now check for preloadAhead count
				var preloadCount=currentIndex - startIndex;
				if(preloadCount<0)
					preloadCount=this.data.length-1-startIndex+currentIndex;
				if(this.settings.preloadAhead >= 0 && preloadCount > this.settings.preloadAhead) {
					// Do this in order to keep checking for relocated start index
					setTimeout(function(){gallery.preloadRecursive(startIndex,currentIndex);},500);
					return this;
				}
				var imageData=this.data[currentIndex];
				if(!imageData)
					return this;
				// If already loaded, continue
				if(imageData.image)
					return this.preloadNext(startIndex,currentIndex); 
				// Preload the image
				var image=new Image();
				image.onload=function() {
					imageData.image=this;
					gallery.preloadNext(startIndex,currentIndex);
				};
				image.alt=imageData.title;
				image.src=imageData.slideUrl;

				return this;
			},
			preloadNext:function(startIndex,currentIndex){
				var nextIndex=this.getNextIndex(currentIndex);
				if(nextIndex==startIndex){
					this.isPreloadComplete=true;
				}
				else{
					// Use set timeout to free up thread
					var gallery=this;
					setTimeout(function(){gallery.preloadRecursive(startIndex,nextIndex);},100);
				}
				return this;
			},
			getNextIndex:function(index){
				var nextIndex=index+1;
				if(nextIndex>=this.data.length)
					nextIndex=0;
				return nextIndex;
			},
			getPrevIndex:function(index){
				var prevIndex=index-1;
				if(prevIndex<0)
					prevIndex=this.data.length-1;
				return prevIndex;
			},
			pause:function(){
				if(this.interval)
					this.toggleSlideshow();
				return this;
			},
			play:function(){
				if(!this.interval)
					this.toggleSlideshow();
				return this;
			},
			toggleSlideshow:function(){
				if(this.interval){
					clearInterval(this.interval);
					this.interval=0;
					if(this.$controlsContainer){
						this.$controlsContainer
							.find('div.ss-controls a').removeClass().addClass('play')
							.attr('title',this.settings.playLinkText)
							.attr('href','#play')
							.html(this.settings.playLinkText);
					}
				}
				else{
					this.ssAdvance();
					var gallery=this;
					this.interval=setInterval(function(){gallery.ssAdvance();},this.settings.delay);
					if(this.$controlsContainer){
						this.$controlsContainer
							.find('div.ss-controls a').removeClass().addClass('pause')
							.attr('title', this.settings.pauseLinkText)
							.attr('href', '#pause')
							.html(this.settings.pauseLinkText);
					}
				}
				return this;
			},
			ssAdvance:function(){
				var nextIndex=this.getNextIndex(this.currentIndex);
				var nextHash=this.data[nextIndex].hash;
				// Seems to be working on both FF and Safari
				if(this.settings.enableHistory)
					location.href='#'+nextHash;
				else
					this.goto(nextIndex);
				// IE we need to explicity call goto
				//if ($.browser.msie) {
				//	this.goto(nextIndex);
				//}
				return this;
			},
			goto:function(index){
				if(index<0) index=0;
				else if(index>=this.data.length) index=this.data.length-1;
				if (this.settings.onChange)
					this.settings.onChange(this.currentIndex,index);
				this.currentIndex=index;
				this.preloadRelocate(index);
				return this.refresh();
			},
			refresh:function(){
				var imageData=this.data[this.currentIndex];
				if(!imageData)
					return this;
				// Flag we are transitioning
				var isTransitioning=true;
				var gallery=this;
				var transitionOutCallback=function(){
					// Flag that the transition has completed
					isTransitioning=false;
					// Update Controls
					if(gallery.$controlsContainer){
						gallery.$controlsContainer
							.find('div.nav-controls a.prev').attr('href','#'+gallery.data[gallery.getPrevIndex(gallery.currentIndex)].hash).end()
							.find('div.nav-controls a.next').attr('href','#'+gallery.data[gallery.getNextIndex(gallery.currentIndex)].hash);
					}
					var imageData=gallery.data[gallery.currentIndex];
					// Replace Caption
					if(gallery.$captionContainer){
						gallery.$captionContainer.empty().append(imageData.caption);
					}
					if(imageData.image){
						gallery.buildImage(imageData.image);
					}
					else{
						// Show loading container
						if(gallery.$loadingContainer){
							gallery.$loadingContainer.show();
						}
					}
				}
				if(this.settings.onTransitionOut){
					this.settings.onTransitionOut(transitionOutCallback);
				}
				else{
					this.$transitionContainers.hide();
					transitionOutCallback();
				}
				if(!imageData.image){
					var image=new Image();
					// Wire up mainImage onload event
					image.onload=function(){
						imageData.image=this;
						if(!isTransitioning){
							gallery.buildImage(imageData.image);
						}
					};
					// set alt and src
					image.alt=imageData.title;
					image.src=imageData.slideUrl;
				}
				// This causes the preloader (if still running) to relocate out from the currentIndex
				this.relocatePreload=true;
				return this.syncThumbs();
			},
			buildImage:function(image){
				if(this.$imageContainer){
					this.$imageContainer.empty();
					var gallery=this;
					var nextIndex=this.getNextIndex(this.currentIndex);
					// Hide the loading conatiner
					if(this.$loadingContainer){
						this.$loadingContainer.hide();
					}
					// Setup image
					this.$imageContainer
						.append('<span class="image-wrapper"></span>')
						.find('span')
						.append(image)
						.click(function(e){
							clickHandler(e,gallery,this);
						});
				}
				if(this.settings.onTransitionIn)
					this.settings.onTransitionIn();
				else
					this.$transitionContainers.show();

				return this;
			},
			syncThumbs:function(){
				if(this.$thumbsContainer){
					var page=Math.floor(this.currentIndex / this.settings.numThumbs);
					if(page!=this.currentPage){
						this.currentPage=page;
						this.updateThumbs();
					}
					// Remove existing selected class and add selected class to new thumb
					var $thumbs=this.$thumbsContainer.find('ul.thumbs').children();
					$thumbs.filter('.selected').removeClass('selected');
					$thumbs.eq(this.currentIndex).addClass('selected');
				}
				return this;
			},
			updateThumbs:function(){
				var gallery=this;
				var transitionOutCallback=function(){
					gallery.rebuildThumbs();
					// Transition In the thumbsContainer
					if(gallery.settings.onPageTransitionIn)
						gallery.settings.onPageTransitionIn();
					else
						gallery.$thumbsContainer.show();
				};

				// Transition Out the thumbsContainer
				if(this.settings.onPageTransitionOut){
					this.settings.onPageTransitionOut(transitionOutCallback);
				}
				else{
					this.$thumbsContainer.hide();
					transitionOutCallback();
				}
				return this;
			},
			rebuildThumbs:function(){
				// Initialize currentPage to first page
				if(this.currentPage<0)
					this.currentPage=0;
				var needsPagination=this.data.length>this.settings.numThumbs;
				// Rebuild top pager
				var $topPager=this.$thumbsContainer.find('div.top');
				if($topPager.length==0)
					$topPager=this.$thumbsContainer.prepend('<div class="top pagination"></div>').find('div.top');
				if(needsPagination&&this.settings.enableTopPager){
					$topPager.empty();
					this.buildPager($topPager);
				}
				// Rebuild bottom pager
				if(needsPagination&&this.settings.enableBottomPager){
					var $bottomPager=this.$thumbsContainer.find('div.bottom');
					if($bottomPager.length==0)
						$bottomPager=this.$thumbsContainer.append('<div class="bottom pagination"></div>').find('div.bottom');
					else
						$bottomPager.empty();

					this.buildPager($bottomPager);
				}
				var startIndex=this.currentPage*this.settings.numThumbs;
				var stopIndex=startIndex+this.settings.numThumbs-1;
				if(stopIndex>=this.data.length)
					stopIndex=this.data.length-1;
				// Show/Hide thumbs
				var $thumbsUl=this.$thumbsContainer.find('ul.thumbs');
				$thumbsUl.find('li').each(function(i) {
					var $li=$(this);
					if(i>=startIndex&&i<=stopIndex)
						$li.show();
					else
						$li.hide();
				});
				// Remove the noscript class from the thumbs container ul
				$thumbsUl.removeClass('noscript');
				$('.noscript').css('display','');
				return this;
			},
			buildPager:function(pager){
				var gallery=this;
				var startIndex=this.currentPage*this.settings.numThumbs;
				// Prev Page Link
				if(this.currentPage>0){
					var prevPage=startIndex - this.settings.numThumbs;
					pager.append('<a rel="history" href="#'+this.data[prevPage].hash+'" title="'+this.settings.prevPageLinkText+'">'+this.settings.prevPageLinkText+'</a>');
				}
				// Page Index Links
				for (i=this.currentPage-3; i<=this.currentPage+3; i++) {
					var pageNum=i+1;
					
					if(i==this.currentPage)
						pager.append('<span class="current">'+pageNum+'</span>');
					else if(i>=0&&i<this.numPages){
						var imageIndex=i*this.settings.numThumbs;
						pager.append('<a rel="history" href="#'+this.data[imageIndex].hash+'" title="'+pageNum+'">'+pageNum+'</a>');
					}
				}
				// Next Page Link
				var nextPage=startIndex+this.settings.numThumbs;
				if(nextPage<this.data.length){
					pager.append('<a rel="history" href="#'+this.data[nextPage].hash+'" title="'+this.settings.nextPageLinkText+'">'+this.settings.nextPageLinkText+'</a>');
				}
				pager.find('a').click(function(e){
					clickHandler(e, gallery, this);
				});
				return this;
			}
		});
		// Now initialize the gallery
		this.settings=$.extend({},defaults,settings);
		//enableHistory = this.settings.enableHistory; // Ghismo
		if(this.interval)
			clearInterval(this.interval);
		this.interval=0;
		if(this.settings.imageContainerSel)
			this.$imageContainer=$(this.settings.imageContainerSel);
		if(this.settings.captionContainerSel)
			this.$captionContainer=$(this.settings.captionContainerSel);
		if(this.settings.loadingContainerSel)
			this.$loadingContainer=$(this.settings.loadingContainerSel);
		// Setup the jQuery object holding each container that will be transitioned
		this.$transitionContainers=$([]);
		if(this.$imageContainer)
			this.$transitionContainers=this.$transitionContainers.add(this.$imageContainer);
		if(this.$captionContainer)
			this.$transitionContainers=this.$transitionContainers.add(this.$captionContainer);
		// Set the hash index offset for this gallery
		this.offset=galleryOffset;
		this.$thumbsContainer=$(thumbsContainerSel);
		this.initializeThumbs();
		// Add this gallery to the global galleries array
		registerGallery(this);
		this.numPages=Math.ceil(this.data.length/this.settings.numThumbs);
		this.currentPage=-1;
		this.currentIndex=0;
		var gallery=this;
		// Hide the loadingContainer
		if(this.$loadingContainer)
			this.$loadingContainer.hide();
		// Setup controls
		if(this.settings.controlsContainerSel){
			this.$controlsContainer=$(this.settings.controlsContainerSel).empty();
			if(this.settings.renderSSControls){
				if(this.settings.autoStart){
					this.$controlsContainer
						.append('<div class="ss-controls"><a href="#pause" class="pause" title="'+this.settings.pauseLinkText+'">'+this.settings.pauseLinkText+'</a></div>');
				}
				else{
					this.$controlsContainer
						.append('<div class="ss-controls"><a href="#play" class="play" title="'+this.settings.playLinkText+'">'+this.settings.playLinkText+'</a></div>');
				}
				this.$controlsContainer.find('div.ss-controls a')
					.click(function(e) {
						gallery.toggleSlideshow();
						e.preventDefault();
						return false;
					});
			}
			if(this.settings.renderNavControls){
				var $navControls=this.$controlsContainer
					.append('<div class="nav-controls"><a class="prev" rel="history" title="'+this.settings.prevLinkText+'">'+this.settings.prevLinkText+'</a><a class="next" rel="history" title="'+this.settings.nextLinkText+'">'+this.settings.nextLinkText+'</a></div>')
					.find('div.nav-controls a')
					.click(function(e) {
						clickHandler(e, gallery,this);
					});
			}
		}
		// Initialize history only once when the first gallery on the page is initialized
		historyInit();
		// Build image
		var hash=getHash();
		var hashGallery=(hash>=0)?getGallery(hash):0;
		var gotoIndex=(hashGallery&&this==hashGallery)?(hash-this.offset):0;
		this.goto(gotoIndex);
		if(this.settings.autoStart){
			setTimeout(function(){gallery.play();},this.settings.delay);
		}
		// Kickoff Image Preloader after 1 second
		setTimeout(function(){gallery.preloadInit();},1000);

		return this;
	};
})(jQuery);