comparison bin/js/jquery.galleriffic.js @ 0:198009598544 draft

Uploaded
author romaingred
date Wed, 11 Oct 2017 09:57:58 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:198009598544
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+'">&nbsp;</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">&hellip;</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">&hellip;</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);