/**
* 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' && mw.config.get('wgNamespaceNumber') === 14) {
importStylesheet('User:DieBuche/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: 5500,
delayInsertBtn: 'Set delay in ms',
delayInsert: 'How many ms to wait for a new image?',
delayInvalid: 'Invalid input. Only numbers greater than 2500 are accepted.',
preloadAhead: 25,
playLinkText: 'Play',
playLinkIcon: '//upload.wikimedia.org/wikipedia/commons/thumb/9/93/Gnome-media-playback-start.svg/35px-Gnome-media-playback-start.svg.png',
pauseLinkText: 'Pause',
pauseLinkIcon: '//upload.wikimedia.org/wikipedia/commons/thumb/0/04/Gnome-media-playback-pause.svg/35px-Gnome-media-playback-pause.svg.png',
prevLinkText: 'Previous',
prevLinkIcon: '//upload.wikimedia.org/wikipedia/commons/thumb/9/92/Gnome-media-skip-backward.svg/35px-Gnome-media-skip-backward.svg.png',
nextLinkText: 'Next',
nextLinkIcon: '//upload.wikimedia.org/wikipedia/commons/thumb/7/73/Gnome-media-skip-forward.svg/35px-Gnome-media-skip-forward.svg.png',
hideText: 'Close slideshow',
continueKeyHowTo: 'Continue Key - You can save this key or bookmark the link if you\'d like to start at this position later.',
continueKeyInsert: 'Please insert the continue-key. You have to go forward in the slideshow to see effect.',
continueKeyInsertBtn: 'Insert continue-key',
continueKeyInvalid: 'Invalid key.',
licenseLabel: 'Available Licenses: ',
helpLinkTitle: 'Help and documentation for this tool',
descriptionLoadText: 'Loading description ...',
descriptionSpinner: '//bits.wikimedia.org/skins-1.5/common/images/ajax-loader.gif',
enableKeyboardNavigation: true,
autoPlay: false,
defaultTransitionDuration: 700,
maxImageHeight: Math.floor(($(window).height() - 120)/50) * 50, // using Interval-Sizes to reduce server-load (cached thumbs)
maxImageWidth: Math.floor(($(window).width() - 400)/50) * 50,
cmdir: 'asc',
continueKey: '',
continueKeyPattern: /^file\|[\da-fA-F]+\|\d+$/,
licenseRecognization: [
// RegExp for the tag note to add to the thumb-page
[/Category:CC[\- _]BY-SA.*/i, 'CC-By-SA'],
[/Category:CC[\- _]BY.*/i, 'CC-By'],
[/Category:CC[\- _]Zero.*/i, 'CC0'],
[/Category:GFDL.*/i, 'GFDL'],
[/Category:PD[\- _]Old.*/i, 'PD-old'],
[/Category:PD[\- _]self.*/i, 'PD-self'],
[/Category:PD[\- _]author.*/i, 'PD-author'],
[/Category:PD.*/i, 'PDx'],
[/Category:FAL/i, 'Art Libre - Free Art'],
[/Category:Images requiring attribution/i, 'Attribution'],
[/Category:Copyrighted free use.*/i, 'Copyrighted FreeUse'],
[/Category:Mozilla Public License/i, 'MPL'],
[/Category:GPL/i, 'GPL'],
[/Category:LGPL/i, 'LGPL'],
[/Category:Free screenshot.*/i, 'free-Screenshot']
],
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: (/^\/\//.test(mw.config.get("wgServer")) ? document.location.protocol : '') + mw.config.get("wgServer") + mw.util.wikiScript( 'api' ),
indexURL: mw.config.get('wgServer') + mw.config.get('wgScript'),
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 gallery = this;
this.$slideshowContainer = $('<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>');
// Gray lines for navigation
this.$ctrBack = $('<div>', { 'class': 'bar-rwd' } )
.append($('<div>', { 'class': 'bar-btn-rwd' }).text('<'))
.click(function (e) { gallery.previous(); e.preventDefault(); })
.mouseenter(function () { $(this).find('div').fadeIn('fast'); }).mouseleave(function () { $(this).find('div').fadeOut('fast'); });
this.$ctrFwd = $('<div>', { 'class': 'bar-fwd' } )
.append( $('<div>', { 'class': 'bar-btn-fwd' }).text('>'))
.click(function (e) { gallery.next(); e.preventDefault(); })
.mouseenter(function () { $(this).find('div').last().fadeIn('fast'); }).mouseleave(function () { $(this).find('div').last().fadeOut('fast'); });
this.$closeButton = $('<div>', { 'class': 'slideshow-close-button', 'title' : this.hideText } )
.text('×').click(function (e) {
gallery.pause();
gallery.toggleVisibility();
e.preventDefault();
});
this.append('<div id="thumbs" class="navigation"><ul class="thumbs"></ul></div>');
this.append(this.$controlsContainer).append( this.$slideshowContainer );
this.$slideshowContainer.append(this.$loadingContainer).append(this.$captionContainer).append(this.$imageContainer);
this.append(this.$ctrBack).append(this.$ctrFwd.prepend(this.$closeButton));
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 = $('<div>').append( $('<a>', { href: imageData.link }).text(imageData.title.replace('File:', '').replace(/\.[\w]{3,4}$/, '')) ).html();
// 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.nav-controls a.pause').removeClass().addClass('play').attr('title', this.playLinkText).attr('href', '#play')
.html('<img src="' + this.playLinkIcon + '" height="35" width="35" alt="' + this.playLinkText + '"/>');
}
return this;
},
// Plays the slideshow
play: function () {
this.isSlideshowRunning = true;
if (this.$controlsContainer) {
this.$controlsContainer.find('div.nav-controls a.play').removeClass().addClass('pause').attr('title', this.pauseLinkText).attr('href', '#pause')
.html('<img src="' + this.pauseLinkIcon + '" height="35" width="35" alt="' + 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);
// computing the "center-position of the space"
var hSpace = (this.$ctrFwd.position().left - (this.$ctrBack.width() + this.$ctrBack.position().left) - 370 - 10);
var left = (this.$slideshowContainer.width() - this.$captionContainer.width() - imageData.width + 30 ) / 2;
var vSpace = this.$ctrFwd.height();
var top = ($(window).height() - this.$thumbsUl.height() - this.$controlsContainer.height() - imageData.height) / 2;
// Send an API request in case of unknown description
if ('string' !== typeof imageData.description) this.queryFile(imageData.title);
if ('string' !== typeof this.data[nextIndex].description) this.queryFile(this.data[nextIndex].title);
// Construct new hidden span for the image
var newSlide = this.$imageContainer.append($('<span>', { 'class': 'image-wrapper current', style: 'left:' + left + 'px; top:' + top + 'px; ' }).append($('<a>', { 'class': 'advance-link', href: imageData.link, title: imageData.title, target: '_blank', style: 'width:' + imageData.width + 'px; ' } ))).find('span.current').css('opacity', '0');
newSlide.find('a').append(imageData.image);
var descript = imageData.description ? imageData.description :
$('<span>').append($('<img>', { src: this.descriptionSpinner, height: 20, width: 20 }), mw.html.escape(this.descriptionLoadText));
var newCaption = 0;
var extraParams = '&gsDir=' + this.cmdir + '&gsAutoStart=1' + (mw.util.getParamValue('withJS') ? ('&withJS=' + mw.util.getParamValue('withJS')) : '' );
if (this.$captionContainer) {
// Construct new hidden caption for the image
newCaption = this.$captionContainer.append( $('<span>', { 'class': 'image-caption current', style: 'height:' + (this.maxImageHeight - 20) + 'px;' }) ).find('span.current').css('opacity', '0')
.append(imageData.caption, $('<br>'))
.append( $('<div>', { 'class': 'gs-img-description', id: 'desc' + imageData.index }).append(descript) )
.append( $('<div>', { 'class': 'gs-img-uploader' }).append(imageData.$user.clone()) )
.append( imageData.$cats, imageData.$licenses, $('<br>') )
.append( $('<div>', { 'class': 'gs-img-metrics' }).html('(' + imageData.oWidth + '×' + imageData.oHeight + ' (' + imageData.oSize + '))') )
.append( (imageData.contKey ?
$('<div>', { 'class': 'cont-key-container', title: this.continueKeyHowTo }).append(
$('<a>', { href: mw.util.getUrl(mw.config.get('wgPageName')) + '?gsContinue=' + imageData.contKey + extraParams, target: '_blank' }
).text(imageData.contKey) ) : ''
) );
}
// 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);
}
// Save the current position in a cookie or delete the cookie
if (imageData.contKey) {
mw.cookie.get( 'gs' + mw.config.get('wgPageName').replace('Category:', ''), imageData.contKey, { expires: 8 } );
} else {
mw.cookie.get( 'gs' + mw.config.get('wgPageName').replace('Category:', ''), null );
}
$(document).trigger('slideshow', ['newSlide', this]); // For external scripts
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();
var that = this;
$(window).resize(function() {
that.maxImageHeight = $(window).height() - that.$thumbsUl.height() - that.$controlsContainer.height();
that.maxImageWidth = that.$slideshowContainer.width() - that.$captionContainer.width() + 20;
}).resize();
// Initialize the thumbails
this.initializeThumbs();
this.currentImage = this.data[0];
var gallery = this;
// Hide the loadingContainer
this.$loadingContainer.hide();
this.gotoIndex(0);
// Setup controls
var playBtn, nextBtn, prevBtn, navCont;
if (this.autoPlay) {
playBtn = $('<a href="#pause" class="pause" title="' + this.pauseLinkText + '">' + '<img src="' + this.playLinkIcon + '" height="35" width="35" alt="' + this.pauseLinkText + '"/>' + '</a>');
} else {
playBtn = $('<a href="#play" class="play" title="' + this.playLinkText + '">' + '<img src="' + this.playLinkIcon + '" height="35" width="35" alt="' + this.playLinkText + '"/>' + '</a>');
}
playBtn.click(function (e) {
gallery.toggleSlideshow();
e.preventDefault();
});
prevBtn = $('<a class="prev" href="#" title="' + this.prevLinkText + '">' + '<img src="' + this.prevLinkIcon + '" height="35" width="35" alt="' + this.prevLinkText + '"/>' + '</a>');
nextBtn = $('<a class="next" href="#" title="' + this.nextLinkText + '">' + '<img src="' + this.nextLinkIcon + '" height="35" width="35" alt="' + this.nextLinkText + '"/>' + '</a>');
prevBtn.click(function (e) {
gallery.previous();
e.preventDefault();
});
nextBtn.click(function (e) {
gallery.next();
e.preventDefault();
});
navCont = $('<div class="nav-controls">');
navCont.hover( function() { $(this).fadeTo('fast', 0.75) }, function() { $(this).fadeTo('fast', 0.4) });
this.$controlsContainer.append( navCont.append(prevBtn, playBtn, nextBtn));
// Option icons
this.$continueKey = $('<a>', { 'class': 'continue-key-insert', href: '#', title: gallery.continueKeyInsertBtn })
.click( function (e) {
e.preventDefault();
var ckey = prompt(gallery.continueKeyInsert, gallery.cont ? gallery.cont : '' );
ckey = $.trim(ckey);
if (gallery.continueKeyPattern.test(ckey)) {
gallery.cont = ckey;
} else {
alert(gallery.continueKeyInvalid);
}
});
var $setDelay = $('<a>', { 'class': 'delay-insert', href: '#', title: gallery.delayInsertBtn })
.click( function (e) {
e.preventDefault();
var delay = prompt(gallery.delayInsert, gallery.delay ? gallery.delay : '' );
if (!delay) return;
delay = $.trim( delay.replace(/ms|s/, '') );
if (/^\d+$/.test(delay)) {
if (delay > 2499) {
gallery.delay = delay;
return;
} else if (3 < delay < 61) {
gallery.delay = delay * 1000;
return;
}
}
alert(gallery.delayInvalid);
});
var $helpLink = $('<a>', { 'class': 'gs-help-link', href: mw.util.getUrl('Help:Slideshow'), title: gallery.helpLinkTitle, target: '_blank' });
var otherCont = $('<div>', { 'class' : 'other-controls'} );
this.$controlsContainer.append( otherCont.append( this.$continueKey, $setDelay, $helpLink ) );
otherCont.hover( function() { $(this).fadeTo('fast', 1) }, function() { $(this).fadeTo('fast', 0.6) });
// Setup Keyboard Navigation
if (this.enableKeyboardNavigation) {
var hidingEnabled = true;
$(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;
}
});
$(document).keyup(function (e) {
var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
//Hide on escape
if ($('#SlideContainer').height() && key == 27) gallery.toggleVisibility();
});
}
// Auto start the slideshow
if (this.autoPlay) this.play();
// Kickoff Image Preloader after 1 second
setTimeout(function () {
gallery.preloadInit();
}, 1000);
$(document).trigger('slideshow', ['shown', this]); // For external scripts
},
start: function () {
$(document).trigger('slideshow', ['starting', this]); // For external scripts
$('#GallerySlideInit').hide();
$('#GallerySlideContinue').hide();
$('#SlideContainer').animate({
height: '100%'
});
//Once done, hide scrollbar
$('body').css('overflow', 'hidden');
// Settings from URL
var autoPlay = mw.util.getParamValue('gsAutoPlay');
if (autoPlay) {
if ('1' === autoPlay || 'true' === autoPlay || 'yes' === autoPlay || '-1' === autoPlay) {
this.autoPlay = true;
} else {
this.autoPlay = false;
}
}
var delay = mw.util.getParamValue('gsDelay');
if (delay) {
if (/^\d+$/.test(delay)) {
if (delay > 2499) {
this.delay = delay;
} else if (3 < delay < 61) {
this.delay = delay * 1000;
}
}
}
var cmdir = mw.util.getParamValue('gsDir');
if (cmdir) {
if ('climbing' === cmdir || 'ascending' === cmdir || 'asc' === cmdir || '123' === cmdir || 'rising' === cmdir) {
this.cmdir = 'asc';
} else {
this.cmdir = 'desc';
}
}
var cmcontinue = mw.util.getParamValue('gsContinue');
if (cmcontinue) {
if (this.continueKeyPattern.test(cmcontinue)) {
this.cont = cmcontinue;
} else {
this.cont = '';
}
}
this.queryApi();
setTimeout( function () {
window.location.hash = "#SlideContainer";
}, 2000); // For IE 6
},
toggleVisibility: function () {
$('#GallerySlideInit').toggle().unbind('click').click(GallerySlide.toggleVisibility);
$('#SlideContainer').slideToggle();
$('body').css('overflow', 'visible');
$(document).trigger('slideshow', ['visibility', GallerySlide]); // For external scripts
},
queryFile: function ( title ) {
params = {
action: 'render',
title: title
};
$.ajax({
url: this.indexURL,
cache: true,
dataType: 'html',
data: params,
type: 'GET',
success: function (result, status, x) {
GallerySlide.processDetails(result, title);
}
});
},
processDetails: function ( result, title ) {
if ('string' !== typeof result) return;
var i,
dItem,
dDescription,
$node,
parsedDOM = $(result);
// dTitle = result.match(/File\:(.+\..{3,5})\&/)[1].replace(/_/g, ' '); // superflous? title ever right?
dDescription = $('<div>').append(
$('#fileinfotpl_desc', parsedDOM).siblings().eq(0).contents(),
$('#fileinfotpl_aut', parsedDOM).siblings().eq(0).contents(),
$('#fileinfotpl_date', parsedDOM).siblings().eq(0).contents()
).html();
if (!dDescription) dDescription = 'Unable to determine description.';
for (i in this.data) {
dItem = this.data[i];
if (dItem.title === title) {
dItem.description = dDescription;
$node = $('#desc' + i);
if ($node.length !== 0) $node.html(dDescription);
}
}
},
queryApi: function () {
$(document).trigger('slideshow', ['beforeQuery', this]); // For external scripts
params = {
action: 'query',
rawcontinue: '',
generator: 'categorymembers',
gcmtitle: mw.config.get('wgPageName'),
gcmlimit: Math.floor($('#SlideContainer').width() / 81) + 1,
gcmtype: 'file',
gcmdir: this.cmdir,
prop: 'imageinfo|categories',
clprop: 'sortkey|hidden',
cllimit: 500,
iiprop: 'url|user|size',
iilimit: 500,
iiurlwidth: this.maxImageWidth,
iiurlheight: this.maxImageHeight,
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) {
$(document).trigger('slideshow', ['afterQuery', this]); // For external scripts
var pages = result.query.pages,
data = [],
i = 0;
if (result['query-continue']) {
if ('undefined' !== typeof this.cont) {
this.contOld = this.cont;
}
this.cont = result['query-continue'].categorymembers.gcmcontinue;
}
else this.cont = false;
// Fromatt a number
var fm = function( iNr ) {
iNr += '';
var rx = /(\d+)(\d{3})/;
while (rx.test(iNr)) {
iNr = iNr.replace(rx, '$1' + '<span class="digit-separator"> </span>' + '$2');
}
return iNr;
};
for (var id in pages) {
var v = pages[id],
r = v.imageinfo[v.imageinfo.length - 1],
rc = v.imageinfo[0],
n = data[i] = {},
sortkey = '',
$cats = $('<div>', { 'class': 'cat-wrap' }),
$licenses = $('<div>', { 'class': 'license-wrap' }).text(this.licenseLabel);
// Process categories; Extract visible cats, sortkey for current cat, licenses
processCats:
for (var c in v.categories) {
var tCat = v.categories[c];
if (tCat.title === mw.config.get('wgPageName').replace(/_/g, ' ')) sortkey = 'file' + '|' + tCat.sortkey + '|' + id;
if ('undefined' === typeof tCat.hidden) {
$cats.append( $('<a>', { 'class': 'cat-label', href: mw.util.getUrl(tCat.title), target: '_blank' }).text(tCat.title.replace('Category:', '')), ' ' );
} else {
for (var recogID in this.licenseRecognization) {
if (this.licenseRecognization[recogID][0].test(tCat.title)) {
$licenses.append( $('<span>', { 'class': 'license-label', title: tCat.title.replace('Category:', '') }).html(this.licenseRecognization[recogID][1]), ' ' );
continue processCats;
}
}
}
}
n.title = v.title;
n['link'] = rc.descriptionurl;
n.slideUrl = rc.thumburl;
n.width = rc.thumbwidth;
n.height = rc.thumbheight;
n.oWidth = fm(r.width);
n.oHeight = fm(r.height);
n.oSize = fm(r.size >> 10) + ' <abbr title="1 KibiByte= 1024 Bytes">KiB</abbr>';
n.$user = $('<span>').text('Uploader: ').append(
$('<a>', { href: mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[2] + ':' + r.user), target: '_blank' }).text(r.user),
' (',
$('<a>', { href: mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[3] + ':' + r.user), target: '_blank' }).text('talk'),
')'
);
n.$cats = $cats;
n.$licenses = $licenses;
n.contKey = (sortkey || this.contOld);
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);
$(document).ready(function () {
if ($('.gallery li').length < 2) return; // no need for a gallery with a few images
mw.loader.using('mediawiki.cookie', function () {
$('body').append('<div id="SlideContainer"></div>');
GallerySlide = $('#SlideContainer').galleriffic();
$('#mw-category-media h2').after('<span id="GallerySlideInit">Show Slideshow</span>');
$('#GallerySlideInit').click(function () {
GallerySlide.start();
});
var lastQuery = mw.cookie.get( 'gs' + mw.config.get('wgPageName').replace('Category:', '') );
if (lastQuery) {
$('#mw-category-media h2').after($('<span>', { id: 'GallerySlideContinue' }).text('Continue Slideshow').click( function () {
if (!GallerySlide.cont) GallerySlide.cont = lastQuery;
GallerySlide.start();
}
));
}
$(document).trigger('slideshow', ['loaded', GallerySlide]); // For external scripts
var autoStart = mw.util.getParamValue('gsAutoStart');
if (autoStart) {
if ('1' === autoStart || 'true' === autoStart || 'yes' === autoStart || '-1' === autoStart) {
$('#GallerySlideInit').click();
}
}
});
});
}