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 ) );