Galleriffic 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +17 -0
  4. data/.idea/.name +1 -0
  5. data/.idea/.rakeTasks +7 -0
  6. data/.idea/Galleriffic.iml +20 -0
  7. data/.idea/encodings.xml +5 -0
  8. data/.idea/misc.xml +5 -0
  9. data/.idea/modules.xml +9 -0
  10. data/.idea/scopes/scope_settings.xml +5 -0
  11. data/.idea/vcs.xml +7 -0
  12. data/.idea/workspace.xml +471 -0
  13. data/Galleriffic.gemspec +24 -0
  14. data/Gemfile +4 -0
  15. data/LICENSE.txt +22 -0
  16. data/README.md +34 -0
  17. data/Rakefile +1 -0
  18. data/lib/.DS_Store +0 -0
  19. data/lib/Galleriffic/version.rb +3 -0
  20. data/lib/Galleriffic.rb +6 -0
  21. data/vendor/.DS_Store +0 -0
  22. data/vendor/assets/.DS_Store +0 -0
  23. data/vendor/assets/javascripts/.DS_Store +0 -0
  24. data/vendor/assets/javascripts/jquery-1.3.2.js +4376 -0
  25. data/vendor/assets/javascripts/jquery.galleriffic.js +979 -0
  26. data/vendor/assets/javascripts/jquery.history.js +168 -0
  27. data/vendor/assets/javascripts/jquery.opacityrollover.js +42 -0
  28. data/vendor/assets/javascripts/jush.js +515 -0
  29. data/vendor/assets/stylesheets/.DS_Store +0 -0
  30. data/vendor/assets/stylesheets/basic.css +63 -0
  31. data/vendor/assets/stylesheets/black.css +57 -0
  32. data/vendor/assets/stylesheets/caption.png +0 -0
  33. data/vendor/assets/stylesheets/galleriffic-1.css +161 -0
  34. data/vendor/assets/stylesheets/galleriffic-2.css +150 -0
  35. data/vendor/assets/stylesheets/galleriffic-3.css +150 -0
  36. data/vendor/assets/stylesheets/galleriffic-4.css +160 -0
  37. data/vendor/assets/stylesheets/galleriffic-5.css +197 -0
  38. data/vendor/assets/stylesheets/jush.css +29 -0
  39. data/vendor/assets/stylesheets/loader.gif +0 -0
  40. data/vendor/assets/stylesheets/loaderWhite.gif +0 -0
  41. data/vendor/assets/stylesheets/nextPageArrow.gif +0 -0
  42. data/vendor/assets/stylesheets/nextPageArrowWhite.gif +0 -0
  43. data/vendor/assets/stylesheets/prevPageArrow.gif +0 -0
  44. data/vendor/assets/stylesheets/prevPageArrowWhite.gif +0 -0
  45. data/vendor/assets/stylesheets/white.css +57 -0
  46. metadata +121 -0
@@ -0,0 +1,979 @@
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);