/* * jQuery gallerificPlus plugin * * Copyright (c) 2008 Matt Gifford * Licensed under the Creative Commons Attribution-Noncommercial-ShareAlike License * http://creativecommons.org/licenses/by-nc-sa/3.0/ * * Thanks to Trent Foley (Gallerific plugin author - http://www.twospy.com) * and Leandro Vieira Pinho (jQuery Lightbox plugin author - http://leandrovieira.com), whose amazing plugins I adapted and integrated to work together. * I wouldn't have had anything to work with if it wasn't for those guys, so thank you very VERY much. */ ;(function($) { // Write noscript style document.write(""); var ver = 'galleriffic-0.7'; var galleryOffset = 0; var galleries = []; var allImages = []; var historyCurrentHash; var historyBackStack; var historyForwardStack; var isFirst = false; var dontCheck = false; var isInitialized = false; function getHash() { var hash = location.hash; if (!hash) return -1; hash = hash.replace(/^.*#/, ''); if (isNaN(hash)) return -1; return (+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 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; 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; historyCallback(); } } else if (historyBackStack[historyBackStack.length - 1] == undefined && !isFirst) { historyCallback(); isFirst = true; } } } else { // otherwise, check for location.hash var current_hash = location.hash; if(current_hash != historyCurrentHash) { historyCurrentHash = current_hash; historyCallback(); } } } var defaults = { delay: 3000, numThumbs: 20, preloadAhead: 40, // Set to -1 to preload all images enableTopPager: true, enableBottomPager: true, imageContainerSel: '', thumbsContainerSel: '', controlsContainerSel: '', titleContainerSel: '', descContainerSel: '', downloadLinkSel: '', renderSSControls: true, renderNavControls: true, playLinkText: '', pauseLinkText: '', prevLinkText: '', nextLinkText: '', nextPageLinkText: '', prevPageLinkText: '', // Configuration related to overlay overlayBgColor: '#000', // (string) Background color to overlay; inform a hexadecimal value like: #RRGGBB. Where RR, GG, and BB are the hexadecimal values for the red, green, and blue values of the color. overlayOpacity: 0.8, // (integer) Opacity value to overlay; inform: 0.X. Where X are number from 0 to 9 // Configuration related to navigation fixedNavigation: false, // (boolean) Boolean that informs if the navigation (next and prev button) will be fixed or not in the interface. // Configuration related to images imageLoading: 'img/bp/lightbox-ico-loading.gif', // (string) Path and the name of the loading icon imageBtnPrev: 'img/bp/lightbox-btn-prev.gif', // (string) Path and the name of the prev button image imageBtnNext: 'img/bp/lightbox-btn-next.gif', // (string) Path and the name of the next button image imageBtnClose: 'img/bp/lightbox-btn-close.gif', // (string) Path and the name of the close btn imageBlank: 'img/bp/lightbox-blank.gif', // (string) Path and the name of a blank image (one pixel) // Configuration related to container image box containerBorderSize: 10, // (integer) If you adjust the padding in the CSS for the container, #lightbox-container-image-box, you will need to update this value containerResizeSpeed: 400, // (integer) Specify the resize duration of container image. These number are miliseconds. 400 is default. // Configuration related to texts in caption. For example: Image 2 of 8. You can alter either "Image" and "of" texts. txtImage: 'Slide', // (string) Specify text "Image" txtOf: 'of', // (string) Specify text "of" // Configuration related to keyboard navigation keyToClose: 'c', // (string) (c = close) Letter to close the jQuery lightBox interface. Beyond this letter, the letter X and the SCAPE key is used to. keyToPrev: 'p', // (string) (p = previous) Letter to show the previous image keyToNext: 'n', // (string) (n = next) Letter to show the next image. // Don´t alter these variables in any way imageArray: [], activeImage: 0 }; // lightbox specific functions function buildLightBox(image,gallery,current) { gallery.pause(); // Hide some elements to avoid conflict with overlay in IE. These elements appear above the overlay. $('embed, object, select').css({ 'visibility' : 'hidden' }); // Call the function to create the markup structure; style some elements; assign events in some elements. _set_interface(); // Unset total images in imageArray defaults.imageArray.length = 0; // Unset image active information defaults.activeImage = 0; if ( gallery.data.length == 1 ) { defaults.imageArray.push(new Array(gallery.data[current].original,gallery.data[current].title)); } else { // Add an Array (as many as we have), with href and title atributes, inside the Array that storage the images references for ( var i = 0; i < gallery.data.length; i++ ) { defaults.imageArray.push(new Array(gallery.data[i].original,gallery.data[i].title)); } } while ( defaults.imageArray[defaults.activeImage][0] != gallery.data[current].original ) { defaults.activeImage++; } // Call the function that prepares image exibition _set_image_to_view(); } function _set_interface() { // Apply the HTML markup into body tag $('body').append('
'); // Get page sizes var arrPageSizes = ___getPageSize(); // Style overlay and show it $('#jquery-overlay').css({ backgroundColor: defaults.overlayBgColor, opacity: defaults.overlayOpacity, width: arrPageSizes[0], height: arrPageSizes[1] }).fadeIn(); // Get page scroll var arrPageScroll = ___getPageScroll(); // Calculate top and left offset for the jquery-lightbox div object and show it $('#jquery-lightbox').css({ top: arrPageScroll[1] + (arrPageSizes[3] / 10), left: arrPageScroll[0] }).show(); // Assigning click events in elements to close overlay $('#jquery-overlay,#jquery-lightbox').click(function() { _finish(); }); // Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects $('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function() { _finish(); return false; }); // If window was resized, calculate the new overlay dimensions $(window).resize(function() { // Get page sizes var arrPageSizes = ___getPageSize(); // Style overlay and show it $('#jquery-overlay').css({ width: arrPageSizes[0], height: arrPageSizes[1] }); // Get page scroll var arrPageScroll = ___getPageScroll(); // Calculate top and left offset for the jquery-lightbox div object and show it $('#jquery-lightbox').css({ top: arrPageScroll[1] + (arrPageSizes[3] / 10), left: arrPageScroll[0] }); }); } /** * Prepares image exibition; doing a image´s preloader to calculate it´s size * */ function _set_image_to_view() { // show the loading // Show the loading $('#lightbox-loading').show(); if ( defaults.fixedNavigation ) { $('#lightbox-image,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber').hide(); } else { // Hide some elements $('#lightbox-image,#lightbox-nav,#lightbox-nav-btnPrev,#lightbox-nav-btnNext,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber').hide(); } // Image preload process var objImagePreloader = new Image(); objImagePreloader.onload = function() { $('#lightbox-image').attr('src',defaults.imageArray[defaults.activeImage][0]); // Perfomance an effect in the image container resizing it _resize_container_image_box(objImagePreloader.width,objImagePreloader.height); // clear onLoad, IE behaves irratically with animated gifs otherwise objImagePreloader.onload=function(){}; }; objImagePreloader.src = defaults.imageArray[defaults.activeImage][0]; }; /** * Perfomance an effect in the image container resizing it * * @param integer intImageWidth The image´s width that will be showed * @param integer intImageHeight The image´s height that will be showed */ function _resize_container_image_box(intImageWidth,intImageHeight) { // Get current width and height var intCurrentWidth = $('#lightbox-container-image-box').width(); var intCurrentHeight = $('#lightbox-container-image-box').height(); // Get the width and height of the selected image plus the padding var intWidth = (intImageWidth + (defaults.containerBorderSize * 2)); // Plus the image´s width and the left and right padding value var intHeight = (intImageHeight + (defaults.containerBorderSize * 2)); // Plus the image´s height and the left and right padding value // Diferences var intDiffW = intCurrentWidth - intWidth; var intDiffH = intCurrentHeight - intHeight; // Perfomance the effect $('#lightbox-container-image-box').animate({ width: intWidth, height: intHeight },defaults.containerResizeSpeed,function() { _show_image(); }); if ( ( intDiffW == 0 ) && ( intDiffH == 0 ) ) { if ( $.browser.msie ) { ___pause(250); } else { ___pause(100); } } $('#lightbox-container-image-data-box').css({ width: intImageWidth }); $('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ height: intImageHeight + (defaults.containerBorderSize * 2) }); }; /** * Show the prepared image * */ function _show_image() { $('#lightbox-loading').hide(); $('#lightbox-image').fadeIn(function() { _show_image_data(); _set_navigation(); }); _preload_neighbor_images(); }; /** * Show the image information * */ function _show_image_data() { $('#lightbox-container-image-data-box').slideDown('fast'); $('#lightbox-image-details-caption').hide(); if ( defaults.imageArray[defaults.activeImage][1] ) { $('#lightbox-image-details-caption').html(defaults.imageArray[defaults.activeImage][1]).show(); } // If we have a image set, display 'Image X of X' if ( defaults.imageArray.length > 1 ) { $('#lightbox-image-details-currentNumber').html(defaults.txtImage + ' ' + ( defaults.activeImage + 1 ) + ' ' + defaults.txtOf + ' ' + defaults.imageArray.length).show(); } } // handles the reloaction of the URL hash variable when the previous/next buttons are clicked within the lightbox UI. function changeThumbnail(activeImg) { location.href = 'http://www.jungleminds.com/publications/best_practices/put_your_love_to_the_test#'+activeImg; // IE we need to explicity call goto /*if ($.browser.msie) { this.goto(activeImg); }*/ } /** * Display the button navigations * */ function _set_navigation() { $('#lightbox-nav').show(); // Instead to define this configuration in CSS file, we define here. And it´s need to IE. Just. $('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ 'background' : 'transparent url(' + defaults.imageBlank + ') no-repeat' }); // Show the prev button, if not the first image in set if ( defaults.activeImage != 0 ) { if ( defaults.fixedNavigation ) { $('#lightbox-nav-btnPrev').css({ 'background' : 'url(' + defaults.imageBtnPrev + ') left 15% no-repeat' }) .unbind() .bind('click',function() { defaults.activeImage = defaults.activeImage - 1; changeThumbnail(); _set_image_to_view(); return false; }); } else { // Show the images button for Next buttons $('#lightbox-nav-btnPrev').unbind().hover(function() { $(this).css({ 'background' : 'url(' + defaults.imageBtnPrev + ') left 15% no-repeat' }); },function() { $(this).css({ 'background' : 'transparent url(' + defaults.imageBlank + ') no-repeat' }); }).show().bind('click',function() { defaults.activeImage = defaults.activeImage - 1; changeThumbnail(defaults.activeImage); _set_image_to_view(); return false; }); } } // Show the next button, if not the last image in set if ( defaults.activeImage != ( defaults.imageArray.length -1 ) ) { if ( defaults.fixedNavigation ) { $('#lightbox-nav-btnNext').css({ 'background' : 'url(' + defaults.imageBtnNext + ') right 15% no-repeat' }) .unbind() .bind('click',function() { defaults.activeImage = defaults.activeImage + 1; changeThumbnail(defaults.activeImage); _set_image_to_view(); return false; }); } else { // Show the images button for Next buttons $('#lightbox-nav-btnNext').unbind().hover(function() { $(this).css({ 'background' : 'url(' + defaults.imageBtnNext + ') right 15% no-repeat' }); },function() { $(this).css({ 'background' : 'transparent url(' + defaults.imageBlank + ') no-repeat' }); }).show().bind('click',function() { defaults.activeImage = defaults.activeImage + 1; changeThumbnail(defaults.activeImage); _set_image_to_view(); return false; }); } } // Enable keyboard navigation _enable_keyboard_navigation(); } /** * Enable a support to keyboard navigation * */ function _enable_keyboard_navigation() { $(document).keydown(function(objEvent) { _keyboard_action(objEvent); }); } /** * Disable the support to keyboard navigation * */ function _disable_keyboard_navigation() { $(document).unbind(); } /** * Perform the keyboard actions * */ function _keyboard_action(objEvent) { // To ie if ( objEvent == null ) { keycode = event.keyCode; escapeKey = 27; // To Mozilla } else { keycode = objEvent.keyCode; escapeKey = objEvent.DOM_VK_ESCAPE; } // Get the key in lower case form key = String.fromCharCode(keycode).toLowerCase(); // Verify the keys to close the ligthBox if ( ( key == defaults.keyToClose ) || ( key == 'x' ) || ( keycode == escapeKey ) ) { _finish(); } // Verify the key to show the previous image if ( ( key == defaults.keyToPrev ) || ( keycode == 37 ) ) { // If we´re not showing the first image, call the previous if ( defaults.activeImage != 0 ) { defaults.activeImage = defaults.activeImage - 1; changeThumbnail(defaults.activeImage); _set_image_to_view(); _disable_keyboard_navigation(); } } // Verify the key to show the next image if ( ( key == defaults.keyToNext ) || ( keycode == 39 ) ) { // If we´re not showing the last image, call the next if ( defaults.activeImage != ( defaults.imageArray.length - 1 ) ) { defaults.activeImage = defaults.activeImage + 1; changeThumbnail(defaults.activeImage); _set_image_to_view(); _disable_keyboard_navigation(); } } } /** * Preload prev and next images being showed * */ function _preload_neighbor_images() { if ( (defaults.imageArray.length -1) > defaults.activeImage ) { objNext = new Image(); objNext.src = defaults.imageArray[defaults.activeImage + 1][0]; } if ( defaults.activeImage > 0 ) { objPrev = new Image(); objPrev.src = defaults.imageArray[defaults.activeImage -1][0]; } } /** * Remove jQuery lightBox plugin HTML markup * */ function _finish() { $('#jquery-lightbox').remove(); $('#jquery-overlay').fadeOut(function() { $('#jquery-overlay').remove(); }); // Show some elements to avoid conflict with overlay in IE. These elements appear above the overlay. $('embed, object, select').css({ 'visibility' : 'visible' }); } /** / THIRD FUNCTION * getPageSize() by quirksmode.com * * @return Array Return an array with page width, height and window width, height */ function ___getPageSize() { var xScroll, yScroll; if (window.innerHeight && window.scrollMaxY) { xScroll = window.innerWidth + window.scrollMaxX; yScroll = window.innerHeight + window.scrollMaxY; } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac xScroll = document.body.scrollWidth; yScroll = document.body.scrollHeight; } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari xScroll = document.body.offsetWidth; yScroll = document.body.offsetHeight; } var windowWidth, windowHeight; if (self.innerHeight) { // all except Explorer if(document.documentElement.clientWidth){ windowWidth = document.documentElement.clientWidth; } else { windowWidth = self.innerWidth; } windowHeight = self.innerHeight; } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode windowWidth = document.documentElement.clientWidth; windowHeight = document.documentElement.clientHeight; } else if (document.body) { // other Explorers windowWidth = document.body.clientWidth; windowHeight = document.body.clientHeight; } // for small pages with total height less then height of the viewport if(yScroll < windowHeight){ pageHeight = windowHeight; } else { pageHeight = yScroll; } // for small pages with total width less then width of the viewport if(xScroll < windowWidth){ pageWidth = xScroll; } else { pageWidth = windowWidth; } arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight); return arrayPageSize; }; /** / THIRD FUNCTION * getPageScroll() by quirksmode.com * * @return Array Return an array with x,y page scroll values. */ function ___getPageScroll() { var xScroll, yScroll; if (self.pageYOffset) { yScroll = self.pageYOffset; xScroll = self.pageXOffset; } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict yScroll = document.documentElement.scrollTop; xScroll = document.documentElement.scrollLeft; } else if (document.body) {// all other Explorers yScroll = document.body.scrollTop; xScroll = document.body.scrollLeft; } arrayPageScroll = new Array(xScroll,yScroll); return arrayPageScroll; }; /** * Stop the code execution from a escified time in milisecond * */ function ___pause(ms) { var date = new Date(); curDate = null; do { var curDate = new Date(); } while ( curDate - date < ms); }; // end of lightbox functions function clickHandler(gallery) { gallery.pause(); return false; } $.fn.galleriffic = function(thumbsContainerSel, settings) { // Extend Gallery Object $.extend(this, { ver: function() { return ver; }, buildDataFromThumbs: function() { this.data = []; var gallery = this; this.$thumbsContainer.find('li').each(function(i) { var $a = $(this).find('a'); var $img = $a.find('img:first'); gallery.data.push({slide:$a.attr('href'),thumb:$img.attr('src'),original:$a.attr('original'),title:$a.attr('title'),description:$a.attr('description'),hash:gallery.offset+i}); }); 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; // 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.slide; 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 span').removeClass().addClass('play') .attr('title', this.settings.playLinkText) .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 span').removeClass().addClass('pause') .attr('title', this.settings.pauseLinkText) .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 location.href = 'http://www.jungleminds.com/publications/best_practices/put_your_love_to_the_test#'+nextHash; // 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; this.currentIndex = index; this.preloadRelocate(index); return this.refresh(); }, refresh: function() { if (this.$imageContainer) { var imageData = this.data[this.currentIndex]; var isFading = 1; var gallery = this; if (! imageData) return; // hide img this.$imageContainer.fadeOut('fast', function() { isFading = 0; // Update Controls if (gallery.$controlsContainer) { gallery.$controlsContainer .find('div.nav-controls a.prev').attr('href', 'http://www.jungleminds.com/publications/best_practices/put_your_love_to_the_test#'+gallery.data[gallery.getPrevIndex(gallery.currentIndex)].hash).end() .find('div.nav-controls a.next').attr('href', 'http://www.jungleminds.com/publications/best_practices/put_your_love_to_the_test#'+gallery.data[gallery.getNextIndex(gallery.currentIndex)].hash); } // Replace Title if (gallery.$titleContainer) { gallery.$titleContainer.empty().html(imageData.title); } // Replace Description if (gallery.$descContainer) { gallery.$descContainer.empty().html(imageData.description); } // Update Download Link if (gallery.$downloadLink) { gallery.$downloadLink.attr('href', imageData.original); } if (imageData.$image) { gallery.buildImage(imageData.$image); } }); if (this.onFadeOut) this.onFadeOut(); if (!imageData.$image) { var image = new Image(); // Wire up mainImage onload event image.onload = function() { imageData.$image = this; if (!isFading) { gallery.buildImage(imageData.$image); } }; // set alt and src image.alt = imageData.title; image.src = imageData.slide; } // 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 thisImageIndex = this.currentIndex; // Setup image this.$imageContainer //.append('') .append('') .find('a') .append(image) //.click(function() { clickHandler(gallery); }) .click(function() { buildLightBox(image,gallery,thisImageIndex);}) .end() .fadeIn('fast'); if (this.onFadeIn) this.onFadeIn(); } 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(); } else { var selectedThumb = this.currentIndex % this.settings.numThumbs; // Remove existing selected class and add selected class to new thumb this.$thumbsContainer .find('ul.thumbs li.selected') .removeClass('selected') .end() //.find('ul.thumbs a[href="#'+this.currentIndex+'"]') .find('ul.thumbs li').eq(selectedThumb) .addClass('selected'); } } return this; }, updateThumbs: function() { if (this.$thumbsContainer) { // Initialize currentPage to first page if (this.currentPage < 0) this.currentPage = 0; var startIndex = this.currentPage*this.settings.numThumbs; var stopIndex = startIndex+this.settings.numThumbs-1; if (stopIndex >= this.data.length) stopIndex = this.data.length-1; var needsPagination = this.data.length > this.settings.numThumbs; // Clear thumbs container this.$thumbsContainer.empty(); // Rebuild top pager this.$thumbsContainer.append(''); if (needsPagination && this.settings.enableTopPager) { this.buildPager(this.$thumbsContainer.find('div.top')); } // Rebuild thumbs var $ulThumbs = this.$thumbsContainer.append('