/** * jQuery Galleriffic plugin * * Copyright (c) 2008 Trent Foley (http://trentacular.com) * Licensed under the MIT License: *   http://www.opensource.org/licenses/mit-license.php * * Much thanks to primary contributer Ponticlaro (http://www.ponticlaro.com) */;(function($) {	// Globally keep track of all images by their unique hash.  Each item is an image data object.	var allImages = {};	var imageCounter = 0;	// Galleriffic static class	$.galleriffic = {		version: '2.0.1',		// Strips invalid characters and any leading # characters		normalizeHash: function(hash) {			return hash.replace(/^.*#/, '').replace(/\?.*$/, '');		},		getImage: function(hash) {			if (!hash)				return undefined;			hash = $.galleriffic.normalizeHash(hash);			return allImages[hash];		},		// Global function that looks up an image by its hash and displays the image.		// Returns false when an image is not found for the specified hash.		// @param {String} hash This is the unique hash value assigned to an image.		gotoImage: function(hash) {			var imageData = $.galleriffic.getImage(hash);			if (!imageData)				return false;			var gallery = imageData.gallery;			gallery.gotoImage(imageData);						return true;		},		// Removes an image from its respective gallery by its hash.		// Returns false when an image is not found for the specified hash or the		// specified owner gallery does match the located images gallery.		// @param {String} hash This is the unique hash value assigned to an image.		// @param {Object} ownerGallery (Optional) When supplied, the located images		// gallery is verified to be the same as the specified owning gallery before		// performing the remove operation.		removeImageByHash: function(hash, ownerGallery) {			var imageData = $.galleriffic.getImage(hash);			if (!imageData)				return false;			var gallery = imageData.gallery;			if (ownerGallery && ownerGallery != gallery)				return false;			return gallery.removeImageByIndex(imageData.index);		}	};	var defaults = {		delay:                     3000,		numThumbs:                 20,		preloadAhead:              40, // Set to -1 to preload all images		enableTopPager:            false,		enableBottomPager:         true,		maxPagesToShow:            7,		imageContainerSel:         '',		captionContainerSel:       '',		controlsContainerSel:      '',		loadingContainerSel:       '',		renderSSControls:          true,		renderNavControls:         true,		playLinkText:              'Play',		pauseLinkText:             'Pause',		prevLinkText:              'Previous',		nextLinkText:              'Next',		nextPageLinkText:          'Next &rsaquo;',		prevPageLinkText:          '&lsaquo; Prev',		enableHistory:             false,		enableKeyboardNavigation:  true,		autoStart:                 false,		syncTransitions:           false,		defaultTransitionDuration: 1000,		onSlideChange:             undefined, // accepts a delegate like such: function(prevIndex, nextIndex) { ... }		onTransitionOut:           undefined, // accepts a delegate like such: function(slide, caption, isSync, callback) { ... }		onTransitionIn:            undefined, // accepts a delegate like such: function(slide, caption, isSync) { ... }		onPageTransitionOut:       undefined, // accepts a delegate like such: function(callback) { ... }		onPageTransitionIn:        undefined, // accepts a delegate like such: function() { ... }		onImageAdded:              undefined, // accepts a delegate like such: function(imageData, $li) { ... }		onImageRemoved:            undefined  // accepts a delegate like such: function(imageData, $li) { ... }	};	// Primary Galleriffic initialization function that should be called on the thumbnail container.	$.fn.galleriffic = function(settings) {		//  Extend Gallery Object		$.extend(this, {			// Returns the version of the script			version: $.galleriffic.version,			// Current state of the slideshow			isSlideshowRunning: false,			slideshowTimeout: undefined,			// This function is attached to the click event of generated hyperlinks within the gallery			clickHandler: function(e, link) {				this.pause();				if (!this.enableHistory) {					// The href attribute holds the unique hash for an image					var hash = $.galleriffic.normalizeHash($(link).attr('href'));					$.galleriffic.gotoImage(hash);					e.preventDefault();				}			},			// Appends an image to the end of the set of images.  Argument listItem can be either a jQuery DOM element or arbitrary html.			// @param listItem Either a jQuery object or a string of html of the list item that is to be added to the gallery.			appendImage: function(listItem) {				this.addImage(listItem, false, false);				return this;			},			// Inserts an image into the set of images.  Argument listItem can be either a jQuery DOM element or arbitrary html.			// @param listItem Either a jQuery object or a string of html of the list item that is to be added to the gallery.			// @param {Integer} position The index within the gallery where the item shouold be added.			insertImage: function(listItem, position) {				this.addImage(listItem, false, true, position);				return this;			},			// Adds an image to the gallery and optionally inserts/appends it to the DOM (thumbExists)			// @param listItem Either a jQuery object or a string of html of the list item that is to be added to the gallery.			// @param {Boolean} thumbExists Specifies whether the thumbnail already exists in the DOM or if it needs to be added.			// @param {Boolean} insert Specifies whether the the image is appended to the end or inserted into the gallery.			// @param {Integer} position The index within the gallery where the item shouold be added.			addImage: function(listItem, thumbExists, insert, position) {				var $li = ( typeof listItem === "string" ) ? $(listItem) : listItem;								var $aThumb = $li.find('a.thumb');				var slideUrl = $aThumb.attr('href');				var title = $aThumb.attr('title');				var $caption = $li.find('.caption').remove();				var hash = $aThumb.attr('name');				// Increment the image counter				imageCounter++;				// Autogenerate a hash value if none is present or if it is a duplicate				if (!hash || allImages[''+hash]) {					hash = imageCounter;				}				// Set position to end when not specified				if (!insert)					position = this.data.length;								var imageData = {					title:title,					slideUrl:slideUrl,					caption:$caption,					hash:hash,					gallery:this,					index:position				};				// Add the imageData to this gallery's array of images				if (insert) {					this.data.splice(position, 0, imageData);					// Reset index value on all imageData objects					this.updateIndices(position);				}				else {					this.data.push(imageData);				}				var gallery = this;				// Add the element to the DOM				if (!thumbExists) {					// Update thumbs passing in addition post transition out handler					this.updateThumbs(function() {						var $thumbsUl = gallery.find('ul.thumbs');						if (insert)							$thumbsUl.children(':eq('+position+')').before($li);						else							$thumbsUl.append($li);												if (gallery.onImageAdded)							gallery.onImageAdded(imageData, $li);					});				}				// Register the image globally				allImages[''+hash] = imageData;				// Setup attributes and click handler				$aThumb.attr('rel', 'history')					.attr('href', '#'+hash)					.removeAttr('name')					.click(function(e) {						gallery.clickHandler(e, this);					});				return this;			},			// Removes an image from the gallery based on its index.			// Returns false when the index is out of range.			removeImageByIndex: function(index) {				if (index < 0 || index >= this.data.length)					return false;								var imageData = this.data[index];				if (!imageData)					return false;								this.removeImage(imageData);								return true;			},			// Convenience method that simply calls the global removeImageByHash method.			removeImageByHash: function(hash) {				return $.galleriffic.removeImageByHash(hash, this);			},			// Removes an image from the gallery.			removeImage: function(imageData) {				var index = imageData.index;								// Remove the image from the gallery data array				this.data.splice(index, 1);								// Remove the global registration				delete allImages[''+imageData.hash];								// Remove the image's list item from the DOM				this.updateThumbs(function() {					var $li = gallery.find('ul.thumbs')						.children(':eq('+index+')')						.remove();					if (gallery.onImageRemoved)						gallery.onImageRemoved(imageData, $li);				});				// Update each image objects index value				this.updateIndices(index);				return this;			},			// Updates the index values of the each of the images in the gallery after the specified index			updateIndices: function(startIndex) {				for (i = startIndex; i < this.data.length; i++) {					this.data[i].index = i;				}								return this;			},			// Scraped the thumbnail container for thumbs and adds each to the gallery			initializeThumbs: function() {				this.data = [];				var gallery = this;				this.find('ul.thumbs > li').each(function(i) {					gallery.addImage($(this), true, false);				});				return this;			},			isPreloadComplete: false,			// Initalizes the image preloader			preloadInit: function() {				if (this.preloadAhead == 0) return this;								this.preloadStartIndex = this.currentImage.index;				var nextIndex = this.getNextIndex(this.preloadStartIndex);				return this.preloadRecursive(this.preloadStartIndex, nextIndex);			},			// Changes the location in the gallery the preloader should work			// @param {Integer} index The index of the image where the preloader should restart at.			preloadRelocate: function(index) {				// By changing this startIndex, the current preload script will restart				this.preloadStartIndex = index;				return this;			},			// Recursive function that performs the image preloading			// @param {Integer} startIndex The index of the first image the current preloader started on.			// @param {Integer} currentIndex The index of the current image to preload.			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.preloadAhead >= 0 && preloadCount > this.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;			},						// Called by preloadRecursive in order to preload the next image after the previous has loaded.			// @param {Integer} startIndex The index of the first image the current preloader started on.			// @param {Integer} currentIndex The index of the current image to preload.			preloadNext: function(startIndex, currentIndex) {				var nextIndex = this.getNextIndex(currentIndex);				if (nextIndex == startIndex) {					this.isPreloadComplete = true;				} else {					// Use setTimeout to free up thread					var gallery = this;					setTimeout(function() { gallery.preloadRecursive(startIndex, nextIndex); }, 100);				}				return this;			},			// Safe way to get the next image index relative to the current image.			// If the current image is the last, returns 0			getNextIndex: function(index) {				var nextIndex = index+1;				if (nextIndex >= this.data.length)					nextIndex = 0;				return nextIndex;			},			// Safe way to get the previous image index relative to the current image.			// If the current image is the first, return the index of the last image in the gallery.			getPrevIndex: function(index) {				var prevIndex = index-1;				if (prevIndex < 0)					prevIndex = this.data.length-1;				return prevIndex;			},			// Pauses the slideshow			pause: function() {				this.isSlideshowRunning = false;				if (this.slideshowTimeout) {					clearTimeout(this.slideshowTimeout);					this.slideshowTimeout = undefined;				}				if (this.$controlsContainer) {					this.$controlsContainer						.find('div.ss-controls a').removeClass().addClass('play')						.attr('title', this.playLinkText)						.attr('href', '#play')						.html(this.playLinkText);				}								return this;			},			// Plays the slideshow			play: function() {				this.isSlideshowRunning = true;				if (this.$controlsContainer) {					this.$controlsContainer						.find('div.ss-controls a').removeClass().addClass('pause')						.attr('title', this.pauseLinkText)						.attr('href', '#pause')						.html(this.pauseLinkText);				}				if (!this.slideshowTimeout) {					var gallery = this;					this.slideshowTimeout = setTimeout(function() { gallery.ssAdvance(); }, this.delay);				}				return this;			},			// Toggles the state of the slideshow (playing/paused)			toggleSlideshow: function() {				if (this.isSlideshowRunning)					this.pause();				else					this.play();				return this;			},			// Advances the slideshow to the next image and delegates navigation to the			// history plugin when history is enabled			// enableHistory is true			ssAdvance: function() {				if (this.isSlideshowRunning)					this.next(true);				return this;			},			// Advances the gallery to the next image.			// @param {Boolean} dontPause Specifies whether to pause the slideshow.			// @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled.  			next: function(dontPause, bypassHistory) {				this.gotoIndex(this.getNextIndex(this.currentImage.index), dontPause, bypassHistory);				return this;			},			// Navigates to the previous image in the gallery.			// @param {Boolean} dontPause Specifies whether to pause the slideshow.			// @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled.			previous: function(dontPause, bypassHistory) {				this.gotoIndex(this.getPrevIndex(this.currentImage.index), dontPause, bypassHistory);				return this;			},			// Navigates to the next page in the gallery.			// @param {Boolean} dontPause Specifies whether to pause the slideshow.			// @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled.			nextPage: function(dontPause, bypassHistory) {				var page = this.getCurrentPage();				var lastPage = this.getNumPages() - 1;				if (page < lastPage) {					var startIndex = page * this.numThumbs;					var nextPage = startIndex + this.numThumbs;					this.gotoIndex(nextPage, dontPause, bypassHistory);				}				return this;			},			// Navigates to the previous page in the gallery.			// @param {Boolean} dontPause Specifies whether to pause the slideshow.			// @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled.			previousPage: function(dontPause, bypassHistory) {				var page = this.getCurrentPage();				if (page > 0) {					var startIndex = page * this.numThumbs;					var prevPage = startIndex - this.numThumbs;									this.gotoIndex(prevPage, dontPause, bypassHistory);				}								return this;			},			// Navigates to the image at the specified index in the gallery			// @param {Integer} index The index of the image in the gallery to display.			// @param {Boolean} dontPause Specifies whether to pause the slideshow.			// @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled.			gotoIndex: function(index, dontPause, bypassHistory) {				if (!dontPause)					this.pause();								if (index < 0) index = 0;				else if (index >= this.data.length) index = this.data.length-1;								var imageData = this.data[index];								if (!bypassHistory && this.enableHistory)					$.historyLoad(String(imageData.hash));  // At the moment, historyLoad only accepts string arguments				else					this.gotoImage(imageData);				return this;			},			// This function is garaunteed to be called anytime a gallery slide changes.			// @param {Object} imageData An object holding the image metadata of the image to navigate to.			gotoImage: function(imageData) {				var index = imageData.index;				if (this.onSlideChange)					this.onSlideChange(this.currentImage.index, index);								this.currentImage = imageData;				this.preloadRelocate(index);								this.refresh();								return this;			},			// Returns the default transition duration value.  The value is halved when not			// performing a synchronized transition.			// @param {Boolean} isSync Specifies whether the transitions are synchronized.			getDefaultTransitionDuration: function(isSync) {				if (isSync)					return this.defaultTransitionDuration;				return this.defaultTransitionDuration / 2;			},			// Rebuilds the slideshow image and controls and performs transitions			refresh: function() {				var imageData = this.currentImage;				if (!imageData)					return this;				var index = imageData.index;				// Update Controls				if (this.$controlsContainer) {					this.$controlsContainer						.find('div.nav-controls a.prev').attr('href', '#'+this.data[this.getPrevIndex(index)].hash).end()						.find('div.nav-controls a.next').attr('href', '#'+this.data[this.getNextIndex(index)].hash);				}				var previousSlide = this.$imageContainer.find('span.current').addClass('previous').removeClass('current');				var previousCaption = 0;				if (this.$captionContainer) {					previousCaption = this.$captionContainer.find('span.current').addClass('previous').removeClass('current');				}				// Perform transitions simultaneously if syncTransitions is true and the next image is already preloaded				var isSync = this.syncTransitions && imageData.image;				// Flag we are transitioning				var isTransitioning = true;				var gallery = this;				var transitionOutCallback = function() {					// Flag that the transition has completed					isTransitioning = false;					// Remove the old slide					previousSlide.remove();					// Remove old caption					if (previousCaption)						previousCaption.remove();					if (!isSync) {						if (imageData.image && imageData.hash == gallery.data[gallery.currentImage.index].hash) {							gallery.buildImage(imageData, isSync);						} else {							// Show loading container							if (gallery.$loadingContainer) {								gallery.$loadingContainer.show();							}						}					}				};				if (previousSlide.length == 0) {					// For the first slide, the previous slide will be empty, so we will call the callback immediately					transitionOutCallback();				} else {					if (this.onTransitionOut) {						this.onTransitionOut(previousSlide, previousCaption, isSync, transitionOutCallback);					} else {						previousSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0, transitionOutCallback);						if (previousCaption)							previousCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0);					}				}				// Go ahead and begin transitioning in of next image				if (isSync)					this.buildImage(imageData, isSync);				if (!imageData.image) {					var image = new Image();										// Wire up mainImage onload event					image.onload = function() {						imageData.image = this;						// Only build image if the out transition has completed and we are still on the same image hash						if (!isTransitioning && imageData.hash == gallery.data[gallery.currentImage.index].hash) {							gallery.buildImage(imageData, isSync);						}					};					// 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();			},			// Called by the refresh method after the previous image has been transitioned out or at the same time			// as the out transition when performing a synchronous transition.			// @param {Object} imageData An object holding the image metadata of the image to build.			// @param {Boolean} isSync Specifies whether the transitions are synchronized.			buildImage: function(imageData, isSync) {				var gallery = this;				var nextIndex = this.getNextIndex(imageData.index);				// Construct new hidden span for the image				var newSlide = this.$imageContainer					.append('<span class="image-wrapper current"><a class="advance-link" rel="history" href="#'+this.data[nextIndex].hash+'" title="'+imageData.title+'">&nbsp;</a></span>')					.find('span.current').css('opacity', '0');								newSlide.find('a')					.append(imageData.image)					.click(function(e) {						gallery.clickHandler(e, this);					});								var newCaption = 0;				if (this.$captionContainer) {					// Construct new hidden caption for the image					newCaption = this.$captionContainer						.append('<span class="image-caption current"></span>')						.find('span.current').css('opacity', '0')						.append(imageData.caption);				}				// Hide the loading conatiner				if (this.$loadingContainer) {					this.$loadingContainer.hide();				}				// Transition in the new image				if (this.onTransitionIn) {					this.onTransitionIn(newSlide, newCaption, isSync);				} else {					newSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0);					if (newCaption)						newCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0);				}								if (this.isSlideshowRunning) {					if (this.slideshowTimeout)						clearTimeout(this.slideshowTimeout);					this.slideshowTimeout = setTimeout(function() { gallery.ssAdvance(); }, this.delay);				}				return this;			},			// Returns the current page index that should be shown for the currentImage			getCurrentPage: function() {				return Math.floor(this.currentImage.index / this.numThumbs);			},			// Applies the selected class to the current image's corresponding thumbnail.			// Also checks if the current page has changed and updates the displayed page of thumbnails if necessary.			syncThumbs: function() {				var page = this.getCurrentPage();				if (page != this.displayedPage)					this.updateThumbs();				// Remove existing selected class and add selected class to new thumb				var $thumbs = this.find('ul.thumbs').children();				$thumbs.filter('.selected').removeClass('selected');				$thumbs.eq(this.currentImage.index).addClass('selected');				return this;			},			// Performs transitions on the thumbnails container and updates the set of			// thumbnails that are to be displayed and the navigation controls.			// @param {Delegate} postTransitionOutHandler An optional delegate that is called after			// the thumbnails container has transitioned out and before the thumbnails are rebuilt.			updateThumbs: function(postTransitionOutHandler) {				var gallery = this;				var transitionOutCallback = function() {					// Call the Post-transition Out Handler					if (postTransitionOutHandler)						postTransitionOutHandler();										gallery.rebuildThumbs();					// Transition In the thumbsContainer					if (gallery.onPageTransitionIn)						gallery.onPageTransitionIn();					else						gallery.show();				};				// Transition Out the thumbsContainer				if (this.onPageTransitionOut) {					this.onPageTransitionOut(transitionOutCallback);				} else {					this.hide();					transitionOutCallback();				}				return this;			},			// Updates the set of thumbnails that are to be displayed and the navigation controls.			rebuildThumbs: function() {				var needsPagination = this.data.length > this.numThumbs;				// Rebuild top pager				if (this.enableTopPager) {					var $topPager = this.find('div.top');					if ($topPager.length == 0)						$topPager = this.prepend('<div class="top pagination"></div>').find('div.top');					else						$topPager.empty();					if (needsPagination)						this.buildPager($topPager);				}				// Rebuild bottom pager				if (this.enableBottomPager) {					var $bottomPager = this.find('div.bottom');					if ($bottomPager.length == 0)						$bottomPager = this.append('<div class="bottom pagination"></div>').find('div.bottom');					else						$bottomPager.empty();					if (needsPagination)						this.buildPager($bottomPager);				}				var page = this.getCurrentPage();				var startIndex = page*this.numThumbs;				var stopIndex = startIndex+this.numThumbs-1;				if (stopIndex >= this.data.length)					stopIndex = this.data.length-1;				// Show/Hide thumbs				var $thumbsUl = this.find('ul.thumbs');				$thumbsUl.find('li').each(function(i) {					var $li = $(this);					if (i >= startIndex && i <= stopIndex) {						$li.show();					} else {						$li.hide();					}				});				this.displayedPage = page;				// Remove the noscript class from the thumbs container ul				$thumbsUl.removeClass('noscript');								return this;			},			// Returns the total number of pages required to display all the thumbnails.			getNumPages: function() {				return Math.ceil(this.data.length/this.numThumbs);			},			// Rebuilds the pager control in the specified matched element.			// @param {jQuery} pager A jQuery element set matching the particular pager to be rebuilt.			buildPager: function(pager) {				var gallery = this;				var numPages = this.getNumPages();				var page = this.getCurrentPage();				var startIndex = page * this.numThumbs;				var pagesRemaining = this.maxPagesToShow - 1;								var pageNum = page - Math.floor((this.maxPagesToShow - 1) / 2) + 1;				if (pageNum > 0) {					var remainingPageCount = numPages - pageNum;					if (remainingPageCount < pagesRemaining) {						pageNum = pageNum - (pagesRemaining - remainingPageCount);					}				}				if (pageNum < 0) {					pageNum = 0;				}				// Prev Page Link				if (page > 0) {					var prevPage = startIndex - this.numThumbs;					pager.append('<a rel="history" href="#'+this.data[prevPage].hash+'" title="'+this.prevPageLinkText+'">'+this.prevPageLinkText+'</a>');				}				// Create First Page link if needed				if (pageNum > 0) {					this.buildPageLink(pager, 0, numPages);					if (pageNum > 1)						pager.append('<span class="ellipsis">&hellip;</span>');										pagesRemaining--;				}				// Page Index Links				while (pagesRemaining > 0) {					this.buildPageLink(pager, pageNum, numPages);					pagesRemaining--;					pageNum++;				}				// Create Last Page link if needed				if (pageNum < numPages) {					var lastPageNum = numPages - 1;					if (pageNum < lastPageNum)						pager.append('<span class="ellipsis">&hellip;</span>');					this.buildPageLink(pager, lastPageNum, numPages);				}				// Next Page Link				var nextPage = startIndex + this.numThumbs;				if (nextPage < this.data.length) {					pager.append('<a rel="history" href="#'+this.data[nextPage].hash+'" title="'+this.nextPageLinkText+'">'+this.nextPageLinkText+'</a>');				}				pager.find('a').click(function(e) {					gallery.clickHandler(e, this);				});				return this;			},			// Builds a single page link within a pager.  This function is called by buildPager			// @param {jQuery} pager A jQuery element set matching the particular pager to be rebuilt.			// @param {Integer} pageNum The page number of the page link to build.			// @param {Integer} numPages The total number of pages required to display all thumbnails.			buildPageLink: function(pager, pageNum, numPages) {				var pageLabel = pageNum + 1;				var currentPage = this.getCurrentPage();				if (pageNum == currentPage)					pager.append('<span class="current">'+pageLabel+'</span>');				else if (pageNum < numPages) {					var imageIndex = pageNum*this.numThumbs;					pager.append('<a rel="history" href="#'+this.data[imageIndex].hash+'" title="'+pageLabel+'">'+pageLabel+'</a>');				}								return this;			}		});		// Now initialize the gallery		$.extend(this, defaults, settings);				// Verify the history plugin is available		if (this.enableHistory && !$.historyInit)			this.enableHistory = false;				// Select containers		if (this.imageContainerSel) this.$imageContainer = $(this.imageContainerSel);		if (this.captionContainerSel) this.$captionContainer = $(this.captionContainerSel);		if (this.loadingContainerSel) this.$loadingContainer = $(this.loadingContainerSel);		// Initialize the thumbails		this.initializeThumbs();				if (this.maxPagesToShow < 3)			this.maxPagesToShow = 3;		this.displayedPage = -1;		this.currentImage = this.data[0];		var gallery = this;		// Hide the loadingContainer		if (this.$loadingContainer)			this.$loadingContainer.hide();		// Setup controls		if (this.controlsContainerSel) {			this.$controlsContainer = $(this.controlsContainerSel).empty();						if (this.renderSSControls) {				if (this.autoStart) {					this.$controlsContainer						.append('<div class="ss-controls"><a href="#pause" class="pause" title="'+this.pauseLinkText+'">'+this.pauseLinkText+'</a></div>');				} else {					this.$controlsContainer						.append('<div class="ss-controls"><a href="#play" class="play" title="'+this.playLinkText+'">'+this.playLinkText+'</a></div>');				}				this.$controlsContainer.find('div.ss-controls a')					.click(function(e) {						gallery.toggleSlideshow();						e.preventDefault();						return false;					});			}					if (this.renderNavControls) {				this.$controlsContainer					.append('<div class="nav-controls"><a class="prev" rel="history" title="'+this.prevLinkText+'">'+this.prevLinkText+'</a><a class="next" rel="history" title="'+this.nextLinkText+'">'+this.nextLinkText+'</a></div>')					.find('div.nav-controls a')					.click(function(e) {						gallery.clickHandler(e, this);					});			}		}		var initFirstImage = !this.enableHistory || !location.hash;		if (this.enableHistory && location.hash) {			var hash = $.galleriffic.normalizeHash(location.hash);			var imageData = allImages[hash];			if (!imageData)				initFirstImage = true;		}		// Setup gallery to show the first image		if (initFirstImage)			this.gotoIndex(0, false, true);		// Setup Keyboard Navigation		if (this.enableKeyboardNavigation) {			$(document).keydown(function(e) {				var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;				switch(key) {					case 32: // space						gallery.next();						e.preventDefault();						break;					case 33: // Page Up						gallery.previousPage();						e.preventDefault();						break;					case 34: // Page Down						gallery.nextPage();						e.preventDefault();						break;					case 35: // End						gallery.gotoIndex(gallery.data.length-1);						e.preventDefault();						break;					case 36: // Home						gallery.gotoIndex(0);						e.preventDefault();						break;					case 37: // left arrow						gallery.previous();						e.preventDefault();						break;					case 39: // right arrow						gallery.next();						e.preventDefault();						break;				}			});		}		// Auto start the slideshow		if (this.autoStart)			this.play();		// Kickoff Image Preloader after 1 second		setTimeout(function() { gallery.preloadInit(); }, 1000);		return this;	};})(jQuery);
