jquery.galleriffic.js 32 KB

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