2013-08-07 08:59:08 +00:00
|
|
|
/*
|
|
|
|
* This file is part of the MediaWiki extension MultimediaViewer.
|
|
|
|
*
|
|
|
|
* MultimediaViewer is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* MultimediaViewer is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2014-02-07 14:47:00 +00:00
|
|
|
( function ( mw, $ ) {
|
2013-11-13 23:01:16 +00:00
|
|
|
var MultiLightbox, lightboxHooks, MMVP,
|
2013-11-25 23:51:40 +00:00
|
|
|
|
|
|
|
comingFromPopstate = false,
|
|
|
|
|
|
|
|
imgsSelector = '.gallery .image img, a.image img',
|
|
|
|
|
2013-10-17 23:38:17 +00:00
|
|
|
validExtensions = {
|
|
|
|
'jpg': true,
|
|
|
|
'jpeg': true,
|
|
|
|
'gif': true,
|
|
|
|
'svg': true,
|
|
|
|
'png': true,
|
|
|
|
'tiff': true,
|
|
|
|
'tif': true
|
2013-10-16 00:28:07 +00:00
|
|
|
};
|
2013-08-07 08:59:08 +00:00
|
|
|
|
2013-11-27 21:57:45 +00:00
|
|
|
/**
|
2014-01-11 00:11:37 +00:00
|
|
|
* @class mw.MultimediaViewer
|
|
|
|
* Analyses the page, looks for image content and sets up the hooks
|
2013-11-27 21:57:45 +00:00
|
|
|
* to manage the viewing experience of such content.
|
|
|
|
* @constructor
|
|
|
|
*/
|
2013-08-07 08:59:08 +00:00
|
|
|
function MultimediaViewer() {
|
2013-11-27 21:57:45 +00:00
|
|
|
/**
|
|
|
|
* MultiLightbox object used to display the pictures in the page.
|
2014-01-11 00:11:37 +00:00
|
|
|
* @property {mlb.MultiLightbox}
|
2013-11-27 21:57:45 +00:00
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this.lightbox = null;
|
|
|
|
|
2013-12-31 00:19:20 +00:00
|
|
|
/**
|
|
|
|
* Whether we've fired an animation for the metadata div.
|
|
|
|
* @property {boolean}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this.hasAnimatedMetadata = false;
|
|
|
|
|
2013-11-27 21:57:45 +00:00
|
|
|
var $thumbs = $( imgsSelector ),
|
2013-08-07 08:59:08 +00:00
|
|
|
urls = [],
|
|
|
|
viewer = this;
|
|
|
|
|
2013-11-13 21:16:49 +00:00
|
|
|
/**
|
2014-02-06 23:18:47 +00:00
|
|
|
* @property {mw.Api}
|
2013-11-13 21:16:49 +00:00
|
|
|
* @private
|
|
|
|
*/
|
2014-02-06 23:18:47 +00:00
|
|
|
this.api = new mw.Api();
|
2013-11-13 21:16:49 +00:00
|
|
|
|
2013-11-27 21:57:45 +00:00
|
|
|
/**
|
2014-02-13 09:52:40 +00:00
|
|
|
* @property {mw.mmv.provider.Image}
|
2013-11-27 21:57:45 +00:00
|
|
|
* @private
|
|
|
|
*/
|
2014-02-13 09:52:40 +00:00
|
|
|
this.imageProvider = new mw.mmv.provider.Image();
|
2013-11-27 21:57:45 +00:00
|
|
|
|
2014-02-03 20:42:17 +00:00
|
|
|
/**
|
|
|
|
* @property {mw.mmv.provider.ImageInfo}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this.imageInfoProvider = new mw.mmv.provider.ImageInfo( this.api, {
|
|
|
|
// Short-circuit, don't fallback, to save some tiny amount of time
|
|
|
|
language: mw.config.get( 'wgUserLanguage', false ) || mw.config.get( 'wgContentLanguage', 'en' )
|
|
|
|
} );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @property {mw.mmv.provider.FileRepoInfo}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this.fileRepoInfoProvider = new mw.mmv.provider.FileRepoInfo( this.api );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @property {mw.mmv.provider.ThumbnailInfo}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this.thumbnailInfoProvider = new mw.mmv.provider.ThumbnailInfo( this.api );
|
|
|
|
|
2014-02-06 00:12:50 +00:00
|
|
|
/**
|
|
|
|
* @property {mw.mmv.provider.UserInfo}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this.userInfoProvider = new mw.mmv.provider.UserInfo( this.api );
|
|
|
|
|
2014-01-30 11:28:43 +00:00
|
|
|
/**
|
2014-01-31 00:06:33 +00:00
|
|
|
* @property {mw.mmv.provider.ImageUsage}
|
2014-01-30 11:28:43 +00:00
|
|
|
* @private
|
|
|
|
*/
|
2014-02-03 20:42:17 +00:00
|
|
|
this.imageUsageProvider = new mw.mmv.provider.ImageUsage( this.api );
|
2014-01-30 11:28:43 +00:00
|
|
|
|
|
|
|
/**
|
2014-01-31 00:06:33 +00:00
|
|
|
* @property {mw.mmv.provider.GlobalUsage}
|
2014-01-30 11:28:43 +00:00
|
|
|
* @private
|
|
|
|
*/
|
2014-02-03 20:42:17 +00:00
|
|
|
this.globalUsageProvider = new mw.mmv.provider.GlobalUsage( this.api, {
|
2014-01-30 11:28:43 +00:00
|
|
|
doNotUseApi: !mw.config.get( 'wgMultimediaViewer' ).globalUsageAvailable
|
|
|
|
} );
|
|
|
|
// replace with this one to test global usage on a local wiki without going through all the
|
|
|
|
// hassle required for installing the extension:
|
2014-02-03 20:42:17 +00:00
|
|
|
//this.globalUsageProvider = new mw.mmv.provider.GlobalUsage(
|
2014-01-30 11:28:43 +00:00
|
|
|
// new mw.Api( {ajax: { url: 'http://commons.wikimedia.org/w/api.php', dataType: 'jsonp' } } )
|
|
|
|
//);
|
|
|
|
|
2014-02-06 16:17:38 +00:00
|
|
|
/**
|
|
|
|
* @property {mw.mmv.performance}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this.performance = new mw.mmv.performance();
|
|
|
|
|
2013-11-27 21:57:45 +00:00
|
|
|
// Traverse DOM, looking for potential thumbnails
|
2013-08-07 08:59:08 +00:00
|
|
|
$thumbs.each( function ( i, thumb ) {
|
2013-12-10 19:54:07 +00:00
|
|
|
var thisImage, $thumbCaption, caption,
|
2013-09-26 01:47:59 +00:00
|
|
|
$thumb = $( thumb ),
|
2013-08-07 08:59:08 +00:00
|
|
|
$link = $thumb.closest( 'a.image' ),
|
|
|
|
$thumbContain = $link.closest( '.thumb' ),
|
|
|
|
$enlarge = $thumbContain.find( '.magnify a' ),
|
|
|
|
$links = $link.add( $enlarge ),
|
|
|
|
filePageLink = $link.prop( 'href' ),
|
2013-09-26 01:47:59 +00:00
|
|
|
fileTitle = mw.Title.newFromImg( $thumb ),
|
|
|
|
index = urls.length;
|
2013-08-07 08:59:08 +00:00
|
|
|
|
2013-10-17 23:38:17 +00:00
|
|
|
if ( !validExtensions[fileTitle.getExtension().toLowerCase()] ) {
|
|
|
|
// Not a valid extension, skip this one
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-25 23:51:40 +00:00
|
|
|
if ( $thumbContain.length === 0 ) {
|
|
|
|
// This isn't a thumbnail! Just use the link.
|
|
|
|
$thumbContain = $link;
|
|
|
|
} else if ( $thumbContain.is( '.thumb' ) ) {
|
2013-12-10 19:54:07 +00:00
|
|
|
$thumbCaption = $thumbContain.find( '.thumbcaption' ).clone();
|
|
|
|
$thumbCaption.find( '.magnify' ).remove();
|
|
|
|
viewer.whitelistHtml( $thumbCaption );
|
|
|
|
caption = $thumbCaption.html();
|
2013-11-25 23:51:40 +00:00
|
|
|
$thumbContain = $thumbContain.find( '.image' );
|
|
|
|
}
|
|
|
|
|
2013-08-07 08:59:08 +00:00
|
|
|
$links.data( 'filePageLink', filePageLink );
|
2013-11-25 23:51:40 +00:00
|
|
|
|
2013-11-27 21:57:45 +00:00
|
|
|
// Create a LightboxImage object for each legit image
|
2013-12-10 19:54:07 +00:00
|
|
|
thisImage = viewer.createNewImage( $thumb.prop( 'src' ), filePageLink, fileTitle, index, thumb, caption );
|
2013-11-13 21:16:49 +00:00
|
|
|
|
2013-11-25 23:51:40 +00:00
|
|
|
urls.push( thisImage );
|
2013-08-07 08:59:08 +00:00
|
|
|
|
2013-11-27 21:57:45 +00:00
|
|
|
// Register callback that launches modal image viewer if valid click
|
2013-08-07 08:59:08 +00:00
|
|
|
$links.click( function ( e ) {
|
2013-12-16 18:24:17 +00:00
|
|
|
return viewer.clickLinkCallback( e, this, $thumbContain, thisImage );
|
2013-08-07 08:59:08 +00:00
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
2013-11-27 21:57:45 +00:00
|
|
|
if ( urls.length === 0 ) {
|
|
|
|
// No legit images found, no need to continue
|
|
|
|
return;
|
2013-08-07 08:59:08 +00:00
|
|
|
}
|
|
|
|
|
2013-11-27 21:57:45 +00:00
|
|
|
// Only if we find legit images, create a MultiLightbox object
|
2014-01-06 21:53:42 +00:00
|
|
|
this.lightbox = new mw.MultiLightbox( urls, 0, mw.LightboxInterface, this );
|
2013-11-27 21:57:45 +00:00
|
|
|
|
|
|
|
// Register various event hooks. TODO: Make this a function that's only called once.
|
|
|
|
|
2013-11-13 00:43:46 +00:00
|
|
|
lightboxHooks.register( 'closeInterface', function () {
|
|
|
|
$( document.body ).removeClass( 'mw-mlb-lightbox-open' );
|
2013-11-25 23:51:40 +00:00
|
|
|
if ( comingFromPopstate === false ) {
|
|
|
|
history.pushState( {}, '', '#' );
|
|
|
|
} else {
|
|
|
|
comingFromPopstate = false;
|
|
|
|
}
|
2013-12-31 00:19:20 +00:00
|
|
|
|
|
|
|
viewer.hasAnimatedMetadata = false;
|
2014-01-06 21:53:42 +00:00
|
|
|
viewer.isOpen = false;
|
2013-11-13 00:43:46 +00:00
|
|
|
} );
|
|
|
|
|
2013-10-28 22:32:56 +00:00
|
|
|
lightboxHooks.register( 'imageResize', function () {
|
2013-11-26 03:37:01 +00:00
|
|
|
var ui = this;
|
|
|
|
viewer.resize( ui );
|
2013-10-28 22:32:56 +00:00
|
|
|
return false;
|
|
|
|
} );
|
2014-02-11 01:12:31 +00:00
|
|
|
|
|
|
|
this.setupEventHandlers();
|
2013-11-06 00:36:06 +00:00
|
|
|
}
|
2013-10-29 20:26:07 +00:00
|
|
|
|
2013-11-06 00:36:06 +00:00
|
|
|
MMVP = MultimediaViewer.prototype;
|
2013-10-29 20:26:07 +00:00
|
|
|
|
2014-01-22 01:21:22 +00:00
|
|
|
// TODO FIXME HACK delete this when other UI elements have been shifted away.
|
|
|
|
MMVP.whitelistHtml = mw.mmv.ui.Element.prototype.whitelistHtml;
|
2013-12-10 19:54:07 +00:00
|
|
|
|
2013-12-02 19:46:52 +00:00
|
|
|
/**
|
|
|
|
* Create an image object for the lightbox to use.
|
|
|
|
* @protected
|
|
|
|
* @param {string} fileLink Link to the file - generally a thumb URL
|
|
|
|
* @param {string} filePageLink Link to the File: page
|
|
|
|
* @param {mw.Title} fileTitle Represents the File: page
|
|
|
|
* @param {number} index Which number file this is
|
2013-11-13 21:16:49 +00:00
|
|
|
* @param {HTMLImageElement} thumb The thumbnail that represents this image on the page
|
2013-12-10 19:54:07 +00:00
|
|
|
* @param {string} [caption] The caption, if any.
|
2013-12-02 19:46:52 +00:00
|
|
|
* @returns {mw.LightboxImage}
|
|
|
|
*/
|
2013-12-10 19:54:07 +00:00
|
|
|
MMVP.createNewImage = function ( fileLink, filePageLink, fileTitle, index, thumb, caption ) {
|
|
|
|
var thisImage = new mw.LightboxImage( fileLink, filePageLink, fileTitle, index, thumb, caption );
|
2013-12-02 19:46:52 +00:00
|
|
|
thisImage.filePageLink = filePageLink;
|
|
|
|
thisImage.filePageTitle = fileTitle;
|
|
|
|
thisImage.index = index;
|
2013-11-13 21:16:49 +00:00
|
|
|
thisImage.thumbnail = thumb;
|
2013-12-02 19:46:52 +00:00
|
|
|
|
|
|
|
return thisImage;
|
|
|
|
};
|
|
|
|
|
2013-11-27 21:57:45 +00:00
|
|
|
/**
|
|
|
|
* Handles clicks on legit image links.
|
|
|
|
*
|
|
|
|
* @protected
|
|
|
|
*
|
|
|
|
* @param {jQuery.Event} e click event
|
|
|
|
* @param {HTMLElement|jQuery} clickedEle clicked element
|
|
|
|
* @param {jQuery} $thumbContain thumbnail container element
|
|
|
|
* @param {mw.LightboxImage} thisImage lightboximage object
|
|
|
|
*/
|
|
|
|
MMVP.clickLinkCallback = function ( e, clickedEle, $thumbContain, thisImage ) {
|
|
|
|
// Do not interfere with non-left clicks or if modifier keys are pressed.
|
|
|
|
if ( e.which !== 1 || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var $clickedEle = $( clickedEle ),
|
2014-02-13 09:52:40 +00:00
|
|
|
initial = $thumbContain.find( 'img' ).clone()[0];
|
2013-11-27 21:57:45 +00:00
|
|
|
|
|
|
|
if ( $clickedEle.is( 'a.image' ) ) {
|
2014-02-07 14:47:00 +00:00
|
|
|
mw.mmv.logger.log( 'thumbnail-link-click' );
|
2013-11-27 21:57:45 +00:00
|
|
|
} else if ( $clickedEle.is( '.magnify a' ) ) {
|
2014-02-07 14:47:00 +00:00
|
|
|
mw.mmv.logger.log( 'enlarge-link-click' );
|
2013-11-27 21:57:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
this.loadImage( thisImage, initial );
|
2013-12-16 18:24:17 +00:00
|
|
|
|
|
|
|
return false;
|
2013-11-27 21:57:45 +00:00
|
|
|
};
|
|
|
|
|
2013-12-03 01:58:09 +00:00
|
|
|
/**
|
|
|
|
* Handles resize events in viewer.
|
|
|
|
*
|
|
|
|
* @protected
|
|
|
|
*
|
|
|
|
* @param {mw.LightboxInterface} ui lightbox that got resized
|
|
|
|
*/
|
2013-11-26 03:37:01 +00:00
|
|
|
MMVP.resize = function ( ui ) {
|
2013-12-03 01:58:09 +00:00
|
|
|
var viewer = this,
|
2014-02-05 01:06:10 +00:00
|
|
|
fileTitle = this.currentImageFileTitle,
|
|
|
|
imageWidths;
|
2013-11-13 21:16:49 +00:00
|
|
|
|
2014-02-13 09:52:40 +00:00
|
|
|
this.preloadThumbnails();
|
|
|
|
|
2014-01-30 07:22:26 +00:00
|
|
|
if ( fileTitle ) {
|
2014-02-13 09:52:40 +00:00
|
|
|
imageWidths = ui.getCurrentImageWidths();
|
|
|
|
this.fetchThumbnail(
|
|
|
|
fileTitle, imageWidths.real
|
|
|
|
).then( function( thumbnail, image ) {
|
|
|
|
viewer.setImage( ui, thumbnail, image, imageWidths );
|
2014-01-30 07:22:26 +00:00
|
|
|
} );
|
|
|
|
}
|
2014-01-06 21:53:42 +00:00
|
|
|
|
|
|
|
this.updateControls();
|
2013-12-03 01:58:09 +00:00
|
|
|
};
|
|
|
|
|
2013-11-13 00:43:46 +00:00
|
|
|
MMVP.updateControls = function () {
|
2014-02-03 11:23:31 +00:00
|
|
|
var numImages = this.lightbox.images ? this.lightbox.images.length : 0,
|
|
|
|
showNextButton = this.lightbox.currentIndex < (numImages - 1),
|
|
|
|
showPreviousButton = this.lightbox.currentIndex > 0;
|
2013-11-13 00:43:46 +00:00
|
|
|
|
2014-02-03 11:23:31 +00:00
|
|
|
this.ui.updateControls( showNextButton, showPreviousButton );
|
2013-11-13 00:43:46 +00:00
|
|
|
};
|
2013-11-06 00:36:06 +00:00
|
|
|
|
2014-01-28 23:57:05 +00:00
|
|
|
/**
|
|
|
|
* @method
|
2014-02-13 09:52:40 +00:00
|
|
|
* Loads and sets the specified image. It also updates the controls.
|
2014-01-28 23:57:05 +00:00
|
|
|
*
|
|
|
|
* @param {mw.LightboxInterface} ui image container
|
2014-02-13 09:52:40 +00:00
|
|
|
* @param {mw.mmv.model.Thumbnail} thumbnail thumbnail information
|
|
|
|
* @param {HTMLImageElement} image
|
2014-02-06 23:18:47 +00:00
|
|
|
* @param {mw.mmv.model.ThumbnailWidth} imageWidths
|
2014-01-28 23:57:05 +00:00
|
|
|
*/
|
2014-02-13 09:52:40 +00:00
|
|
|
MMVP.setImage = function ( ui, thumbnail, image, imageWidths ) {
|
|
|
|
// we downscale larger images but do not scale up smaller ones, that would look ugly
|
|
|
|
if ( thumbnail.width > imageWidths.screen ) {
|
|
|
|
image.width = imageWidths.css;
|
2014-02-06 23:18:47 +00:00
|
|
|
}
|
2014-01-28 23:57:05 +00:00
|
|
|
|
2014-02-13 09:52:40 +00:00
|
|
|
ui.replaceImageWith( image );
|
|
|
|
this.updateControls();
|
2014-01-28 23:57:05 +00:00
|
|
|
};
|
|
|
|
|
2013-12-10 19:54:07 +00:00
|
|
|
/**
|
|
|
|
* @method
|
|
|
|
* Loads a specified image.
|
|
|
|
* @param {mw.LightboxImage} image
|
2014-02-13 09:52:40 +00:00
|
|
|
* @param {HTMLImageElement} initialImage A thumbnail to use as placeholder while the image loads
|
2013-12-10 19:54:07 +00:00
|
|
|
*/
|
2014-02-13 09:52:40 +00:00
|
|
|
MMVP.loadImage = function ( image, initialImage ) {
|
2014-02-06 23:18:47 +00:00
|
|
|
var imageWidths,
|
2014-02-13 09:52:40 +00:00
|
|
|
viewer = this,
|
|
|
|
imagePromise,
|
|
|
|
metadataPromise;
|
|
|
|
|
|
|
|
// FIXME dependency injection happens in completely random order and location, needs cleanup
|
|
|
|
this.ui = this.lightbox.iface;
|
2013-11-25 23:51:40 +00:00
|
|
|
|
|
|
|
this.lightbox.currentIndex = image.index;
|
|
|
|
|
|
|
|
this.currentImageFilename = image.filePageTitle.getPrefixedText();
|
2014-01-06 20:02:39 +00:00
|
|
|
this.currentImageFileTitle = image.filePageTitle;
|
2013-11-25 23:51:40 +00:00
|
|
|
this.lightbox.iface.comingFromPopstate = comingFromPopstate;
|
2014-01-06 21:53:42 +00:00
|
|
|
|
|
|
|
if ( !this.isOpen ) {
|
|
|
|
this.lightbox.open();
|
|
|
|
this.isOpen = true;
|
|
|
|
} else {
|
|
|
|
this.lightbox.iface.empty();
|
|
|
|
}
|
2014-02-13 09:52:40 +00:00
|
|
|
this.lightbox.iface.setupForLoad();
|
|
|
|
this.lightbox.iface.showImage( image, initialImage );
|
|
|
|
|
|
|
|
this.preloadImagesMetadata();
|
|
|
|
this.preloadThumbnails();
|
2014-01-06 21:53:42 +00:00
|
|
|
|
2013-11-25 23:51:40 +00:00
|
|
|
$( document.body ).addClass( 'mw-mlb-lightbox-open' );
|
|
|
|
|
2014-02-13 09:52:40 +00:00
|
|
|
imageWidths = this.ui.getCurrentImageWidths();
|
|
|
|
imagePromise = this.fetchThumbnail(
|
2014-02-06 23:18:47 +00:00
|
|
|
image.filePageTitle, imageWidths.real
|
2014-02-13 09:52:40 +00:00
|
|
|
).done( function( thumbnail, image ) {
|
|
|
|
viewer.setImage( viewer.lightbox.iface, thumbnail, image, imageWidths );
|
|
|
|
viewer.lightbox.iface.$imageDiv.removeClass( 'empty' );
|
|
|
|
} );
|
|
|
|
|
|
|
|
metadataPromise = this.fetchSizeIndependentLightboxInfo(
|
|
|
|
image.filePageTitle
|
|
|
|
).done( function ( imageInfo, repoInfo, localUsage, globalUsage, userInfo ) {
|
|
|
|
viewer.lightbox.iface.panel.setImageInfo(image, imageInfo, repoInfo,
|
|
|
|
localUsage, globalUsage, userInfo );
|
|
|
|
} );
|
2014-01-28 23:57:05 +00:00
|
|
|
|
2014-02-13 09:52:40 +00:00
|
|
|
$.when( imagePromise, metadataPromise ).then( function() {
|
2014-01-23 00:38:01 +00:00
|
|
|
viewer.stopListeningToScroll();
|
|
|
|
viewer.animateMetadataDivOnce()
|
|
|
|
// We need to wait until the animation is finished before we listen to scroll
|
|
|
|
.then( function() { viewer.startListeningToScroll(); } );
|
2014-02-13 09:52:40 +00:00
|
|
|
} );
|
2013-11-25 23:51:40 +00:00
|
|
|
|
2014-02-13 09:52:40 +00:00
|
|
|
comingFromPopstate = false;
|
|
|
|
};
|
2013-12-10 21:04:45 +00:00
|
|
|
|
2014-02-13 09:52:40 +00:00
|
|
|
/**
|
|
|
|
* Preload this many prev/next images to speed up navigation.
|
|
|
|
* (E.g. preloadDistance = 3 means that the previous 3 and the next 3 images will be loaded.)
|
|
|
|
* Preloading only happens when the viewer is open.
|
|
|
|
* @property {number}
|
|
|
|
*/
|
|
|
|
MMVP.preloadDistance = 1;
|
2014-02-07 14:47:00 +00:00
|
|
|
|
2014-02-13 09:52:40 +00:00
|
|
|
/**
|
|
|
|
* Stores image metadata preloads, so they can be cancelled.
|
|
|
|
* @type {mw.mmv.model.TaskQueue}
|
|
|
|
*/
|
|
|
|
MMVP.metadataPreloadQueue = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stores image thumbnail preloads, so they can be cancelled.
|
|
|
|
* @type {mw.mmv.model.TaskQueue}
|
|
|
|
*/
|
|
|
|
MMVP.thumbnailPreloadQueue = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Orders lightboximage indexes for preloading. Works similar to $.each, except it only takes
|
|
|
|
* the callback argument. Calls the callback with each lightboximage index in some sequence
|
|
|
|
* that is ideal for preloading.
|
|
|
|
* @private
|
|
|
|
* @param {function(number, mw.LightboxImage)} callback
|
|
|
|
*/
|
|
|
|
MMVP.eachPrealoadableLightboxIndex = function( callback ) {
|
|
|
|
for ( var i = 0; i <= this.preloadDistance; i++ ) {
|
|
|
|
if ( this.lightbox.currentIndex + i < this.lightbox.images.length ) {
|
|
|
|
callback(
|
|
|
|
this.lightbox.currentIndex + i,
|
|
|
|
this.lightbox.images[this.lightbox.currentIndex + i]
|
|
|
|
);
|
2014-02-07 14:47:00 +00:00
|
|
|
}
|
2014-02-13 09:52:40 +00:00
|
|
|
if ( i && this.lightbox.currentIndex - i >= 0 ) { // skip duplicate for i==0
|
|
|
|
callback(
|
|
|
|
this.lightbox.currentIndex - i,
|
|
|
|
this.lightbox.images[this.lightbox.currentIndex - i]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A helper function to fill up the preload queues.
|
|
|
|
* taskFactory(lightboxImage) should return a preload task for the given lightboximage.
|
|
|
|
* @private
|
|
|
|
* @param {function(mw.LightboxImage): function()} taskFactory
|
|
|
|
* @return {mw.mmv.model.TaskQueue}
|
|
|
|
*/
|
|
|
|
MMVP.pushLightboxImagesIntoQueue = function( taskFactory ) {
|
|
|
|
var queue = new mw.mmv.model.TaskQueue();
|
|
|
|
|
|
|
|
this.eachPrealoadableLightboxIndex( function( i, lightboxImage ) {
|
|
|
|
queue.push( taskFactory( lightboxImage ) );
|
2013-11-25 23:51:40 +00:00
|
|
|
} );
|
|
|
|
|
2014-02-13 09:52:40 +00:00
|
|
|
return queue;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cancels in-progress image metadata preloading.
|
|
|
|
*/
|
|
|
|
MMVP.cancelImageMetadataPreloading = function() {
|
|
|
|
if ( this.metadataPreloadQueue ) {
|
|
|
|
this.metadataPreloadQueue.cancel();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cancels in-progress image thumbnail preloading.
|
|
|
|
*/
|
|
|
|
MMVP.cancelThumbnailsPreloading = function() {
|
|
|
|
if ( this.thumbnailPreloadQueue ) {
|
|
|
|
this.thumbnailPreloadQueue.cancel();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Preload metadata for next and prev N image (N = MMVP.preloadDistance).
|
|
|
|
* Two images will be loaded at a time (one forward, one backward), with closer images
|
|
|
|
* being loaded sooner.
|
|
|
|
*/
|
|
|
|
MMVP.preloadImagesMetadata = function() {
|
|
|
|
var viewer = this;
|
|
|
|
|
|
|
|
this.cancelImageMetadataPreloading();
|
|
|
|
|
|
|
|
this.metadataPreloadQueue = this.pushLightboxImagesIntoQueue( function( lightboxImage ) {
|
|
|
|
return function() {
|
|
|
|
return viewer.fetchSizeIndependentLightboxInfo( lightboxImage.filePageTitle );
|
|
|
|
};
|
|
|
|
} );
|
|
|
|
|
|
|
|
this.metadataPreloadQueue.execute();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Preload thumbnails for next and prev N image (N = MMVP.preloadDistance).
|
|
|
|
* Two images will be loaded at a time (one forward, one backward), with closer images
|
|
|
|
* being loaded sooner.
|
|
|
|
*/
|
|
|
|
MMVP.preloadThumbnails = function() {
|
|
|
|
var viewer = this,
|
|
|
|
ui = this.lightbox.iface;
|
|
|
|
|
|
|
|
this.cancelThumbnailsPreloading();
|
|
|
|
|
|
|
|
this.thumbnailPreloadQueue = this.pushLightboxImagesIntoQueue( function( lightboxImage ) {
|
|
|
|
return function() {
|
|
|
|
return viewer.fetchThumbnail(
|
|
|
|
lightboxImage.filePageTitle,
|
|
|
|
ui.getLightboxImageWidths( lightboxImage ).real
|
|
|
|
);
|
|
|
|
};
|
|
|
|
} );
|
|
|
|
|
|
|
|
this.thumbnailPreloadQueue.execute();
|
2013-11-25 23:51:40 +00:00
|
|
|
};
|
|
|
|
|
2014-01-23 00:38:01 +00:00
|
|
|
/**
|
|
|
|
* @method
|
|
|
|
* Animates the metadata area when the viewer is first opened.
|
|
|
|
* @return {jQuery.Promise} an empty promise which resolves when the animation is finished
|
|
|
|
*/
|
2014-01-07 19:58:29 +00:00
|
|
|
MMVP.animateMetadataDivOnce = function () {
|
|
|
|
if ( !this.hasAnimatedMetadata ) {
|
|
|
|
this.hasAnimatedMetadata = true;
|
2014-01-23 00:38:01 +00:00
|
|
|
$.scrollTo( 40, 400 )
|
|
|
|
.scrollTo( 0, 400 );
|
2014-01-07 19:58:29 +00:00
|
|
|
}
|
2014-01-23 00:38:01 +00:00
|
|
|
return $.scrollTo.window().promise();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @method
|
|
|
|
* Stop listening to the page's scroll events
|
|
|
|
*/
|
|
|
|
MMVP.stopListeningToScroll = function () {
|
|
|
|
$.scrollTo().off( 'scroll.mmvp' );
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @method
|
|
|
|
* Start listening to the page's scroll events
|
|
|
|
* Will call MMVP.scroll(), throttled so it is not triggered on every pixel.
|
|
|
|
*/
|
|
|
|
MMVP.startListeningToScroll = function () {
|
|
|
|
var viewer = this;
|
|
|
|
|
|
|
|
$.scrollTo().on( 'scroll.mmvp', $.throttle( 250, function() { viewer.scroll(); } ) );
|
|
|
|
|
|
|
|
// Trigger a check in case the user scrolled manually during the animation
|
|
|
|
viewer.scroll();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @method
|
|
|
|
* Receives the window's scroll events and flips the chevron if necessary.
|
|
|
|
*/
|
|
|
|
MMVP.scroll = function () {
|
2014-02-07 14:47:00 +00:00
|
|
|
this.ui.panel.$dragIcon.toggleClass( 'pointing-down', !!$.scrollTo().scrollTop() );
|
2013-12-31 00:19:20 +00:00
|
|
|
};
|
|
|
|
|
2014-01-06 20:02:39 +00:00
|
|
|
/**
|
2014-02-13 09:52:40 +00:00
|
|
|
* Loads all the size-independent information needed by the lightbox (image metadata, repo
|
|
|
|
* information, file usage, uploader data).
|
|
|
|
* @param {mw.Title} fileTitle Title of the file page for the image.
|
|
|
|
* @returns {jQuery.Promise.<mw.mmv.model.Image, mw.mmv.model.Repo, mw.mmv.model.FileUsage,
|
|
|
|
* mw.mmv.model.FileUsage, mw.mmv.model.User>}
|
2014-01-06 20:02:39 +00:00
|
|
|
*/
|
2014-02-13 09:52:40 +00:00
|
|
|
MMVP.fetchSizeIndependentLightboxInfo = function ( fileTitle ) {
|
|
|
|
var viewer = this,
|
|
|
|
imageInfoPromise = this.imageInfoProvider.get( fileTitle ),
|
|
|
|
repoInfoPromise = this.fileRepoInfoProvider.get( fileTitle ),
|
|
|
|
imageUsagePromise = this.imageUsageProvider.get( fileTitle ),
|
|
|
|
globalUsagePromise = this.globalUsageProvider.get( fileTitle ),
|
|
|
|
userInfoPromise;
|
|
|
|
|
|
|
|
userInfoPromise = $.when(
|
|
|
|
imageInfoPromise, repoInfoPromise
|
|
|
|
).then( function( imageInfo, repoInfoHash ) {
|
|
|
|
if ( imageInfo.lastUploader ) {
|
|
|
|
return viewer.userInfoProvider.get( imageInfo.lastUploader, repoInfoHash[imageInfo.repo] );
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
2014-02-03 20:42:17 +00:00
|
|
|
return $.when(
|
2014-02-13 09:52:40 +00:00
|
|
|
imageInfoPromise, repoInfoPromise, imageUsagePromise, globalUsagePromise, userInfoPromise
|
|
|
|
).then( function( imageInfo, repoInfoHash, imageUsage, globalUsage, userInfo ) {
|
|
|
|
return $.Deferred().resolve( imageInfo, repoInfoHash[imageInfo.repo], imageUsage, globalUsage, userInfo );
|
2014-02-03 20:42:17 +00:00
|
|
|
} );
|
2013-09-26 01:47:59 +00:00
|
|
|
};
|
|
|
|
|
2014-01-30 11:28:43 +00:00
|
|
|
/**
|
2014-02-13 09:52:40 +00:00
|
|
|
* Loads size-dependent components of a lightbox - the thumbnail model and the image itself.
|
|
|
|
* @param {mw.Title} fileTitle
|
|
|
|
* @param {number} width
|
|
|
|
* @returns {jQuery.Promise.<mw.mmv.model.Thumbnail, HTMLImageElement>}
|
2014-01-30 11:28:43 +00:00
|
|
|
*/
|
2014-02-13 09:52:40 +00:00
|
|
|
MMVP.fetchThumbnail = function ( fileTitle, width ) {
|
|
|
|
var viewer = this,
|
|
|
|
thumbnailPromise,
|
|
|
|
imagePromise;
|
|
|
|
|
|
|
|
thumbnailPromise = this.thumbnailInfoProvider.get( fileTitle, width );
|
|
|
|
imagePromise = thumbnailPromise.then( function( thumbnail ) {
|
|
|
|
return viewer.imageProvider.get( thumbnail.url );
|
2014-01-30 11:28:43 +00:00
|
|
|
} );
|
2014-02-13 09:52:40 +00:00
|
|
|
|
|
|
|
return $.when( thumbnailPromise, imagePromise );
|
2014-01-30 11:28:43 +00:00
|
|
|
};
|
|
|
|
|
2013-11-26 03:37:01 +00:00
|
|
|
MMVP.loadIndex = function ( index ) {
|
2014-02-13 09:52:40 +00:00
|
|
|
var $thumbnails = $( imgsSelector ).eq( index );
|
2013-11-26 03:37:01 +00:00
|
|
|
if ( index < this.lightbox.images.length && index >= 0 ) {
|
2014-02-13 09:52:40 +00:00
|
|
|
this.loadImage( this.lightbox.images[index], $thumbnails.clone()[0] );
|
2013-11-26 03:37:01 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
MMVP.nextImage = function () {
|
|
|
|
this.loadIndex( this.lightbox.currentIndex + 1 );
|
|
|
|
};
|
|
|
|
|
|
|
|
MMVP.prevImage = function () {
|
|
|
|
this.loadIndex( this.lightbox.currentIndex - 1 );
|
|
|
|
};
|
|
|
|
|
2014-02-11 01:12:31 +00:00
|
|
|
MMVP.setupEventHandlers = function() {
|
|
|
|
var viewer = this;
|
|
|
|
|
|
|
|
this.lightbox.iface.$imageWrapper.on( 'mmv-next', function () {
|
|
|
|
viewer.nextImage();
|
|
|
|
} );
|
|
|
|
|
|
|
|
this.lightbox.iface.$imageWrapper.on( 'mmv-prev', function () {
|
|
|
|
viewer.prevImage();
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
|
2014-01-31 09:29:39 +00:00
|
|
|
function handleHash() {
|
|
|
|
var statedIndex,
|
|
|
|
$foundElement,
|
|
|
|
hash = decodeURIComponent( document.location.hash ),
|
|
|
|
linkState = hash.split( '/' );
|
|
|
|
|
2013-11-25 23:51:40 +00:00
|
|
|
comingFromPopstate = true;
|
|
|
|
if ( linkState[0] === '#mediaviewer' ) {
|
|
|
|
statedIndex = mw.mediaViewer.lightbox.images[linkState[2]];
|
2014-01-31 09:29:39 +00:00
|
|
|
|
2013-11-25 23:51:40 +00:00
|
|
|
if ( statedIndex.filePageTitle.getPrefixedText() === linkState[1] ) {
|
2013-12-10 19:54:07 +00:00
|
|
|
$foundElement = $( imgsSelector ).eq( linkState[2] );
|
2014-02-13 09:52:40 +00:00
|
|
|
mw.mediaViewer.loadImage( statedIndex, $foundElement.clone()[0] );
|
2013-11-25 23:51:40 +00:00
|
|
|
}
|
|
|
|
} else {
|
2014-01-31 09:29:39 +00:00
|
|
|
// If the hash is invalid (not a mmv hash) we check if there's any mmv lightbox open and we close it
|
|
|
|
if ( mw.mediaViewer && mw.mediaViewer.lightbox && mw.mediaViewer.lightbox.iface ) {
|
2013-11-27 21:57:45 +00:00
|
|
|
mw.mediaViewer.lightbox.iface.unattach();
|
|
|
|
}
|
2013-11-25 23:51:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-07 08:59:08 +00:00
|
|
|
$( function () {
|
|
|
|
MultiLightbox = window.MultiLightbox;
|
|
|
|
lightboxHooks = window.lightboxHooks;
|
|
|
|
|
2014-02-03 11:23:31 +00:00
|
|
|
mw.mediaViewer = new MultimediaViewer();
|
2013-11-25 23:51:40 +00:00
|
|
|
|
2014-01-31 09:29:39 +00:00
|
|
|
handleHash();
|
|
|
|
window.addEventListener( 'popstate', handleHash );
|
2013-08-07 08:59:08 +00:00
|
|
|
} );
|
|
|
|
|
|
|
|
mw.MultimediaViewer = MultimediaViewer;
|
2014-02-07 14:47:00 +00:00
|
|
|
}( mediaWiki, jQuery ) );
|