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