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