PageSourceSearch

https://www.rottenburg.de/js/jquery.galleriffic.js

js rottenburg.de collected 2026-10-02 08:24:04 UTC 32,765 bytes, 990 lines download raw bytes

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&ouml;&szlig;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">&hellip;</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">&hellip;</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.