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