To get rid of this ad please Sign up/Login.
MediaWiki:Gadget-GallerySlideshow.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/** * jQuery Galleriffic plugin * * Copyright (c) 2008 Trent Foley (http://trentacular.com) * Licensed under the MIT License: * http://www.opensource.org/licenses/mit-license.php * * Much thanks to primary contributer Ponticlaro (http://www.ponticlaro.com) * * Rewritten for commons by [[User:DieBuche]], * Based on a script by [[User:Dschwen]] */ if (typeof(GallerySlide) == 'undefined' && wgNamespaceNumber == 14) { importStylesheet('MediaWiki:Gadget-GallerySlideshow.css'); //Declare global var for todays specific gallery var GallerySlide; (function ($) { // Globally keep track of all images by their unique hash. Each item is an image data object. var allImages = {}; var imageCounter = 0; // Galleriffic static class $.galleriffic = { version: '2.1', // Strips invalid characters and any leading # characters normalizeHash: function (hash) { return hash.replace('#', ''); }, getImage: function (hash) { if (!hash) return undefined; hash = $.galleriffic.normalizeHash(hash); return allImages[hash]; }, // Global function that looks up an image by its hash and displays the image. // Returns false when an image is not found for the specified hash. // @param {String} hash This is the unique hash value assigned to an image. gotoImage: function (hash) { var imageData = $.galleriffic.getImage(hash); //if (!imageData) return false; var gallery = imageData.gallery; gallery.gotoImage(imageData); return true; } }; var defaults = { delay: 2500, preloadAhead: 25, renderSSControls: true, renderNavControls: true, playLinkText: 'Play', pauseLinkText: 'Pause', prevLinkText: 'Previous', nextLinkText: 'Next', hideText: 'Close slideshow', enableKeyboardNavigation: true, autoStart: false, defaultTransitionDuration: 700, onSlideChange: function (prevIndex, nextIndex) { var displayed = Math.floor(this.$thumbsUl.parent().width() / 81); var spaceRight = displayed - (nextIndex - this.hiddenLeft); var spaceLeft = (1 + nextIndex - this.hiddenLeft); if (spaceRight < 3) { //Time to slide viewport var current = this.$thumbsUl.css('left').replace('px', ''); var offset = (parseFloat(current) - (3 - spaceRight) * 81); this.$thumbsUl.animate({ left: offset + 'px' }, 'fast'); this.hiddenLeft = this.hiddenLeft + (3 - spaceRight); } if (spaceLeft < 3 && nextIndex > 1) { var current = this.$thumbsUl.css('left').replace('px', ''); var offset = (parseFloat(current) + (3 - spaceLeft) * 81); this.$thumbsUl.animate({ left: offset + 'px' }, 'fast'); this.hiddenLeft = this.hiddenLeft - (3 - spaceLeft); } if (nextIndex == 0) { this.$thumbsUl.animate({ left: '0px' }, 'fast'); this.hiddenLeft = 0; } if (this.data.length - 5 < nextIndex) { //Time to fetch more GallerySlide.queryApi(); } }, // accepts a delegate like such: function(prevIndex, nextIndex) { ... } onTransitionOut: undefined, // accepts a delegate like such: function(slide, caption, isSync, callback) { ... } onTransitionIn: undefined }; // Primary Galleriffic initialization function that should be called on the thumbnail container. $.fn.galleriffic = function (settings) { // Extend Gallery Object $.extend(this, { // Returns the version of the script version: $.galleriffic.version, // Current state of the slideshow isSlideshowRunning: false, slideshowTimeout: undefined, hiddenLeft: 0, apiURL: wgServer + wgScriptPath + "/api.php", initial: true, data: [], // This function is attached to the click event of generated hyperlinks within the gallery clickHandler: function (e, link) { this.pause(); // The href attribute holds the unique hash for an image var hash = $.galleriffic.normalizeHash($(link).attr('href')); $.galleriffic.gotoImage(hash); e.preventDefault(); }, createContainer: function () { var $d = $('<div class="slideshow-container"></div>'); this.$imageContainer = $('<div id="slideshow" class="slideshow"></div>'); this.$captionContainer = $('<div id="caption" class="caption-container"></div>'); this.$loadingContainer = $('<div id="loading" class="loader"></div>'); this.$controlsContainer = $('<div id="controls" class="controls"></div>'); this.append('<div id="thumbs" class="navigation"><ul class="thumbs"></ul></div>'); this.append($d); $d.append(this.$controlsContainer).append(this.$loadingContainer).append(this.$imageContainer); this.append(this.$captionContainer); this.$thumbsUl = this.find('ul.thumbs'); }, // Scrapes the thumbnail container for thumbs and adds each to the gallery initializeThumbs: function () { var data = this.passedData; var gallery = this; for (i in data) { imageData = data[i]; imageData.index = hash = imageCounter; imageData.gallery = gallery; var aspect = (imageData.width / imageData.height); var size = (aspect > 1) ? 'height' : 'width'; var $thumb = $('<li><a class="thumb"><img ' + size + '=75px src="' + imageData.slideUrl + '" /></a></li>'); $thumb.css('opacity', 0.67).hover( function () { $(this).not('.selected').fadeTo('fast', 1); }, function () { $(this).not('.selected').fadeTo('fast', 0.67); }); this.$thumbsUl.append($thumb); imageData.caption = '<a href=' + wgServer + '/wiki/' + imageData.title.replace(/ /g, '_') + '>' + imageData.title.replace('File:', '').replace(/\.[\w]{3,4}$/, '') +'</a>'; // Register the image globally allImages['' + hash] = imageData; // Setup attributes and click handler $thumb.find('a').attr('href', '#' + hash).removeAttr('name').click(function (e) { gallery.clickHandler(e, this); }); imageCounter++; } this.data = this.data.concat(data); return this; }, isPreloadComplete: false, // Initalizes the image preloader preloadInit: function () { if (this.preloadAhead == 0) return this; this.preloadStartIndex = this.currentImage.index; var nextIndex = this.getNextIndex(this.preloadStartIndex); return this.preloadRecursive(this.preloadStartIndex, nextIndex); }, // Changes the location in the gallery the preloader should work // @param {Integer} index The index of the image where the preloader should restart at. preloadRelocate: function (index) { // By changing this startIndex, the current preload script will restart this.preloadStartIndex = index; return this; }, // Recursive function that performs the image preloading // @param {Integer} startIndex The index of the first image the current preloader started on. // @param {Integer} currentIndex The index of the current image to preload. preloadRecursive: function (startIndex, currentIndex) { // Check if startIndex has been relocated if (startIndex != this.preloadStartIndex) { var nextIndex = this.getNextIndex(this.preloadStartIndex); return this.preloadRecursive(this.preloadStartIndex, nextIndex); } var gallery = this; // Now check for preloadAhead count var preloadCount = currentIndex - startIndex; if (preloadCount < 0) preloadCount = this.data.length - 1 - startIndex + currentIndex; if (this.preloadAhead >= 0 && preloadCount > this.preloadAhead) { // Do this in order to keep checking for relocated start index setTimeout(function () { gallery.preloadRecursive(startIndex, currentIndex); }, 500); return this; } var imageData = this.data[currentIndex]; if (!imageData) return this; // If already loaded, continue if (imageData.image) return this.preloadNext(startIndex, currentIndex); // Preload the image var image = new Image(); image.onload = function () { imageData.image = this; gallery.preloadNext(startIndex, currentIndex); }; image.alt = imageData.title; image.src = imageData.slideUrl; return this; }, // Called by preloadRecursive in order to preload the next image after the previous has loaded. // @param {Integer} startIndex The index of the first image the current preloader started on. // @param {Integer} currentIndex The index of the current image to preload. preloadNext: function (startIndex, currentIndex) { var nextIndex = this.getNextIndex(currentIndex); if (nextIndex == startIndex) { this.isPreloadComplete = true; } else { // Use setTimeout to free up thread var gallery = this; setTimeout(function () { gallery.preloadRecursive(startIndex, nextIndex); }, 100); } return this; }, // Safe way to get the next image index relative to the current image. // If the current image is the last, returns 0 getNextIndex: function (index) { var nextIndex = index + 1; if (nextIndex >= this.data.length) nextIndex = 0; return nextIndex; }, // Safe way to get the previous image index relative to the current image. // If the current image is the first, return the index of the last image in the gallery. getPrevIndex: function (index) { var prevIndex = index - 1; if (prevIndex < 0) prevIndex = this.data.length - 1; return prevIndex; }, // Pauses the slideshow pause: function () { this.isSlideshowRunning = false; if (this.slideshowTimeout) { clearTimeout(this.slideshowTimeout); this.slideshowTimeout = undefined; } if (this.$controlsContainer) { this.$controlsContainer.find('div.ss-controls a').removeClass().addClass('play').attr('title', this.playLinkText).attr('href', '#play').html(this.playLinkText); } return this; }, // Plays the slideshow play: function () { this.isSlideshowRunning = true; if (this.$controlsContainer) { this.$controlsContainer.find('div.ss-controls a').removeClass().addClass('pause').attr('title', this.pauseLinkText).attr('href', '#pause').html(this.pauseLinkText); } if (!this.slideshowTimeout) { var gallery = this; this.slideshowTimeout = setTimeout(function () { gallery.next(true); }, this.delay); } return this; }, // Toggles the state of the slideshow (playing/paused) toggleSlideshow: function () { if (this.isSlideshowRunning) this.pause(); else this.play(); return this; }, // Advances the gallery to the next image. // @param {Boolean} dontPause Specifies whether to pause the slideshow. next: function (dontPause) { this.gotoIndex(this.getNextIndex(this.currentImage.index), dontPause); return this; }, // Navigates to the previous image in the gallery. // @param {Boolean} dontPause Specifies whether to pause the slideshow. previous: function (dontPause) { this.gotoIndex(this.getPrevIndex(this.currentImage.index), dontPause); return this; }, // Navigates to the image at the specified index in the gallery // @param {Integer} index The index of the image in the gallery to display. // @param {Boolean} dontPause Specifies whether to pause the slideshow. gotoIndex: function (index, dontPause) { if (!dontPause) this.pause(); if (index < 0) index = 0; else if (index >= this.data.length) index = this.data.length - 1; var imageData = this.data[index]; this.gotoImage(imageData); return this; }, // This function is guaranteed to be called anytime a gallery slide changes. // @param {Object} imageData An object holding the image metadata of the image to navigate to. gotoImage: function (imageData) { var index = imageData.index; if (this.onSlideChange) this.onSlideChange(this.currentImage.index, index); this.currentImage = imageData; this.preloadRelocate(index); this.refresh(); return this; }, // Returns the default transition duration value. The value is halved when not // performing a synchronized transition. // @param {Boolean} isSync Specifies whether the transitions are synchronized. getDefaultTransitionDuration: function (isSync) { if (isSync) return this.defaultTransitionDuration; return this.defaultTransitionDuration / 2; }, // Rebuilds the slideshow image and controls and performs transitions refresh: function () { var imageData = this.currentImage; if (!imageData) return this; var index = imageData.index; var previousSlide = this.$imageContainer.find('span.current').addClass('previous').removeClass('current'); var previousCaption = 0; if (this.$captionContainer) { previousCaption = this.$captionContainer.find('span.current').addClass('previous').removeClass('current'); } // Perform transitions simultaneously if the next image is already preloaded var isSync = imageData.image; // Flag we are transitioning var isTransitioning = true; var gallery = this; var transitionOutCallback = function () { // Flag that the transition has completed isTransitioning = false; // Remove the old slide previousSlide.remove(); // Remove old caption if (previousCaption) previousCaption.remove(); if (!isSync) { if (imageData.image && imageData.index == gallery.data[gallery.currentImage.index].index) { gallery.buildImage(imageData, isSync); } else { // Show loading container if (gallery.$loadingContainer) { gallery.$loadingContainer.show(); } } } }; if (previousSlide.length == 0) { // For the first slide, the previous slide will be empty, so we will call the callback immediately transitionOutCallback(); } else { previousSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0, transitionOutCallback); if (previousCaption) previousCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0); } // Go ahead and begin transitioning in of next image if (isSync) this.buildImage(imageData, isSync); if (!imageData.image) { var image = new Image(); // Wire up mainImage onload event image.onload = function () { imageData.image = this; // Only build image if the out transition has completed and we are still on the same image hash if (!isTransitioning && imageData.index == gallery.data[gallery.currentImage.index].index) { gallery.buildImage(imageData, isSync); } }; // set alt and src image.alt = imageData.title; image.src = imageData.slideUrl; } // This causes the preloader (if still running) to relocate out from the currentIndex this.relocatePreload = true; return this.syncThumbs(); }, // Called by the refresh method after the previous image has been transitioned out or at the same time // as the out transition when performing a synchronous transition. // @param {Object} imageData An object holding the image metadata of the image to build. // @param {Boolean} isSync Specifies whether the transitions are synchronized. buildImage: function (imageData, isSync) { var gallery = this; var nextIndex = this.getNextIndex(imageData.index); var left = (gallery.$thumbsUl.parent().width() - imageData.width) / 2; // Construct new hidden span for the image var newSlide = this.$imageContainer.append('<span class="image-wrapper current" style="left:' + left + 'px ;"><a class="advance-link" href="#' + this.data[nextIndex].index + '" title="' + imageData.title + '"> </a></span>').find('span.current').css('opacity', '0'); newSlide.find('a').append(imageData.image).click(function (e) { gallery.clickHandler(e, this); }); var newCaption = 0; if (this.$captionContainer) { // Construct new hidden caption for the image newCaption = this.$captionContainer.append('<span class="image-caption current" style="left:' + left + 'px;"></span>').find('span.current').css('opacity', '0').append(imageData.caption); } // Hide the loading conatiner if (this.$loadingContainer) { this.$loadingContainer.hide(); } // Transition in the new image if (this.onTransitionIn) { this.onTransitionIn(newSlide, newCaption, isSync); } else { newSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0); if (newCaption) newCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0); } if (this.isSlideshowRunning) { if (this.slideshowTimeout) clearTimeout(this.slideshowTimeout); this.slideshowTimeout = setTimeout(function () { gallery.next(true); }, this.delay); } return this; }, // Applies the selected class to the current image's corresponding thumbnail. // Also checks if the current page has changed and updates the displayed page of thumbnails if necessary. syncThumbs: function () { // Remove existing selected class and add selected class to new thumb var $thumbs = this.$thumbsUl.children(); $thumbs.filter('.selected').removeClass('selected').fadeTo('fast', 0.67); $thumbs.eq(this.currentImage.index).addClass('selected').fadeTo('fast', 1); return this; }, init: function () { this.createContainer(); // Initialize the thumbails this.initializeThumbs(); this.currentImage = this.data[0]; var gallery = this; // Hide the loadingContainer this.$loadingContainer.hide(); this.gotoIndex(0); // Setup controls if (this.renderSSControls) { if (this.autoStart) { this.$controlsContainer.append('<div class="ss-controls"><a href="#pause" class="pause" title="' + this.pauseLinkText + '">' + this.pauseLinkText + '</a></div>'); } else { this.$controlsContainer.append('<div class="ss-controls"><a href="#play" class="play" title="' + this.playLinkText + '">' + this.playLinkText + '</a></div>'); } this.$controlsContainer.find('div.ss-controls a').click(function (e) { gallery.toggleSlideshow(); e.preventDefault(); return false; }); } if (this.renderNavControls) { this.$controlsContainer.append('<div class="nav-controls"><a class="prev" title="' + this.prevLinkText + '">' + this.prevLinkText + '</a><a class="next" title="' + this.nextLinkText + '">' + this.nextLinkText + '</a><a class="toggle" title="' + this.hideText + '">' + this.hideText + '</a></div>'); this.$controlsContainer.find('div.nav-controls a.prev').click(function () { gallery.previous(); }); this.$controlsContainer.find('div.nav-controls a.next').click(function () { gallery.next(); }); this.$controlsContainer.find('div.nav-controls a.toggle').click(function () { gallery.toggleVisibility(); }); } // Setup Keyboard Navigation if (this.enableKeyboardNavigation) { $(document).keydown(function (e) { var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0; switch (key) { case 32: // space gallery.next(); e.preventDefault(); break; case 35: // End gallery.gotoIndex(gallery.data.length - 1); e.preventDefault(); break; case 37: // left arrow gallery.previous(); e.preventDefault(); break; case 39: // right arrow gallery.next(); e.preventDefault(); break; } }); } // Auto start the slideshow if (this.autoStart) this.play(); // Kickoff Image Preloader after 1 second setTimeout(function () { gallery.preloadInit(); }, 1000); }, start: function () { $('#GallerySlideInit').hide(); $('#SlideContainer').animate({ height: '600px' }); this.queryApi(); }, toggleVisibility: function () { $('#GallerySlideInit').toggle().unbind('click').click(GallerySlide.toggleVisibility); $('#SlideContainer').toggle(); }, queryApi: function () { params = { action: 'query', generator: 'categorymembers', gcmtitle: wgPageName, gcmlimit: Math.floor($('#mw-category-media').width() / 81) + 1, gcmnamespace: 6, prop: 'imageinfo', iiprop: 'url', iiurlwidth: '1500', iiurlheight: '400', format: 'json' }; if (this.cont) params.gcmcontinue = this.cont; if (!this.initial && !this.cont) return; $.ajax({ url: this.apiURL, cache: false, dataType: 'json', data: params, type: 'POST', success: function (result, status, x) { GallerySlide.processReturn(result); } }); }, processReturn: function (result) { var pages = result.query.pages; data = []; i = 0; if (result['query-continue']) this.cont = result['query-continue'].categorymembers.gcmcontinue; else this.cont = false; for (var id in pages) { v = pages[id]; n = data[i] = {}; n.title = v.title; n['link'] = v.imageinfo[0].descriptionurl; n.slideUrl = v.imageinfo[0].thumburl; n.width = v.imageinfo[0].thumbwidth; n.height = v.imageinfo[0].thumbheight; i++; } this.passedData = data; if (this.initial) { this.init(); } else { this.initializeThumbs(); } this.initial = false; } }); // Now initialize the gallery $.extend(this, defaults, settings); return this; }; })(jQuery); $j(document).ready(function () { $('#mw-category-media h2').after('<span id="GallerySlideInit">Show Slideshow</span>'); $('#GallerySlideInit').after('<div id="SlideContainer" style="width:100%; height:0px; background: #EBEBEB; "></div>'); GallerySlide = $('#SlideContainer').galleriffic(); $('#GallerySlideInit').click(function () { GallerySlide.start(); }); }); }