/* * 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(""); 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, showImageContainer: true, imageContainerSel: '', captionContainerSel: '', controlsContainerSel: '', loadingContainerSel: '', renderSSControls: true, renderNavControls: true, playLinkText: 'Play', pauseLinkText: 'Pause', prevLinkText: 'Previous', nextLinkText: 'Next', nextPageLinkText: 'Next ›', prevPageLinkText: '‹ 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; $aThumb.attr('title',$li.find('.caption').text()); 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.after(" " + $aThumb.children("img").attr('alt') + "") //$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('') .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('').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('').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(''+this.settings.prevPageLinkText+''); } // Page Index Links for (i=this.currentPage-3; i<=this.currentPage+3; i++) { var pageNum = i+1; if (i == this.currentPage) pager.append(''+pageNum+''); else if (i>=0 && i'+pageNum+''); } } // Next Page Link var nextPage = startIndex+this.settings.numThumbs; if (nextPage < this.data.length) { pager.append(''+this.settings.nextPageLinkText+''); } 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('
'+this.settings.pauseLinkText+'
'); } else { this.$controlsContainer .append('
'+this.settings.playLinkText+'
'); } 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('') .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); /* * */