diff --git a/MultimediaViewer.php b/MultimediaViewer.php
index 6df2b66c9..a7f6a08e5 100644
--- a/MultimediaViewer.php
+++ b/MultimediaViewer.php
@@ -86,6 +86,23 @@ $wgResourceModules['ext.multimediaViewer.lightboxinterface'] = array_merge( arra
),
), $moduleInfoMMV );
+$wgResourceModules['ext.multimediaViewer.dataModel'] = array_merge( array(
+ 'scripts' => array(
+ 'ext.multimediaViewer.dataModel.js',
+ ),
+
+ 'dependencies' => array(
+ 'ext.multimediaViewer.base',
+ 'oojs',
+ ),
+), $moduleInfoMMV );
+
+$wgResourceModules['ext.multimediaViewer.base'] = array_merge( array(
+ 'scripts' => array(
+ 'mmv.js',
+ ),
+), $moduleInfoMMV );
+
$wgResourceModules['ext.multimediaViewer'] = array_merge( array(
'scripts' => array(
'ext.multimediaViewer.js',
@@ -102,6 +119,7 @@ $wgResourceModules['ext.multimediaViewer'] = array_merge( array(
'mediawiki.Title',
'jquery.ui.dialog',
'jquery.hidpi',
+ 'ext.multimediaViewer.dataModel',
),
'messages' => array(
diff --git a/MultimediaViewerHooks.php b/MultimediaViewerHooks.php
index 3fa971a94..1c2e2b8db 100644
--- a/MultimediaViewerHooks.php
+++ b/MultimediaViewerHooks.php
@@ -118,6 +118,7 @@ class MultimediaViewerHooks {
$testModules['qunit']['ext.multimediaViewer.tests'] = array(
'scripts' => array(
'tests/qunit/ext.multimediaViewer.test.js',
+ 'tests/qunit/ext.multimediaViewer.dataModel.test.js',
'tests/qunit/ext.multimediaViewer.lightboxinterface.test.js',
'tests/qunit/lightboximage.test.js',
'tests/qunit/lightboxinterface.test.js',
diff --git a/resources/ext.multimediaViewer/ext.multimediaViewer.dataModel.js b/resources/ext.multimediaViewer/ext.multimediaViewer.dataModel.js
new file mode 100644
index 000000000..5753e9552
--- /dev/null
+++ b/resources/ext.multimediaViewer/ext.multimediaViewer.dataModel.js
@@ -0,0 +1,303 @@
+/*
+ * 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 .
+ */
+
+( function ( mw, oo ) {
+ /**
+ * @class mw.mmv.model.Image
+ * Represents information about a single image
+ * @constructor
+ * @param {mw.Title} title
+ * @param {number} size Filesize in bytes of the original image
+ * @param {number} width Width of the original image
+ * @param {number} height Height of the original image
+ * @param {string} mimeType
+ * @param {string} url URL to the image itself (original version)
+ * @param {string} descriptionUrl URL to the image description page
+ * @param {string} repo The repository this image belongs to
+ * @param {string} lastUploader The last person to upload a version of this image.
+ * @param {string} lastUploadDateTime The time and date the last upload occurred
+ * @param {string} originalUploadDateTime The time and date the original upload occurred
+ * @param {string} description
+ * @param {string} source
+ * @param {string} author
+ * @param {string} license
+ */
+ function Image(
+ title,
+ size,
+ width,
+ height,
+ mimeType,
+ url,
+ descriptionUrl,
+ repo,
+ lastUploader,
+ uploadDateTime,
+ creationDateTime,
+ description,
+ source,
+ author,
+ license
+ ) {
+ /** @property {mw.Title} title */
+ this.title = title;
+
+ /** @property {number} size */
+ this.size = size;
+
+ /** @property {number} width */
+ this.width = width;
+
+ /** @property {number} height */
+ this.height = height;
+
+ /** @property {string} mimeType */
+ this.mimeType = mimeType;
+
+ /** @property {string} url */
+ this.url = url;
+
+ /** @property {string} descriptionUrl */
+ this.descriptionUrl = descriptionUrl;
+
+ /** @property {string} repo */
+ this.repo = repo;
+
+ /** @property {string} lastUploader */
+ this.lastUploader = lastUploader;
+
+ /** @property {string} uploadDateTime */
+ this.uploadDateTime = uploadDateTime;
+
+ /** @property {string} creationDateTime */
+ this.creationDateTime = creationDateTime;
+
+ /** @property {string} description */
+ this.description = description;
+
+ /** @property {string} source */
+ this.source = source;
+
+ /** @property {string} author */
+ this.author = author;
+
+ /** @property {string} license */
+ this.license = license;
+
+ /**
+ * @property {object} thumbUrls
+ * An object indexed by image widths
+ * with URLs to appropriately sized thumbnails
+ */
+ this.thumbUrls = {};
+ }
+
+ /**
+ * @method
+ * @static
+ * Constructs a new Image object out of an object containing
+ * imageinfo data from an API response.
+ * @param {mw.Title} title
+ * @param {object} imageInfo
+ * @returns {mw.mmv.model.Image}
+ */
+ Image.newFromImageInfo = function ( title, imageInfo ) {
+ var uploadDateTime, creationDateTime, imageData,
+ description, source, author, license,
+ innerInfo = imageInfo.imageinfo[0],
+ extmeta = innerInfo.extmetadata;
+
+ if ( extmeta ) {
+ creationDateTime = extmeta.DateTimeOriginal;
+ uploadDateTime = extmeta.DateTime;
+
+ if ( uploadDateTime ) {
+ uploadDateTime = uploadDateTime.value.replace( /<.*?>/g, '' );
+ }
+
+ if ( creationDateTime ) {
+ creationDateTime = creationDateTime.value.replace( /<.*?>/g, '' );
+ }
+
+ description = extmeta.ImageDescription && extmeta.ImageDescription.value;
+ source = extmeta.Credit && extmeta.Credit.value;
+ author = extmeta.Artist && extmeta.Artist.value;
+ license = extmeta.License && extmeta.License.value;
+ }
+
+ imageData = new Image(
+ title,
+ innerInfo.size,
+ innerInfo.width,
+ innerInfo.height,
+ innerInfo.mime,
+ innerInfo.url,
+ innerInfo.descriptionurl,
+ imageInfo.imagerepository,
+ innerInfo.user,
+ uploadDateTime,
+ creationDateTime,
+ description,
+ source,
+ author,
+ license
+ );
+
+ if ( innerInfo.thumburl ) {
+ imageData.addThumbUrl(
+ innerInfo.thumbwidth,
+ innerInfo.thumburl
+ );
+ }
+
+ return imageData;
+ };
+
+ /**
+ * @method
+ * Add a thumb URL
+ * @param {number} width
+ * @param {string} url
+ */
+ Image.prototype.addThumbUrl = function ( width, url ) {
+ this.thumbUrls[width] = url;
+ };
+
+ /**
+ * @method
+ * Get a thumb URL if we have it.
+ * @param {number} width
+ * @returns {string|undefined}
+ */
+ Image.prototype.getThumbUrl = function ( width ) {
+ return this.thumbUrls[width];
+ };
+
+ /**
+ * @method
+ * Check whether the image is CC-licensed.
+ * @returns {boolean}
+ */
+ Image.prototype.isCcLicensed = function () {
+ return this.license && this.license.substr( 0, 2 ) === 'cc';
+ };
+
+ /**
+ * @class mw.mmv.model.Repo
+ * Represents information about a single image repository
+ * @constructor
+ * @param {string} displayName
+ * @param {boolean} isLocal
+ */
+ function Repo(
+ displayName,
+ isLocal
+ ) {
+ /** @property {string} displayName */
+ this.displayName = displayName;
+
+ /** @property {boolean} isLocal */
+ this.isLocal = isLocal;
+ }
+
+ /**
+ * @method
+ * @static
+ * Creates a new object from repoInfo we found in an API response.
+ * @param {object} repoInfo
+ * @returns {mw.mmv.model.Repo}
+ */
+ Repo.newFromRepoInfo = function ( repoInfo ) {
+ if ( repoInfo.apiurl ) {
+ return new ForeignApiRepo(
+ repoInfo.displayname,
+ false,
+ repoInfo.apiurl,
+ repoInfo.server,
+ repoInfo.articlepath
+ );
+ } else if ( repoInfo.descBaseUrl ) {
+ return new ForeignDbRepo(
+ repoInfo.displayname,
+ false,
+ repoInfo.descBaseUrl
+ );
+ } else {
+ return new Repo( repoInfo.displayname, repoInfo.local );
+ }
+ };
+
+ /**
+ * @class mw.mmv.model.ForeignApiRepo
+ * Represents information about a foreign API repository
+ * @extends Repo
+ * @constructor
+ * @inheritdoc
+ * @param {string} apiUrl URL to the wiki's api.php
+ * @param {string} server Hostname for the wiki
+ * @param {string} articlePath Path to articles on the wiki, relative to the hostname.
+ */
+ function ForeignApiRepo(
+ displayName,
+ isLocal,
+ apiUrl,
+ server,
+ articlePath
+ ) {
+ Repo.call( this, displayName, isLocal );
+
+ /** @property {string} apiUrl */
+ this.apiUrl = apiUrl;
+
+ /** @property {string} server */
+ this.server = server;
+
+ /** @property {string} articlePath */
+ this.articlePath = articlePath;
+
+ /** @property {string} absoluteArticlePath */
+ this.absoluteArticlePath = server + articlePath;
+ }
+
+ oo.inheritClass( ForeignApiRepo, Repo );
+
+ /**
+ * @class mw.mmv.model.ForeignDbRepo
+ * Represents information about a foreign, shared DB repository
+ * @extends Repo
+ * @constructor
+ * @inheritdoc
+ */
+ function ForeignDbRepo(
+ displayName,
+ isLocal,
+ descBaseUrl
+ ) {
+ Repo.call( this, displayName, isLocal );
+
+ /** @property {string} descBaseUrl */
+ this.descBaseUrl = descBaseUrl;
+ }
+
+ oo.inheritClass( ForeignDbRepo, Repo );
+
+ mw.mmv.model = {};
+ mw.mmv.model.Image = Image;
+ mw.mmv.model.Repo = Repo;
+ mw.mmv.model.ForeignApiRepo = ForeignApiRepo;
+ mw.mmv.model.ForeignDbRepo = ForeignDbRepo;
+}( mediaWiki, OO ) );
diff --git a/resources/ext.multimediaViewer/ext.multimediaViewer.js b/resources/ext.multimediaViewer/ext.multimediaViewer.js
index 09ff6d165..d1c4ece72 100755
--- a/resources/ext.multimediaViewer/ext.multimediaViewer.js
+++ b/resources/ext.multimediaViewer/ext.multimediaViewer.js
@@ -115,8 +115,11 @@
this.api = new mw.Api();
/**
- * imageInfo object. TODO: Describe structure and valid states.
- * @property {Object}
+ * imageInfo object, used for caching - promises will resolve with
+ * an mw.mmv.model.Image object, a repoInfo object, the best width for
+ * the current screen configuration, and the width requested from
+ * the server (if any).
+ * @property {jQuery.Promise[]}
* @private
*/
this.imageInfo = {};
@@ -317,8 +320,8 @@
var viewer = this,
fileTitle = this.currentImageFileTitle;
- this.fetchImageInfo( fileTitle, [ 'url' ] ).done( function ( imageInfo, repoInfo, targetWidth ) {
- viewer.loadResizedImage( ui, imageInfo, targetWidth );
+ this.fetchImageInfo( fileTitle, [ 'url' ] ).done( function ( imageData, repoInfo, targetWidth ) {
+ viewer.loadResizedImage( ui, imageData, targetWidth );
} );
};
@@ -328,31 +331,30 @@
* @protected
*
* @param {LightboxInterface} ui lightbox that got resized
- * @param {Object} imageInfo information regarding the new resized image
+ * @param {mw.mmv.model.Image} imageData information regarding the new resized image
* @param {number} targetWidth
*/
- MMVP.loadResizedImage = function ( ui, imageInfo, targetWidth ) {
- var innerInfo, rpid, viewer, image;
+ MMVP.loadResizedImage = function ( ui, imageData, targetWidth, requestedWidth ) {
+ var rpid, viewer, image, maybeThumb;
// Replace image only if data was returned.
- if ( imageInfo ) {
+ if ( imageData ) {
viewer = this;
image = new Image();
- innerInfo = imageInfo.imageinfo[0];
-
image.onload = function () {
viewer.profileEnd( rpid );
};
rpid = this.profileStart( 'image-resize', {
- width: innerInfo.width,
- height: innerInfo.height,
- fileSize: innerInfo.size
- }, innerInfo.mime );
+ width: imageData.width,
+ height: imageData.height,
+ fileSize: imageData.size
+ }, imageData.mimeType );
- image.src = innerInfo.thumburl || innerInfo.url;
- if ( innerInfo.thumbwidth > targetWidth ) {
+ maybeThumb = imageData.getThumbUrl( requestedWidth );
+ image.src = maybeThumb || imageData.url;
+ if ( maybeThumb && requestedWidth > targetWidth || !maybeThumb && imageData.width > targetWidth ) {
image.width = targetWidth;
}
ui.replaceImageWith( image );
@@ -463,24 +465,32 @@
};
/**
- * Get image information out of an API response.
- * @param {Object[]} images The query.pages member of the API response.
- * @returns {Object} Representing image information.
+ * @method
+ * Get first (hopefully only) member of an object.
+ * @param {Array|Object} things
+ * @returns {Mixed}
*/
- MMVP.getImageInfo = function ( images ) {
- var imageInfo;
+ MMVP.getFirst = function ( things ) {
+ var thing;
- if ( images ) {
- $.each( images, function ( i, page ) {
- imageInfo = page;
+ if ( things ) {
+ $.each( things, function ( i, thisone ) {
+ thing = thisone;
return false;
} );
}
- return imageInfo;
+ return thing;
};
- MMVP.setImageInfo = function ( fileTitle, imageInfo ) {
+ /**
+ * @method
+ * Set the image information in the UI.
+ * @param {mw.Title} fileTitle
+ * @param {mw.mmv.model.Image} imageData
+ * @param {mw.mmv.model.Repo} repoData
+ */
+ MMVP.setImageInfo = function ( fileTitle, imageData, repoData ) {
function whitelistHtml( $el ) {
var child, $prev, $child = $el.children().first();
@@ -508,37 +518,22 @@
}
}
- var extmeta, gfpid,
- repoInfo,
- desc,
- datetime, dtmsg,
- license, msgname,
- username,
- source, author,
+ var gfpid,
+ msgname,
viewer = this,
- ui = this.lightbox.iface,
- innerInfo = imageInfo.imageinfo[0] || {};
+ ui = this.lightbox.iface;
ui.$title.text( fileTitle.getNameText() );
- ui.$useFile.data( 'title', fileTitle );
- ui.$useFile.data( 'src', innerInfo.url );
+ ui.initUseFileData( fileTitle, imageData.url, repoData.isLocal );
ui.$useFileLi.removeClass( 'empty' );
- if ( this.repoInfo ) {
- repoInfo = this.repoInfo[imageInfo.imagerepository];
- }
+ ui.setRepoDisplayName( repoData.displayname, repoData.isLocal );
+ ui.setFilePageLink( imageData.descriptionUrl );
- if ( repoInfo ) {
- ui.setRepoDisplayName( repoInfo.displayname, repoInfo.local );
- ui.setFilePageLink( repoInfo, fileTitle );
- }
+ ui.$repoLi.removeClass( 'empty' );
- ui.$repoLi.toggleClass( 'empty', !repoInfo );
-
- username = innerInfo.user;
-
- if ( username ) {
+ if ( imageData.lastUploader ) {
gfpid = this.profileStart( 'gender-fetch' );
// TODO: Reuse the api member, fix everywhere.
@@ -546,115 +541,98 @@
// TODO this is ugly as hell, let's fix this in core.
new mw.Api( {
ajax: {
- url: repoInfo.apiurl || mw.util.wikiScript( 'api' )
+ url: repoData.apiUrl || mw.util.wikiScript( 'api' )
}
} ).get( {
action: 'query',
list: 'users',
- ususers: username,
+ ususers: imageData.lastUploader,
usprop: 'gender'
} ).done( function ( data ) {
- var gender = data.query.users[0].gender;
+ var gender = 'unknown';
viewer.profileEnd( gfpid );
- ui.setUserPageLink( repoInfo, username, gender );
+ if ( data && data.query && data.query.users &&
+ data.query.users[0] && data.query.users[0].gender ) {
+ gender = data.query.users[0].gender;
+ }
+
+ ui.setUserPageLink( repoData, imageData.lastUploader, gender );
} ).fail( function () {
mw.log( 'Gender fetch with ID ' + gfpid + ' failed, probably due to cross-domain API request.' );
- ui.setUserPageLink( repoInfo, username, 'unknown' );
+ ui.setUserPageLink( repoData, imageData.lastUploader, 'unknown' );
} );
}
- extmeta = innerInfo.extmetadata;
-
- if ( extmeta ) {
- desc = extmeta.ImageDescription;
-
- ui.$imageDescDiv.toggleClass( 'empty', !desc );
-
- if ( desc ) {
- desc = desc.value;
- whitelistHtml( ui.$imageDesc.append( $.parseHTML( desc ) ) );
- } else {
- ui.$imageDesc.append( mw.message( 'multimediaviewer-desc-nil' ).text() );
- }
-
- datetime = extmeta.DateTimeOriginal || extmeta.DateTime;
-
- if ( datetime ) {
- // get rid of HTML tags
- datetime = datetime.value.replace( /<.*?>/g, '' );
- datetime = this.formatDate( datetime );
-
- dtmsg = (
- 'multimediaviewer-datetime-' +
- ( extmeta.DateTimeOriginal ? 'created' : 'uploaded' )
- );
-
- ui.$datetime.text(
- mw.message( dtmsg, datetime ).text()
- );
- }
-
- ui.$datetimeLi.toggleClass( 'empty', !datetime );
-
- source = extmeta.Credit;
- author = extmeta.Artist;
-
- if ( source ) {
- source = source.value;
- whitelistHtml( ui.$source.empty().append( $.parseHTML( source ) ) );
- }
-
- if ( author ) {
- author = author.value;
- whitelistHtml( ui.$author.empty().append( $.parseHTML( author ) ) );
- }
-
- if ( source && author ) {
- ui.$credit.html(
- mw.message(
- 'multimediaviewer-credit',
- ui.$author.get( 0 ).outerHTML,
- ui.$source.get( 0 ).outerHTML
- ).plain()
- );
- } else {
- // Clobber the contents and only have one of the fields
- if ( source ) {
- ui.$credit.html( ui.$source );
- } else if ( author ) {
- ui.$credit.html( ui.$author );
- }
- }
-
- ui.$credit.toggleClass( 'empty', !source && !author );
-
- license = extmeta.License;
+ if ( imageData.creationDateTime ) {
+ ui.$datetime.text(
+ mw.message(
+ 'multimediaviewer-datetime-created',
+ this.formatDate( imageData.creationDateTime )
+ ).text()
+ );
+ } else if ( imageData.uploadDateTime ) {
+ ui.$datetime.text(
+ mw.message(
+ 'multimediaviewer-datetime-uploaded',
+ this.formatDate( imageData.uploadDateTime )
+ ).text()
+ );
}
- if ( license ) {
- license = license.value;
+ ui.$datetimeLi.toggleClass( 'empty', !imageData.uploadDateTime && !imageData.creationDateTime );
+
+ if ( imageData.description ) {
+ whitelistHtml( ui.$imageDesc.empty().append( $.parseHTML( imageData.description ) ) );
+ } else {
+ ui.$imageDesc.append( mw.message( 'multimediaviewer-desc-nil' ).text() );
}
- msgname = 'multimediaviewer-license-' + ( license || '' );
+ ui.$imageDescDiv.toggleClass( 'empty', !imageData.description );
- if ( !license || !mw.messages.exists( msgname ) ) {
+ if ( imageData.source ) {
+ whitelistHtml( ui.$source.empty().append( $.parseHTML( imageData.source ) ) );
+ }
+
+ if ( imageData.author ) {
+ whitelistHtml( ui.$author.empty().append( $.parseHTML( imageData.author ) ) );
+ }
+
+ if ( imageData.source && imageData.author ) {
+ ui.$credit.html(
+ mw.message(
+ 'multimediaviewer-credit',
+ ui.$author.get( 0 ).outerHTML,
+ ui.$source.get( 0 ).outerHTML
+ ).plain()
+ );
+ } else {
+ // Clobber the contents and only have one of the fields
+ if ( imageData.source ) {
+ ui.$credit.empty().append( ui.$source );
+ } else if ( imageData.author ) {
+ ui.$credit.empty().append( ui.$author );
+ }
+ }
+
+ ui.$credit.toggleClass( 'empty', !imageData.source && !imageData.author );
+
+ msgname = 'multimediaviewer-license-' + ( imageData.license || '' );
+
+ if ( !imageData.license || !mw.messages.exists( msgname ) ) {
// Cannot display, fallback or fail
- license = 'default';
msgname = 'multimediaviewer-license-default';
} else {
// License found, store the license data
ui.$license.data( 'license', mw.message( msgname ).text() );
}
- if ( license ) {
- ui.$license
- .text( mw.message( msgname ).text() )
- .toggleClass( 'cc-license', license.substr( 0, 2 ) === 'cc' );
- }
+ ui.$license
+ .text( mw.message( msgname ).text() )
+ .toggleClass( 'cc-license', imageData.isCcLicensed() );
- ui.$license.toggleClass( 'empty', !license );
+ ui.$license.toggleClass( 'empty', !imageData.license );
};
MMVP.loadImage = function ( image, initialSrc ) {
@@ -676,9 +654,9 @@
mdpid = this.profileStart( 'metadata-fetch' );
- this.fetchImageInfo( image.filePageTitle ).done( function ( imageInfo, res, size ) {
+ this.fetchImageInfo( image.filePageTitle ).done( function ( imageData, repoInfo, size, requestedWidth ) {
var pid,
- innerInfo = imageInfo.imageinfo[0],
+ repoData = mw.mmv.model.Repo.newFromRepoInfo( repoInfo[imageData.repo] ),
imageEle = new Image(),
targetWidth = size;
@@ -698,16 +676,16 @@
viewer.profileEnd( mdpid );
pid = viewer.profileStart( 'image-load', {
- width: innerInfo.width,
- height: innerInfo.height,
- fileSize: innerInfo.size
- }, innerInfo.mime );
+ width: imageData.width,
+ height: imageData.height,
+ fileSize: imageData.size
+ }, imageData.mimeType );
- imageEle.src = imageInfo.imageinfo[0].thumburl || imageInfo.imageinfo[0].url;
+ imageEle.src = imageData.getThumbUrl( requestedWidth ) || imageData.url;
viewer.lightbox.iface.$imageDiv.removeClass( 'empty' );
viewer.lightbox.iface.replaceImageWith( imageEle );
- viewer.setImageInfo( image.filePageTitle, imageInfo );
+ viewer.setImageInfo( image.filePageTitle, imageData, repoData );
} );
comingFromPopstate = false;
@@ -729,9 +707,13 @@
* @method
* Fetches image information from the API.
*
- * Will resolve the promise with two objects (imageInfo and repoInfo) and the
+ * Will resolve the promise with two objects (imageData and repoData), the
* target width - basically the screen size - that the caller should resize
- * the image to eventually.
+ * the image to eventually, and the requested width - that is, what we asked
+ * for from the API - that should be used to fetch the thumbnail URL from
+ * the imageData object.
+ *
+ * The target
* @param {mw.Title} fileTitle Title of the file page for the image.
* @param {string[]} [props] List of properties to get from imageinfo
* @returns {jQuery.Promise}
@@ -753,7 +735,7 @@
requestedWidth = widths.requested;
function handleApiData( data ) {
- var imageInfo;
+ var imageInfo, imageData;
if ( !data || !data.query ) {
// No information, oh well
@@ -761,17 +743,13 @@
}
viewer.cacheRepoInfo( data.query.repos );
- imageInfo = viewer.getImageInfo( data.query.pages );
+ imageInfo = viewer.getFirst( data.query.pages );
if ( imageInfo ) {
- if ( !imageInfo.imageinfo ||
- imageInfo.imageinfo.length === 0 ) {
- // No data, fail.
- $.Deferred().reject();
- }
+ imageData = mw.mmv.model.Image.newFromImageInfo( fileTitle, imageInfo );
// Give back the information we have
- return $.Deferred().resolve( imageInfo, viewer.repoInfo, targetWidth );
+ return $.Deferred().resolve( imageData, viewer.repoInfo, targetWidth, requestedWidth );
} else {
return $.Deferred().reject();
}
@@ -802,20 +780,25 @@
// Fetch the new thumb url but nothing else, because it's
// the only non-cacheable thing
apiArgs.iiprop = 'url';
- return this.imageInfo[filename].then( function ( cachedInfo ) {
- return makeImageInfoRequest( apiArgs ).then( function ( imageInfo, repoInfo, targetWidth ) {
- var innerInfo,
- newInfo = $.extend( true, {}, cachedInfo );
- $.each( imageInfo.imageinfo, function ( i, item ) {
- innerInfo = item;
- return false;
- } );
- $.each( newInfo.imageinfo, function ( i, item ) {
- item.thumburl = innerInfo.thumburl;
- item.thumbwidth = innerInfo.thumbwidth;
- item.thumbheight = innerInfo.thumbheight;
- } );
- return $.Deferred().resolve( newInfo, repoInfo, targetWidth );
+ return this.imageInfo[filename].then( function ( imageData, repoInfo ) {
+ var maybeThumb = imageData.getThumbUrl( requestedWidth );
+
+ // Thumbnail caching! Woo!
+ if ( maybeThumb ) {
+ return $.Deferred().resolve( imageData, repoInfo, targetWidth, requestedWidth );
+ }
+
+ return viewer.api.get( apiArgs ).then( function ( data ) {
+ var imageInfo, innerInfo;
+
+ imageInfo = viewer.getFirst( data.query.pages );
+ innerInfo = viewer.getFirst( imageInfo.imageinfo );
+
+ if ( innerInfo.thumburl ) {
+ imageData.addThumbUrl( innerInfo.thumbwidth, innerInfo.thumburl );
+ }
+
+ return $.Deferred().resolve( imageData, repoInfo, targetWidth, requestedWidth );
} );
} );
}
diff --git a/resources/ext.multimediaViewer/ext.multimediaViewer.lightboxinterface.js b/resources/ext.multimediaViewer/ext.multimediaViewer.lightboxinterface.js
index d0d3bcd69..7e6cd6b91 100644
--- a/resources/ext.multimediaViewer/ext.multimediaViewer.lightboxinterface.js
+++ b/resources/ext.multimediaViewer/ext.multimediaViewer.lightboxinterface.js
@@ -524,54 +524,43 @@
/**
* @method
* Sets the URL for the File: page of the image
- * @param {Object} repoInfo
- * @param {mw.Title} fileTitle
+ * @param {string} url
*/
- LIP.setFilePageLink = function ( repoInfo, fileTitle ) {
- var linkpath;
+ LIP.setFilePageLink = function ( url ) {
+ this.$repo.prop( 'href', url );
+ this.$license.prop( 'href', url );
+ };
- if ( repoInfo.descBaseUrl ) {
- linkpath = repoInfo.descBaseUrl + fileTitle.getMainText();
- } else {
- if ( repoInfo.server && repoInfo.articlepath ) {
- linkpath = repoInfo.server + repoInfo.articlepath;
- } else {
- linkpath = mw.config.get( 'wgArticlePath' );
- }
- linkpath = linkpath.replace( '$1', fileTitle.getPrefixedText() );
- }
-
- if ( repoInfo.local ) {
- this.$useFile.data( 'isLocal', repoInfo.local );
- }
-
- if ( !/^(https?:)?\/\//.test( linkpath ) ) {
- this.$useFile.data( 'link', mw.config.get( 'wgServer' ) + linkpath );
- } else {
- this.$useFile.data( 'link', linkpath );
- }
-
- this.$repo.prop( 'href', linkpath );
- this.$license.prop( 'href', linkpath );
+ /**
+ * @method
+ * Saves some data about the image on the $useFile element for later setup.
+ * @param {mw.Title} title
+ * @param {string} src The URL for the full-size image
+ * @param {boolean} isLocal Whether the file is on this wiki or not
+ */
+ LIP.initUseFileData = function ( title, src, isLocal ) {
+ this.$useFile.data( 'title', title );
+ this.$useFile.data( 'src', src );
+ this.$useFile.data( 'isLocal', isLocal );
};
/**
* @method
* Sets the link to the user page where possible
- * @param {Object} repoInfo
+ * @param {mw.mmv.model.Repo} repoData
* @param {string} username
* @param {string} gender
*/
- LIP.setUserPageLink = function ( repoInfo, username, gender ) {
+ LIP.setUserPageLink = function ( repoData, username, gender ) {
var userlink,
userpage = 'User:' + username;
- if ( repoInfo.descBaseUrl ) {
+ if ( repoData instanceof mw.mmv.model.ForeignDbRepo ) {
// We basically can't do anything about this; fail
this.$username.addClass( 'empty' );
} else {
- if ( repoInfo.server && repoInfo.articlepath ) {
- userlink = repoInfo.server + repoInfo.articlepath;
+ if ( repoData.absoluteArticlePath ) {
+ userlink = repoData.absoluteArticlePath;
} else {
userlink = mw.config.get( 'wgArticlePath' );
}
diff --git a/resources/ext.multimediaViewer/mmv.js b/resources/ext.multimediaViewer/mmv.js
new file mode 100644
index 000000000..87f59301e
--- /dev/null
+++ b/resources/ext.multimediaViewer/mmv.js
@@ -0,0 +1,22 @@
+/*
+ * 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 .
+ */
+
+// This will just set up various objects for the main
+// MultimediaViewer load.
+( function ( mw ) {
+ mw.mmv = {};
+}( mediaWiki ) );
diff --git a/tests/qunit/ext.multimediaViewer.dataModel.test.js b/tests/qunit/ext.multimediaViewer.dataModel.test.js
new file mode 100644
index 000000000..e290426dd
--- /dev/null
+++ b/tests/qunit/ext.multimediaViewer.dataModel.test.js
@@ -0,0 +1,60 @@
+/*
+ * 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 .
+ */
+
+( function ( mw ) {
+ QUnit.module( 'ext.multimediaViewer.dataModel', QUnit.newMwEnvironment() );
+
+ QUnit.test( 'Image model constructor sanity check', 16, function ( assert ) {
+ var
+ title = mw.Title.newFromText( 'File:Foobar.jpg' ),
+ size = 100,
+ width = 10,
+ height = 15,
+ mime = 'image/jpeg',
+ url = 'https://upload.wikimedia.org/wikipedia/commons/3/3a/Foobar.jpg',
+ descurl = 'https://commons.wikimedia.org/wiki/File:Foobar.jpg',
+ repo = 'wikimediacommons',
+ user = 'Kaldari',
+ datetime = '2011-07-04T23:31:14Z',
+ origdatetime = '2010-07-04T23:31:14Z',
+ description = 'This is a test file.',
+ source = 'WMF',
+ author = 'Ryan Kaldari',
+ license = 'cc0',
+ imageData = new mw.mmv.model.Image(
+ title, size, width, height, mime, url,
+ descurl, repo, user, datetime, origdatetime,
+ description, source, author, license );
+
+ assert.strictEqual( imageData.title, title, 'Title is set correctly' );
+ assert.strictEqual( imageData.size, size, 'Size is set correctly' );
+ assert.strictEqual( imageData.width, width, 'Width is set correctly' );
+ assert.strictEqual( imageData.height, height, 'Height is set correctly' );
+ assert.strictEqual( imageData.mimeType, mime, 'MIME type is set correctly' );
+ assert.strictEqual( imageData.url, url, 'URL for original image is set correctly' );
+ assert.strictEqual( imageData.descriptionUrl, descurl, 'URL for image description page is set correctly' );
+ assert.strictEqual( imageData.repo, repo, 'Repository name is set correctly' );
+ assert.strictEqual( imageData.lastUploader, user, 'Name of last uploader is set correctly' );
+ assert.strictEqual( imageData.uploadDateTime, datetime, 'Date and time of last upload is set correctly' );
+ assert.strictEqual( imageData.creationDateTime, origdatetime, 'Date and time of original upload is set correctly' );
+ assert.strictEqual( imageData.description, description, 'Description is set correctly' );
+ assert.strictEqual( imageData.source, source, 'Source is set correctly' );
+ assert.strictEqual( imageData.author, author, 'Author is set correctly' );
+ assert.strictEqual( imageData.license, license, 'License is set correctly' );
+ assert.ok( imageData.thumbUrls, 'Thumb URL cache is set up properly' );
+ } );
+}( mediaWiki ) );
diff --git a/tests/qunit/ext.multimediaViewer.lightboxinterface.test.js b/tests/qunit/ext.multimediaViewer.lightboxinterface.test.js
index 3e87ed402..52e5a5e0e 100644
--- a/tests/qunit/ext.multimediaViewer.lightboxinterface.test.js
+++ b/tests/qunit/ext.multimediaViewer.lightboxinterface.test.js
@@ -123,21 +123,8 @@
assert.strictEqual( handlerCalls, 1, 'The handler was not called after calling lightbox.clearEvents().' );
} );
- QUnit.test( 'Setting repository information in the UI works as expected', 5, function ( assert ) {
- var lightbox = new mw.LightboxInterface(),
-
- localRepoInfo = {
- local: true
- },
-
- remoteDBRepoInfo = {
- descBaseUrl: 'http://example.com/wiki/File:'
- },
-
- remoteAPIRepoInfo = {
- server: 'http://commons.example.org',
- articlepath: '/wiki/$1'
- };
+ QUnit.test( 'Setting repository information in the UI works as expected', 3, function ( assert ) {
+ var lightbox = new mw.LightboxInterface();
lightbox.setRepoDisplayName( 'Example Wiki' );
assert.strictEqual( lightbox.$repo.text(), 'Learn more on Example Wiki', 'Text set to something useful for remote wiki - if this fails it might be because of localisation' );
@@ -145,13 +132,7 @@
lightbox.setRepoDisplayName();
assert.strictEqual( lightbox.$repo.text(), 'Learn more on ' + mw.config.get( 'wgSiteName' ), 'Text set to something useful for local wiki - if this fails it might be because of localisation' );
- lightbox.setFilePageLink( localRepoInfo, mw.Title.newFromText( 'File:Foobar.jpg' ) );
- assert.strictEqual( lightbox.$repo.prop( 'href' ), mw.config.get( 'wgServer' ) + mw.config.get( 'wgArticlePath' ).replace( '$1', 'File:Foobar.jpg' ), 'The file link was set to a local page successfully.' );
-
- lightbox.setFilePageLink( remoteDBRepoInfo, mw.Title.newFromText( 'File:Foobar.jpg' ) );
- assert.strictEqual( lightbox.$repo.prop( 'href' ), 'http://example.com/wiki/File:Foobar.jpg', 'The file link was set to a remote shared DB page successfully.' );
-
- lightbox.setFilePageLink( remoteAPIRepoInfo, mw.Title.newFromText( 'File:Foobar.jpg' ) );
- assert.strictEqual( lightbox.$repo.prop( 'href' ), 'http://commons.example.org/wiki/File:Foobar.jpg', 'The file link was set to a remote API page successfully.' );
+ lightbox.setFilePageLink( 'https://commons.wikimedia.org/wiki/File:Foobar.jpg' );
+ assert.strictEqual( lightbox.$repo.prop( 'href' ), 'https://commons.wikimedia.org/wiki/File:Foobar.jpg', 'The file link was set successfully.' );
} );
}( mediaWiki, jQuery ) );