/*

 * 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($) {



	// Write noscript style

	document.write("<style type='text/css'>.noscript{display:none}</style>");



	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"><a class="advance-link" rel="history" href="#'+this.data[nextIndex].hash+'" title="'+image.alt+'"></a></span>')

						.find('a')

						.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');

				

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

