1/** 2 * jQuery Galleriffic plugin 3 * 4 * Copyright (c) 2008 Trent Foley (http://trentacular.com) 5 * Licensed under the MIT License: 6 * http://www.opensource.org/licenses/mit-license.php 7 * 8 * Much thanks to primary contributer Ponticlaro (http://www.ponticlaro.com) 9 */ 10;(function($) { 11 // Globally keep track of all images by their unique hash. Each item is an image data object. 12 var allImages = {}; 13 var imageCounter = 0; 14 15 // Galleriffic static class 16 $.galleriffic = { 17 version: '2.0.1', 18 19 // Strips invalid characters and any leading # characters 20 normalizeHash: function(hash) { 21 return hash.replace(/^.*#/, '').replace(/\?.*$/, ''); 22 }, 23 24 getImage: function(hash) { 25 if (!hash) 26 return undefined; 27 28 hash = $.galleriffic.normalizeHash(hash); 29 return allImages[hash]; 30 }, 31 32 // Global function that looks up an image by its hash and displays the image. 33 // Returns false when an image is not found for the specified hash. 34 // @param {String} hash This is the unique hash value assigned to an image. 35 gotoImage: function(hash) { 36 var imageData = $.galleriffic.getImage(hash); 37 if (!imageData) 38 return false; 39 40 var gallery = imageData.gallery; 41 gallery.gotoImage(imageData); 42 43 return true; 44 }, 45 46 // Removes an image from its respective gallery by its hash. 47 // Returns false when an image is not found for the specified hash or the 48 // specified owner gallery does match the located images gallery. 49 // @param {String} hash This is the unique hash value assigned to an image. 50 // @param {Object} ownerGallery (Optional) When supplied, the located images 51 // gallery is verified to be the same as the specified owning gallery before 52 // performing the remove operation. 53 removeImageByHash: function(hash, ownerGallery) { 54 var imageData = $.galleriffic.getImage(hash); 55 if (!imageData) 56 return false; 57 58 var gallery = imageData.gallery; 59 if (ownerGallery && ownerGallery != gallery) 60 return false; 61 62 return gallery.removeImageByIndex(imageData.index); 63 } 64 }; 65 66 var defaults = { 67 delay: 3000, 68 numThumbs: 20, 69 preloadAhead: 40, // Set to -1 to preload all images 70 enableTopPager: false, 71 enableBottomPager: true, 72 maxPagesToShow: 7, 73 imageContainerSel: '', 74 captionContainerSel: '', 75 controlsContainerSel: '', 76 loadingContainerSel: '', 77 renderSSControls: true, 78 renderNavControls: true, 79 playLinkText: 'Play', 80 pauseLinkText: 'Pause', 81 prevLinkText: 'Previous', 82 nextLinkText: 'Next', 83 nextPageLinkText: 'Next ›', 84 prevPageLinkText: '‹ Prev', 85 enableHistory: false, 86 enableKeyboardNavigation: true, 87 autoStart: false, 88 syncTransitions: false, 89 defaultTransitionDuration: 1000, 90 onSlideChange: undefined, // accepts a delegate like such: function(prevIndex, nextIndex) { ... } 91 onTransitionOut: undefined, // accepts a delegate like such: function(slide, caption, isSync, callback) { ... } 92 onTransitionIn: undefined, // accepts a delegate like such: function(slide, caption, isSync) { ... } 93 onPageTransitionOut: undefined, // accepts a delegate like such: function(callback) { ... } 94 onPageTransitionIn: undefined, // accepts a delegate like such: function() { ... } 95 onImageAdded: undefined, // accepts a delegate like such: function(imageData, $li) { ... } 96 onImageRemoved: undefined // accepts a delegate like such: function(imageData, $li) { ... } 97 }; 98 99 // Primary Galleriffic initialization function that should be called on the thumbnail container. 100 $.fn.galleriffic = function(settings) { 101 // Extend Gallery Object 102 $.extend(this, { 103 // Returns the version of the script 104 version: $.galleriffic.version, 105 106 // Current state of the slideshow 107 isSlideshowRunning: false, 108 slideshowTimeout: undefined, 109 110 // This function is attached to the click event of generated hyperlinks within the gallery 111 clickHandler: function(e, link) { 112 this.pause(); 113 114 if (!this.enableHistory) { 115 // The href attribute holds the unique hash for an image 116 var hash = $.galleriffic.normalizeHash($(link).attr('href')); 117 $.galleriffic.gotoImage(hash); 118 e.preventDefault(); 119 } 120 }, 121 122 // Appends an image to the end of the set of images. Argument listItem can be either a jQuery DOM element or arbitrary html. 123 // @param listItem Either a jQuery object or a string of html of the list item that is to be added to the gallery. 124 appendImage: function(listItem) { 125 this.addImage(listItem, false, false); 126 return this; 127 }, 128 129 // Inserts an image into the set of images. Argument listItem can be either a jQuery DOM element or arbitrary html. 130 // @param listItem Either a jQuery object or a string of html of the list item that is to be added to the gallery. 131 // @param {Integer} position The index within the gallery where the item shouold be added. 132 insertImage: function(listItem, position) { 133 this.addImage(listItem, false, true, position); 134 return this; 135 }, 136 137 // Adds an image to the gallery and optionally inserts/appends it to the DOM (thumbExists) 138 // @param listItem Either a jQuery object or a string of html of the list item that is to be added to the gallery. 139 // @param {Boolean} thumbExists Specifies whether the thumbnail already exists in the DOM or if it needs to be added. 140 // @param {Boolean} insert Specifies whether the the image is appended to the end or inserted into the gallery. 141 // @param {Integer} position The index within the gallery where the item shouold be added. 142 addImage: function(listItem, thumbExists, insert, position) { 143 var $li = ( typeof listItem === "string" ) ? $(listItem) : listItem; 144 var $aThumb = $li.find('a.thumb'); 145 var slideUrl = $aThumb.attr('href'); 146 var title = $aThumb.attr('title'); 147 var $caption = $li.find('.caption').remove(); 148 var hash = $aThumb.attr('name'); 149 150 // Increment the image counter 151 imageCounter++; 152 153 // Autogenerate a hash value if none is present or if it is a duplicate 154 if (!hash || allImages[''+hash]) { 155 hash = imageCounter; 156 } 157 158 // Set position to end when not specified 159 if (!insert) 160 position = this.data.length; 161 162 var imageData = { 163 title:title, 164 slideUrl:slideUrl, 165 caption:$caption, 166 hash:hash, 167 gallery:this, 168 index:position 169 }; 170 171 // Add the imageData to this gallery's array of images 172 if (insert) { 173 this.data.splice(position, 0, imageData); 174 175 // Reset index value on all imageData objects 176 this.updateIndices(position); 177 } 178 else { 179 this.data.push(imageData); 180 } 181 182 var gallery = this; 183 184 // Add the element to the DOM 185 if (!thumbExists) { 186 // Update thumbs passing in addition post transition out handler 187 this.updateThumbs(function() { 188 var $thumbsUl = gallery.find('ul.thumbs'); 189 if (insert) 190 $thumbsUl.children(':eq('+position+')').before($li); 191 else 192 $thumbsUl.append($li); 193 194 if (gallery.onImageAdded) 195 gallery.onImageAdded(imageData, $li); 196 }); 197 } 198 199 // Register the image globally 200 allImages[''+hash] = imageData; 201 202 // Setup attributes and click handler 203 $aThumb.attr('rel', 'history') 204 .attr('href', '#'+hash) 205 .removeAttr('name') 206 .click(function(e) { 207 gallery.clickHandler(e, this); 208 }); 209 210 return this; 211 }, 212 213 // Removes an image from the gallery based on its index. 214 // Returns false when the index is out of range. 215 removeImageByIndex: function(index) { 216 if (index < 0 || index >= this.data.length) 217 return false;
218 219 var imageData = this.data[index]; 220 if (!imageData) 221 return false; 222 223 this.removeImage(imageData); 224 225 return true; 226 }, 227 228 // Convenience method that simply calls the global removeImageByHash method. 229 removeImageByHash: function(hash) { 230 return $.galleriffic.removeImageByHash(hash, this); 231 }, 232 233 // Removes an image from the gallery. 234 removeImage: function(imageData) { 235 var index = imageData.index; 236 237 // Remove the image from the gallery data array 238 this.data.splice(index, 1); 239 240 // Remove the global registration 241 delete allImages[''+imageData.hash]; 242 243 // Remove the image's list item from the DOM 244 this.updateThumbs(function() { 245 var $li = gallery.find('ul.thumbs') 246 .children(':eq('+index+')') 247 .remove(); 248 249 if (gallery.onImageRemoved) 250 gallery.onImageRemoved(imageData, $li); 251 }); 252 253 // Update each image objects index value 254 this.updateIndices(index); 255 256 return this; 257 }, 258 259 // Updates the index values of the each of the images in the gallery after the specified index 260 updateIndices: function(startIndex) { 261 for (i = startIndex; i < this.data.length; i++) { 262 this.data[i].index = i; 263 } 264 265 return this; 266 }, 267 268 // Scraped the thumbnail container for thumbs and adds each to the gallery 269 initializeThumbs: function() { 270 this.data = []; 271 var gallery = this; 272 273 this.find('ul.thumbs > li').each(function(i) { 274 gallery.addImage($(this), true, false); 275 }); 276 277 return this; 278 }, 279 280 isPreloadComplete: false, 281 282 // Initalizes the image preloader 283 preloadInit: function() { 284 if (this.preloadAhead == 0) return this; 285 286 this.preloadStartIndex = this.currentImage.index; 287 var nextIndex = this.getNextIndex(this.preloadStartIndex); 288 return this.preloadRecursive(this.preloadStartIndex, nextIndex); 289 }, 290 291 // Changes the location in the gallery the preloader should work 292 // @param {Integer} index The index of the image where the preloader should restart at. 293 preloadRelocate: function(index) { 294 // By changing this startIndex, the current preload script will restart 295 this.preloadStartIndex = index; 296 return this; 297 }, 298 299 // Recursive function that performs the image preloading 300 // @param {Integer} startIndex The index of the first image the current preloader started on. 301 // @param {Integer} currentIndex The index of the current image to preload. 302 preloadRecursive: function(startIndex, currentIndex) { 303 // Check if startIndex has been relocated 304 if (startIndex != this.preloadStartIndex) { 305 var nextIndex = this.getNextIndex(this.preloadStartIndex); 306 return this.preloadRecursive(this.preloadStartIndex, nextIndex); 307 } 308 309 var gallery = this; 310 311 // Now check for preloadAhead count 312 var preloadCount = currentIndex - startIndex; 313 if (preloadCount < 0) 314 preloadCount = this.data.length-1-startIndex+currentIndex; 315 if (this.preloadAhead >= 0 && preloadCount > this.preloadAhead) { 316 // Do this in order to keep checking for relocated start index 317 setTimeout(function() { gallery.preloadRecursive(startIndex, currentIndex); }, 500); 318 return this; 319 } 320 321 var imageData = this.data[currentIndex]; 322 if (!imageData) 323 return this; 324 325 // If already loaded, continue 326 if (imageData.image) 327 return this.preloadNext(startIndex, currentIndex); 328 329 // Preload the image 330 var image = new Image(); 331 332 image.onload = function() { 333 imageData.image = this; 334 gallery.preloadNext(startIndex, currentIndex); 335 }; 336 337 image.alt = imageData.title; 338 image.src = imageData.slideUrl; 339 340 return this; 341 }, 342 343 // Called by preloadRecursive in order to preload the next image after the previous has loaded. 344 // @param {Integer} startIndex The index of the first image the current preloader started on. 345 // @param {Integer} currentIndex The index of the current image to preload. 346 preloadNext: function(startIndex, currentIndex) { 347 var nextIndex = this.getNextIndex(currentIndex); 348 if (nextIndex == startIndex) { 349 this.isPreloadComplete = true; 350 } else { 351 // Use setTimeout to free up thread 352 var gallery = this; 353 setTimeout(function() { gallery.preloadRecursive(startIndex, nextIndex); }, 100); 354 } 355 356 return this; 357 }, 358 359 // Safe way to get the next image index relative to the current image. 360 // If the current image is the last, returns 0 361 getNextIndex: function(index) { 362 var nextIndex = index+1; 363 if (nextIndex >= this.data.length) 364 nextIndex = 0; 365 return nextIndex; 366 }, 367 368 // Safe way to get the previous image index relative to the current image. 369 // If the current image is the first, return the index of the last image in the gallery. 370 getPrevIndex: function(index) { 371 var prevIndex = index-1; 372 if (prevIndex < 0) 373 prevIndex = this.data.length-1; 374 return prevIndex; 375 }, 376 377 // Pauses the slideshow 378 pause: function() { 379 this.isSlideshowRunning = false;
380 if (this.slideshowTimeout) { 381 clearTimeout(this.slideshowTimeout); 382 this.slideshowTimeout = undefined; 383 } 384 385 if (this.$controlsContainer) { 386 this.$controlsContainer 387 .find('div.ss-controls a').removeClass().addClass('play') 388 .attr('title', this.playLinkText) 389 .attr('href', '#play') 390 .html(this.playLinkText); 391 } 392 393 return this; 394 }, 395 396 // Plays the slideshow 397 play: function() { 398 this.isSlideshowRunning = true; 399 400 if (this.$controlsContainer) { 401 this.$controlsContainer 402 .find('div.ss-controls a').removeClass().addClass('pause') 403 .attr('title', this.pauseLinkText) 404 .attr('href', '#pause') 405 .html(this.pauseLinkText); 406 } 407 408 if (!this.slideshowTimeout) { 409 var gallery = this; 410 this.slideshowTimeout = setTimeout(function() { gallery.ssAdvance(); }, this.delay); 411 } 412 413 return this; 414 }, 415 416 // Toggles the state of the slideshow (playing/paused) 417 toggleSlideshow: function() { 418 if (this.isSlideshowRunning) 419 this.pause(); 420 else 421 this.play(); 422 423 return this; 424 }, 425 426 // Advances the slideshow to the next image and delegates navigation to the 427 // history plugin when history is enabled 428 // enableHistory is true 429 ssAdvance: function() { 430 if (this.isSlideshowRunning) 431 this.next(true); 432 433 return this; 434 }, 435 436 // Advances the gallery to the next image. 437 // @param {Boolean} dontPause Specifies whether to pause the slideshow. 438 // @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled. 439 next: function(dontPause, bypassHistory) { 440 this.gotoIndex(this.getNextIndex(this.currentImage.index), dontPause, bypassHistory); 441 return this; 442 }, 443 444 // Navigates to the previous image in the gallery. 445 // @param {Boolean} dontPause Specifies whether to pause the slideshow. 446 // @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled. 447 previous: function(dontPause, bypassHistory) { 448 this.gotoIndex(this.getPrevIndex(this.currentImage.index), dontPause, bypassHistory); 449 return this; 450 }, 451 452 // Navigates to the next page in the gallery. 453 // @param {Boolean} dontPause Specifies whether to pause the slideshow. 454 // @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled. 455 nextPage: function(dontPause, bypassHistory) { 456 var page = this.getCurrentPage(); 457 var lastPage = this.getNumPages() - 1; 458 if (page < lastPage) { 459 var startIndex = page * this.numThumbs; 460 var nextPage = startIndex + this.numThumbs; 461 this.gotoIndex(nextPage, dontPause, bypassHistory); 462 } 463 464 return this; 465 }, 466 467 // Navigates to the previous page in the gallery. 468 // @param {Boolean} dontPause Specifies whether to pause the slideshow. 469 // @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled. 470 previousPage: function(dontPause, bypassHistory) { 471 var page = this.getCurrentPage(); 472 if (page > 0) { 473 var startIndex = page * this.numThumbs; 474 var prevPage = startIndex - this.numThumbs; 475 this.gotoIndex(prevPage, dontPause, bypassHistory); 476 } 477 478 return this; 479 }, 480 481 // Navigates to the image at the specified index in the gallery 482 // @param {Integer} index The index of the image in the gallery to display. 483 // @param {Boolean} dontPause Specifies whether to pause the slideshow. 484 // @param {Boolean} bypassHistory Specifies whether to delegate navigation to the history plugin when history is enabled. 485 gotoIndex: function(index, dontPause, bypassHistory) { 486 if (!dontPause) 487 this.pause(); 488 489 if (index < 0) index = 0; 490 else if (index >= this.data.length) index = this.data.length-1; 491 492 var imageData = this.data[index]; 493 494 if (!bypassHistory && this.enableHistory) 495 $.historyLoad(String(imageData.hash)); // At the moment, historyLoad only accepts string arguments 496 else 497 this.gotoImage(imageData); 498 499 return this; 500 }, 501 502 // This function is garaunteed to be called anytime a gallery slide changes. 503 // @param {Object} imageData An object holding the image metadata of the image to navigate to. 504 gotoImage: function(imageData) { 505 var index = imageData.index; 506 507 if (this.onSlideChange) 508 this.onSlideChange(this.currentImage.index, index); 509 510 this.currentImage = imageData; 511 this.preloadRelocate(index); 512 513 this.refresh(); 514 515 return this; 516 }, 517 518 // Returns the default transition duration value. The value is halved when not 519 // performing a synchronized transition. 520 // @param {Boolean} isSync Specifies whether the transitions are synchronized. 521 getDefaultTransitionDuration: function(isSync) { 522 if (isSync) 523 return this.defaultTransitionDuration; 524 return this.defaultTransitionDuration / 2; 525 }, 526 527 // Rebuilds the slideshow image and controls and performs transitions 528 refresh: function() { 529 var imageData = this.currentImage; 530 if (!imageData) 531 return this; 532 533 var index = imageData.index; 534 535 // Update Controls 536 if (this.$controlsContainer) { 537 this.$controlsContainer
538 .find('div.nav-controls a.prev').attr('href', '#'+this.data[this.getPrevIndex(index)].hash).end() 539 .find('div.nav-controls a.next').attr('href', '#'+this.data[this.getNextIndex(index)].hash); 540 } 541 542 var previousSlide = this.$imageContainer.find('span.current').addClass('previous').removeClass('current'); 543 var previousCaption = 0; 544 545 if (this.$captionContainer) { 546 previousCaption = this.$captionContainer.find('span.current').addClass('previous').removeClass('current'); 547 } 548 549 // Perform transitions simultaneously if syncTransitions is true and the next image is already preloaded 550 var isSync = this.syncTransitions && imageData.image; 551 552 // Flag we are transitioning 553 var isTransitioning = true; 554 var gallery = this; 555 556 var transitionOutCallback = function() { 557 // Flag that the transition has completed 558 isTransitioning = false; 559 560 // Remove the old slide 561 previousSlide.remove(); 562 563 // Remove old caption 564 if (previousCaption) 565 previousCaption.remove(); 566 567 if (!isSync) { 568 if (imageData.image && imageData.hash == gallery.data[gallery.currentImage.index].hash) { 569 gallery.buildImage(imageData, isSync); 570 } else { 571 // Show loading container 572 if (gallery.$loadingContainer) { 573 gallery.$loadingContainer.show(); 574 } 575 } 576 } 577 }; 578 579 if (previousSlide.length == 0) { 580 // For the first slide, the previous slide will be empty, so we will call the callback immediately 581 transitionOutCallback(); 582 } else { 583 if (this.onTransitionOut) { 584 this.onTransitionOut(previousSlide, previousCaption, isSync, transitionOutCallback); 585 } else { 586 previousSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0, transitionOutCallback); 587 if (previousCaption) 588 previousCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0); 589 } 590 } 591 592 // Go ahead and begin transitioning in of next image 593 if (isSync) 594 this.buildImage(imageData, isSync); 595 596 if (!imageData.image) { 597 var image = new Image(); 598 599 // Wire up mainImage onload event 600 image.onload = function() { 601 imageData.image = this; 602 603 // Only build image if the out transition has completed and we are still on the same image hash 604 if (!isTransitioning && imageData.hash == gallery.data[gallery.currentImage.index].hash) { 605 gallery.buildImage(imageData, isSync); 606 } 607 }; 608 609 // set alt and src 610 image.alt = imageData.title; 611 image.src = imageData.slideUrl; 612 } 613 614 // This causes the preloader (if still running) to relocate out from the currentIndex 615 this.relocatePreload = true; 616 617 return this.syncThumbs(); 618 }, 619 620 // Called by the refresh method after the previous image has been transitioned out or at the same time 621 // as the out transition when performing a synchronous transition. 622 // @param {Object} imageData An object holding the image metadata of the image to build. 623 // @param {Boolean} isSync Specifies whether the transitions are synchronized. 624 buildImage: function(imageData, isSync) { 625 var gallery = this; 626 var nextIndex = this.getNextIndex(imageData.index); 627 628 // Construct new hidden span for the image 629 var newSlide = this.$imageContainer 630 .append('<span class="image-wrapper current"><a class="advance-link" rel="history" href="#'+this.data[nextIndex].hash+'" title="'+imageData.title+'"> </a></span>') 631 .find('span.current').css('opacity', '0'); 632 633 newSlide.find('a') 634 .append(imageData.image) 635 .click(function(e) { 636 gallery.clickHandler(e, this); 637 }); 638 639 var newCaption = 0; 640 if (this.$captionContainer) { 641 // Construct new hidden caption for the image 642 newCaption = this.$captionContainer 643 .append('<span class="image-caption current"></span>') 644 .find('span.current').css('opacity', '0') 645 .append(imageData.caption); 646 } 647 648 // Hide the loading conatiner 649 if (this.$loadingContainer) { 650 this.$loadingContainer.hide(); 651 } 652 653 // Transition in the new image 654 if (this.onTransitionIn) { 655 this.onTransitionIn(newSlide, newCaption, isSync); 656 } else { 657 newSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0); 658 if (newCaption) 659 newCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0); 660 } 661 662 if (this.isSlideshowRunning) { 663 if (this.slideshowTimeout) 664 clearTimeout(this.slideshowTimeout); 665 666 this.slideshowTimeout = setTimeout(function() { gallery.ssAdvance(); }, this.delay); 667 } 668 669 return this; 670 }, 671 672 // Returns the current page index that should be shown for the currentImage 673 getCurrentPage: function() {
674 return Math.floor(this.currentImage.index / this.numThumbs); 675 }, 676 677 // Applies the selected class to the current image's corresponding thumbnail. 678 // Also checks if the current page has changed and updates the displayed page of thumbnails if necessary. 679 syncThumbs: function() { 680 var page = this.getCurrentPage(); 681 if (page != this.displayedPage) 682 this.updateThumbs(); 683 684 // Remove existing selected class and add selected class to new thumb 685 var $thumbs = this.find('ul.thumbs').children(); 686 $thumbs.filter('.selected').removeClass('selected'); 687 $thumbs.eq(this.currentImage.index).addClass('selected'); 688 689 return this; 690 }, 691 692 // Performs transitions on the thumbnails container and updates the set of 693 // thumbnails that are to be displayed and the navigation controls. 694 // @param {Delegate} postTransitionOutHandler An optional delegate that is called after 695 // the thumbnails container has transitioned out and before the thumbnails are rebuilt. 696 updateThumbs: function(postTransitionOutHandler) { 697 var gallery = this; 698 var transitionOutCallback = function() { 699 // Call the Post-transition Out Handler 700 if (postTransitionOutHandler) 701 postTransitionOutHandler(); 702 703 gallery.rebuildThumbs(); 704 705 // Transition In the thumbsContainer 706 if (gallery.onPageTransitionIn) 707 gallery.onPageTransitionIn(); 708 else 709 gallery.show(); 710 }; 711 712 // Transition Out the thumbsContainer 713 if (this.onPageTransitionOut) { 714 this.onPageTransitionOut(transitionOutCallback); 715 } else { 716 this.hide(); 717 transitionOutCallback(); 718 } 719 720 return this; 721 }, 722 723 // Updates the set of thumbnails that are to be displayed and the navigation controls. 724 rebuildThumbs: function() { 725 var needsPagination = this.data.length > this.numThumbs; 726 727 // Rebuild top pager 728 if (this.enableTopPager) { 729 var $topPager = this.find('div.top'); 730 if ($topPager.length == 0) 731 $topPager = this.prepend('<div class="top pagination"></div>').find('div.top'); 732 else 733 $topPager.empty(); 734 735 if (needsPagination) 736 this.buildPager($topPager); 737 } 738 739 // Rebuild bottom pager 740 if (this.enableBottomPager) { 741 var $bottomPager = this.find('div.bottom'); 742 if ($bottomPager.length == 0) 743 $bottomPager = this.append('<div class="bottom pagination"></div>').find('div.bottom'); 744 else 745 $bottomPager.empty(); 746 747 if (needsPagination) 748 this.buildPager($bottomPager); 749 } 750 751 var page = this.getCurrentPage(); 752 var startIndex = page*this.numThumbs; 753 var stopIndex = startIndex+this.numThumbs-1; 754 if (stopIndex >= this.data.length) 755 stopIndex = this.data.length-1; 756 757 // Show/Hide thumbs 758 var $thumbsUl = this.find('ul.thumbs'); 759 $thumbsUl.find('li').each(function(i) { 760 var $li = $(this); 761 if (i >= startIndex && i <= stopIndex) { 762 $li.show(); 763 } else { 764 $li.hide(); 765 } 766 }); 767 768 this.displayedPage = page; 769 770 // Remove the noscript class from the thumbs container ul 771 $thumbsUl.removeClass('noscript'); 772 773 return this; 774 }, 775 776 // Returns the total number of pages required to display all the thumbnails. 777 getNumPages: function() { 778 return Math.ceil(this.data.length/this.numThumbs); 779 }, 780 781 // Rebuilds the pager control in the specified matched element. 782 // @param {jQuery} pager A jQuery element set matching the particular pager to be rebuilt. 783 buildPager: function(pager) { 784 var gallery = this; 785 var numPages = this.getNumPages(); 786 var page = this.getCurrentPage(); 787 var startIndex = page * this.numThumbs; 788 var pagesRemaining = this.maxPagesToShow - 1; 789 790 var pageNum = page - Math.floor((this.maxPagesToShow - 1) / 2) + 1; 791 if (pageNum > 0) { 792 var remainingPageCount = numPages - pageNum; 793 if (remainingPageCount < pagesRemaining) { 794 pageNum = pageNum - (pagesRemaining - remainingPageCount); 795 } 796 } 797 798 if (pageNum < 0) { 799 pageNum = 0; 800 } 801 802 // Prev Page Link 803 if (page > 0) { 804 var prevPage = startIndex - this.numThumbs; 805 pager.append('<a rel="history" href="#'+this.data[prevPage].hash+'" title="'+this.prevPageLinkText+'">'+this.prevPageLinkText+'</a>'); 806 } 807 808 // Create First Page link if needed 809 if (pageNum > 0) { 810 this.buildPageLink(pager, 0, numPages); 811 if (pageNum > 1) 812 pager.append('<span class="ellipsis">…</span>'); 813 814 pagesRemaining--; 815 } 816 817 // Page Index Links 818 while (pagesRemaining > 0) { 819 this.buildPageLink(pager, pageNum, numPages); 820 pagesRemaining--; 821 pageNum++; 822 } 823 824 // Create Last Page link if needed 825 if (pageNum < numPages) { 826 var lastPageNum = numPages - 1; 827 if (pageNum < lastPageNum) 828 pager.append('<span class="ellipsis">…</span>'); 829 830 this.buildPageLink(pager, lastPageNum, numPages); 831 } 832 833 // Next Page Link 834 var nextPage = startIndex + this.numThumbs; 835 if (nextPage < this.data.length) { 836 pager.append('<a rel="history" href="#'+this.data[nextPage].hash+'" title="'+this.nextPageLinkText+'">'+this.nextPageLinkText+'</a>'); 837 } 838 839 pager.find('a').click(function(e) { 840 gallery.clickHandler(e, this); 841 }); 842 843 return this; 844 }, 845 846 // Builds a single page link within a pager. This function is called by buildPager 847 // @param {jQuery} pager A jQuery element set matching the particular pager to be rebuilt. 848 // @param {Integer} pageNum The page number of the page link to build. 849 // @param {Integer} numPages The total number of pages required to display all thumbnails. 850 buildPageLink: function(pager, pageNum, numPages) { 851 var pageLabel = pageNum + 1; 852 var currentPage = this.getCurrentPage(); 853 if (pageNum == currentPage) 854 pager.append('<span class="current">'+pageLabel+'</span>'); 855 else if (pageNum < numPages) { 856 var imageIndex = pageNum*this.numThumbs; 857 pager.append('<a rel="history" href="#'+this.data[imageIndex].hash+'" title="'+pageLabel+'">'+pageLabel+'</a>'); 858 } 859 860 return this; 861 } 862 }); 863 864 // Now initialize the gallery 865 $.extend(this, defaults, settings); 866 867 // Verify the history plugin is available 868 if (this.enableHistory && !$.historyInit) 869 this.enableHistory = false;
870 871 // Select containers 872 if (this.imageContainerSel) this.$imageContainer = $(this.imageContainerSel); 873 if (this.captionContainerSel) this.$captionContainer = $(this.captionContainerSel); 874 if (this.loadingContainerSel) this.$loadingContainer = $(this.loadingContainerSel); 875 876 // Initialize the thumbails 877 this.initializeThumbs(); 878 879 if (this.maxPagesToShow < 3) 880 this.maxPagesToShow = 3; 881 882 this.displayedPage = -1; 883 this.currentImage = this.data[0]; 884 var gallery = this; 885 886 // Hide the loadingContainer 887 if (this.$loadingContainer) 888 this.$loadingContainer.hide(); 889 890 // Setup controls 891 if (this.controlsContainerSel) { 892 this.$controlsContainer = $(this.controlsContainerSel).empty(); 893 894 if (this.renderSSControls) { 895 if (this.autoStart) { 896 this.$controlsContainer 897 .append('<div class="ss-controls"><a href="#pause" class="pause" title="'+this.pauseLinkText+'">'+this.pauseLinkText+'</a></div>'); 898 } else { 899 this.$controlsContainer 900 .append('<div class="ss-controls"><a href="#play" class="play" title="'+this.playLinkText+'">'+this.playLinkText+'</a></div>'); 901 } 902 903 this.$controlsContainer.find('div.ss-controls a') 904 .click(function(e) { 905 gallery.toggleSlideshow(); 906 e.preventDefault(); 907 return false; 908 }); 909 } 910 911 if (this.renderNavControls) { 912 this.$controlsContainer 913 .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>') 914 .find('div.nav-controls a') 915 .click(function(e) { 916 gallery.clickHandler(e, this); 917 }); 918 } 919 } 920 921 var initFirstImage = !this.enableHistory || !location.hash; 922 if (this.enableHistory && location.hash) { 923 var hash = $.galleriffic.normalizeHash(location.hash); 924 var imageData = allImages[hash]; 925 if (!imageData) 926 initFirstImage = true; 927 } 928 929 // Setup gallery to show the first image 930 if (initFirstImage) 931 this.gotoIndex(0, false, true); 932 933 // Setup Keyboard Navigation 934 if (this.enableKeyboardNavigation) { 935 $(document).keydown(function(e) { 936 var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0; 937 switch(key) { 938 case 32: // space 939 gallery.next(); 940 e.preventDefault(); 941 break; 942 case 33: // Page Up 943 gallery.previousPage(); 944 e.preventDefault(); 945 break; 946 case 34: // Page Down 947 gallery.nextPage(); 948 e.preventDefault(); 949 break; 950 case 35: // End 951 gallery.gotoIndex(gallery.data.length-1); 952 e.preventDefault(); 953 break; 954 case 36: // Home 955 gallery.gotoIndex(0); 956 e.preventDefault(); 957 break; 958 case 37: // left arrow 959 gallery.previous(); 960 e.preventDefault(); 961 break; 962 case 39: // right arrow 963 gallery.next(); 964 e.preventDefault(); 965 break; 966 } 967 }); 968 } 969 970 // Auto start the slideshow 971 if (this.autoStart) 972 this.play(); 973 974 // Kickoff Image Preloader after 1 second 975 setTimeout(function() { gallery.preloadInit(); }, 1000); 976 977 return this; 978 }; 979})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.