Merge "Make Media Viewer pluggable for the 3D extension"

This commit is contained in:
jenkins-bot 2016-05-09 20:14:35 +00:00 committed by Gerrit Code Review
commit a0e97aab78
12 changed files with 150 additions and 54 deletions

View file

@ -287,7 +287,7 @@ class MultimediaViewerHooks {
$wgMediaViewerDurationLoggingSamplingFactor, $wgMediaViewerDurationLoggingLoggedinSamplingFactor,
$wgMediaViewerAttributionLoggingSamplingFactor, $wgMediaViewerDimensionLoggingSamplingFactor,
$wgMediaViewerIsInBeta, $wgMediaViewerUseThumbnailGuessing, $wgMediaViewerImageQueryParameter,
$wgMediaViewerRecordVirtualViewBeaconURI;
$wgMediaViewerRecordVirtualViewBeaconURI, $wgMediaViewerExtensions;
$vars['wgMultimediaViewer'] = array(
'infoLink' => self::$infoLink,
@ -303,6 +303,7 @@ class MultimediaViewerHooks {
'imageQueryParameter' => $wgMediaViewerImageQueryParameter,
'recordVirtualViewBeaconURI' => $wgMediaViewerRecordVirtualViewBeaconURI,
'tooltipDelay' => 1000,
'extensions' => $wgMediaViewerExtensions,
);
$vars['wgMediaViewer'] = true;
$vars['wgMediaViewerIsInBeta'] = $wgMediaViewerIsInBeta;

View file

@ -401,6 +401,16 @@
"MultimediaViewerHooks::thumbnailBeforeProduceHTML"
]
},
"config": {},
"config": {
"MediaViewerExtensions": {
"jpg": "default",
"jpeg": "default",
"gif": "default",
"svg": "default",
"png": "default",
"tiff": "default",
"tif": "default"
}
},
"manifest_version": 1
}

View file

@ -23,13 +23,11 @@
* @class mw.mmv.logging.ViewLogger
* @extends mw.Api
* @constructor
* @param {mw.Map} mwConfig mw.config
* @param {mw.mmv.Config} config mw.mmv.Config object
* @param {Object} window Browser window object
* @param {mw.mmv.logging.ActionLogger} actionLogger ActionLogger object
*/
function ViewLogger( mwConfig, windowObject, actionLogger ) {
var config = mwConfig && mwConfig.get ? mwConfig.get( 'wgMultimediaViewer' ) : false;
function ViewLogger( config, windowObject, actionLogger ) {
/**
* Was the last image view logged or was logging skipped?
* @property {boolean}
@ -58,7 +56,7 @@
* If set, URI to send the beacon request to in order to record the virtual view
* @property {string}
*/
this.recordVirtualViewBeaconURI = config ? config.recordVirtualViewBeaconURI : false;
this.recordVirtualViewBeaconURI = config.recordVirtualViewBeaconURI();
/**
* Browser window

View file

@ -222,5 +222,50 @@
this.setInLocalStorage( 'mmv-showStatusInfo', '0' );
};
/**
* Returns file extensions handled by Media Viewer.
*
* The object's keys are the file extensions.
* The object's values are either 'default' when Media Viewer handles that file extension
* directly or the name of a ResourceLoader module to load when such a file is opened.
*
* @returns {Object}
*/
CP.extensions = function ( ) {
return this.viewerConfig.extensions;
};
/**
* Returns UI language
* @returns {string} Language code
*/
CP.language = function ( ) {
return this.mwConfig.get( 'wgUserLanguage', false ) || this.mwConfig.get( 'wgContentLanguage', 'en' );
};
/**
* Returns URI of virtual view beacon or false if not set
* @returns {string|boolean} URI
*/
CP.recordVirtualViewBeaconURI = function ( ) {
return this.viewerConfig.recordVirtualViewBeaconURI;
};
/**
* Returns useThumbnailGuessing flag
* @returns {boolean}
*/
CP.useThumbnailGuessing = function ( ) {
return this.viewerConfig.useThumbnailGuessing;
};
/**
* Returns imageQueryParameter, if set
* @returns {string|boolean}
*/
CP.imageQueryParameter = function ( ) {
return this.viewerConfig.imageQueryParameter;
};
mw.mmv.Config = Config;
} ( mediaWiki, jQuery ) );

View file

@ -30,16 +30,6 @@
localStorage = window.localStorage || false;
} catch ( e ) { }
this.validExtensions = {
'jpg' : true,
'jpeg' : true,
'gif' : true,
'svg' : true,
'png' : true,
'tiff' : true,
'tif' : true
};
// Exposed for tests
this.hoverWaitDuration = 200;
@ -54,6 +44,8 @@
localStorage
);
this.validExtensions = this.config.extensions();
/** @property {mw.mmv.HtmlUtils} htmlUtils - */
this.htmlUtils = new mw.mmv.HtmlUtils();
@ -184,7 +176,7 @@
link = $link.prop( 'href' ),
alt = $thumb.attr( 'alt' );
if ( !bs.validExtensions[ title.getExtension().toLowerCase() ] ) {
if ( !( title.getExtension().toLowerCase() in bs.validExtensions ) ) {
return;
}
@ -494,7 +486,7 @@
*/
MMVB.getViewer = function () {
if ( this.viewer === undefined ) {
this.viewer = new mw.mmv.MultimediaViewer( mw.config );
this.viewer = new mw.mmv.MultimediaViewer( this.config );
this.viewer.setupEventHandlers();
mw.mmv.viewer = this.viewer;
}

View file

@ -24,30 +24,30 @@
* to manage the viewing experience of such content.
* @class mw.mmv.MultimediaViewer
* @constructor
* @param {mw.Map} mwConfig mw.config
* @param {mw.mmv.Config} config mw.mmv.Config object
*/
function MultimediaViewer( mwConfig ) {
function MultimediaViewer( config ) {
var apiCacheMaxAge = 86400; // one day (24 hours * 60 min * 60 sec)
var apiCacheFiveMinutes = 300; // 5 min * 60 sec
/**
* @property {mw.Map}
* @property {mw.mmv.Config}
* @private
*/
this.mwConfig = mwConfig;
this.config = config;
/**
* @property {mw.mmv.provider.Image}
* @private
*/
this.imageProvider = new mw.mmv.provider.Image( mw.config.get( 'wgMultimediaViewer' ).imageQueryParameter );
this.imageProvider = new mw.mmv.provider.Image( this.config.imageQueryParameter() );
/**
* @property {mw.mmv.provider.ImageInfo}
* @private
*/
this.imageInfoProvider = new mw.mmv.provider.ImageInfo( new mw.mmv.logging.Api( 'imageinfo' ), {
language: this.mwConfig.get( 'wgUserLanguage', false ) || this.mwConfig.get( 'wgContentLanguage', 'en' ),
language: this.config.language(),
maxage: apiCacheFiveMinutes
});
@ -107,7 +107,7 @@
/**
* @property {mw.mmv.logging.ViewLogger} view -
*/
this.viewLogger = new mw.mmv.logging.ViewLogger( this.mwConfig, window, mw.mmv.actionLogger );
this.viewLogger = new mw.mmv.logging.ViewLogger( this.config, window, mw.mmv.actionLogger );
}
MMVP = MultimediaViewer.prototype;
@ -228,11 +228,14 @@
canvasDimensions,
imagePromise,
metadataPromise,
pluginsPromise,
start,
viewer = this,
$initialImage = $( initialImage ),
extraStatsDeferred = $.Deferred();
pluginsPromise = this.loadExtensionPlugins( image.filePageTitle.ext );
this.currentIndex = image.index;
this.currentImageFileTitle = image.filePageTitle;
@ -251,6 +254,8 @@
// the aspect ratio
$initialImage.hide();
$initialImage.addClass( 'mw-mmv-placeholder-image' );
$initialImage.addClass( image.filePageTitle.ext );
this.ui.canvas.set( image, $initialImage );
this.preloadImagesMetadata();
@ -294,8 +299,13 @@
} );
}
imageElement.className = 'mw-mmv-final-image';
imageElement.className = 'mw-mmv-final-image ' + image.filePageTitle.ext;
imageElement.alt = image.alt;
$.when( metadataPromise, pluginsPromise ).done( function ( metadata ) {
$( document ).trigger( $.Event( 'mmv-metadata', { viewer : viewer, image : image, imageInfo: metadata[ 0 ] } ) );
} );
viewer.displayRealThumbnail( thumbnail, imageElement, imageWidths, $.now() - start );
} ).fail( function ( error ) {
viewer.ui.canvas.showError( error );
@ -741,7 +751,7 @@
if (
sampleUrl && originalWidth && originalHeight &&
mw.config.get( 'wgMultimediaViewer' ).useThumbnailGuessing
this.config.useThumbnailGuessing()
) {
guessing = true;
thumbnailPromise = this.guessedThumbnailInfoProvider.get(
@ -946,5 +956,25 @@
mw.loader.load( [ 'mmv.ui.reuse.shareembed', 'moment' ] );
};
/**
* Loads the RL module defined for a given file extension, if any
* @param {string} extension File extension
* @returns {jQuery.Promise}
*/
MMVP.loadExtensionPlugins = function ( extension ) {
var deferred = $.Deferred(),
config = this.config.extensions();
if ( !( extension in config ) || config[ extension ] === 'default' ) {
return deferred.resolve();
}
mw.loader.using( config[ extension ], function() {
deferred.resolve();
} );
return deferred;
};
mw.mmv.MultimediaViewer = MultimediaViewer;
}( mediaWiki, jQuery ) );

View file

@ -60,13 +60,20 @@
display: block;
margin-right: auto;
margin-left: auto;
background: url( checker.png ) repeat;
cursor: pointer;
cursor: zoom-in;
/* Whitelist file types that are potentially transparent.
We don't set it for other file types because Media Viewer plugins
can find that undesirable (eg. 3d) */
&.gif, &.png, &.svg, &.tiff, &.tif {
background: url( checker.png ) repeat;
}
&.mw-mmv-dialog-is-open {
cursor: default;
}
.metadata-panel-is-open & {
cursor: pointer;
}

View file

@ -7,7 +7,7 @@
QUnit.test( 'unview()', 4, function ( assert ) {
var logger = { log: $.noop },
viewLogger = new mw.mmv.logging.ViewLogger( {}, {}, logger );
viewLogger = new mw.mmv.logging.ViewLogger( { recordVirtualViewBeaconURI : $.noop }, {}, logger );
this.sandbox.stub( logger, 'log' );
@ -32,7 +32,7 @@
QUnit.test( 'focus and blur', 1, function ( assert ) {
var fakeWindow = $( '<div>' ),
viewLogger = new mw.mmv.logging.ViewLogger( {}, fakeWindow, { log: $.noop } );
viewLogger = new mw.mmv.logging.ViewLogger( { recordVirtualViewBeaconURI : $.noop }, fakeWindow, { log: $.noop } );
this.clock.tick( 1 ); // This is just so that $.now() > 0 in the fake timer environment
@ -56,7 +56,7 @@
} );
QUnit.test( 'stopViewDuration before startViewDuration', 1, function ( assert ) {
var viewLogger = new mw.mmv.logging.ViewLogger( {}, {}, { log: $.noop } );
var viewLogger = new mw.mmv.logging.ViewLogger( { recordVirtualViewBeaconURI : $.noop }, {}, { log: $.noop } );
this.clock.tick( 1 ); // This is just so that $.now() > 0 in the fake timer environment

View file

@ -288,7 +288,7 @@
var bootstrap,
$div,
$link,
viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
viewer = mw.mmv.testHelpers.getMultimediaViewer(),
fname = 'valid',
imgSrc = '/' + fname + '.jpg/300px-' + fname + '.jpg',
imgRegex = new RegExp( imgSrc + '$' );

View file

@ -142,7 +142,7 @@
var buttonOffset, panelBottom,
oldRevealButtonsAndFadeIfNeeded,
lightbox = new mw.mmv.LightboxInterface(),
viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
viewer = mw.mmv.testHelpers.getMultimediaViewer(),
oldFnEnterFullscreen = $.fn.enterFullscreen,
oldFnExitFullscreen = $.fn.exitFullscreen;
@ -251,7 +251,7 @@
} );
QUnit.test( 'Keyboard prev/next', 2, function ( assert ) {
var viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
var viewer = mw.mmv.testHelpers.getMultimediaViewer(),
lightbox = new mw.mmv.LightboxInterface();
viewer.setupEventHandlers();

View file

@ -2,7 +2,7 @@
QUnit.module( 'mmv', QUnit.newMwEnvironment() );
QUnit.test( 'eachPrealoadableLightboxIndex()', 11, function ( assert ) {
var viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
var viewer = mw.mmv.testHelpers.getMultimediaViewer(),
expectedIndices,
i;
@ -31,7 +31,7 @@
QUnit.test( 'Hash handling', 8, function ( assert ) {
var oldUnattach,
viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
viewer = mw.mmv.testHelpers.getMultimediaViewer(),
ui = new mw.mmv.LightboxInterface(),
imageSrc = 'Foo bar.jpg',
image = { filePageTitle: new mw.Title( 'File:' + imageSrc ) };
@ -113,7 +113,7 @@
QUnit.test( 'Progress', 4, function ( assert ) {
var imageDeferred = $.Deferred(),
viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
viewer = mw.mmv.testHelpers.getMultimediaViewer(),
fakeImage = {
filePageTitle: new mw.Title( 'File:Stuff.jpg' ),
extraStatsDeferred: $.Deferred().reject()
@ -181,7 +181,7 @@
filePageTitle: new mw.Title( 'File:Second.jpg' ),
extraStatsDeferred: $.Deferred().reject()
},
viewer = new mw.mmv.MultimediaViewer( { get: $.noop } );
viewer = mw.mmv.testHelpers.getMultimediaViewer();
// animation would keep running, conflict with other tests
this.sandbox.stub( $.fn, 'animate' ).returnsThis();
@ -282,7 +282,7 @@
} );
QUnit.test( 'resetBlurredThumbnailStates', 4, function ( assert ) {
var viewer = new mw.mmv.MultimediaViewer( { get: $.noop } );
var viewer = mw.mmv.testHelpers.getMultimediaViewer();
// animation would keep running, conflict with other tests
this.sandbox.stub( $.fn, 'animate' ).returnsThis();
@ -300,7 +300,7 @@
} );
QUnit.test( 'Placeholder first, then real thumbnail', 4, function ( assert ) {
var viewer = new mw.mmv.MultimediaViewer( { get: $.noop } );
var viewer = mw.mmv.testHelpers.getMultimediaViewer();
viewer.setImage = $.noop;
viewer.ui = { canvas: {
@ -322,7 +322,7 @@
} );
QUnit.test( 'Placeholder first, then real thumbnail - missing size', 4, function ( assert ) {
var viewer = new mw.mmv.MultimediaViewer( { get: $.noop } );
var viewer = mw.mmv.testHelpers.getMultimediaViewer();
viewer.currentIndex = 1;
viewer.setImage = $.noop;
@ -345,7 +345,7 @@
} );
QUnit.test( 'Real thumbnail first, then placeholder', 4, function ( assert ) {
var viewer = new mw.mmv.MultimediaViewer( { get: $.noop } );
var viewer = mw.mmv.testHelpers.getMultimediaViewer();
viewer.setImage = $.noop;
viewer.ui = {
@ -367,7 +367,7 @@
} );
QUnit.test( 'displayRealThumbnail', 2, function ( assert ) {
var viewer = new mw.mmv.MultimediaViewer( { get: $.noop } );
var viewer = mw.mmv.testHelpers.getMultimediaViewer();
viewer.setImage = $.noop;
viewer.ui = { canvas: {
@ -386,7 +386,7 @@
} );
QUnit.test( 'New image loaded while another one is loading', 5, function ( assert ) {
var viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
var viewer = mw.mmv.testHelpers.getMultimediaViewer(),
firstImageDeferred = $.Deferred(),
secondImageDeferred = $.Deferred(),
firstLigthboxInfoDeferred = $.Deferred(),
@ -458,7 +458,7 @@
QUnit.test( 'Events are not trapped after the viewer is closed', 0, function ( assert ) {
var i, j, k, eventParameters,
viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
viewer = mw.mmv.testHelpers.getMultimediaViewer(),
$document = $( document ),
$qf = $( '#qunit-fixture' ),
eventTypes = [ 'keydown', 'keyup', 'keypress', 'click', 'mousedown', 'mouseup' ],
@ -533,7 +533,7 @@
} );
QUnit.test( 'Refuse to load too-big thumbnails', 1, function ( assert ) {
var viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
var viewer = mw.mmv.testHelpers.getMultimediaViewer(),
intendedWidth = 50,
title = mw.Title.newFromText( 'File:Foobar.svg' );
@ -550,9 +550,9 @@
thumbnailInfoStub,
imageStub,
promise,
viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
useThumbnailGuessing,
viewer = new mw.mmv.MultimediaViewer( { imageQueryParameter: $.noop, language: $.noop, recordVirtualViewBeaconURI: $.noop, extensions: function () { return { 'jpg' : 'default' }; }, useThumbnailGuessing : function () { return useThumbnailGuessing; } } ),
sandbox = this.sandbox,
oldUseThumbnailGuessing = mw.config.get( 'wgMultimediaViewer' ).useThumbnailGuessing,
file = new mw.Title( 'File:Copyleft.svg' ),
sampleURL = 'http://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Copyleft.svg/300px-Copyleft.svg.png',
width = 100,
@ -566,7 +566,7 @@
imageStub = viewer.imageProvider.get = sandbox.stub();
}
mw.config.get( 'wgMultimediaViewer' ).useThumbnailGuessing = true;
useThumbnailGuessing = true;
// When we lack sample URL and original dimensions, the classic provider should be used
setupStubs();
@ -632,7 +632,7 @@
assert.ok( imageStub.getCall( 1 ).calledWith( 'apiURL' ), 'When even the retry fails, ImageProvider is called second with the guessed url' );
assert.strictEqual( promise.state(), 'rejected', 'When even the retry fails, fetchThumbnail rejects' );
mw.config.get( 'wgMultimediaViewer' ).useThumbnailGuessing = false;
useThumbnailGuessing = false;
// When guessing is disabled, the classic provider is used
setupStubs();
@ -645,12 +645,10 @@
assert.ok( imageStub.calledOnce, 'When guessing is disabled, ImageProvider is called once' );
assert.ok( imageStub.calledWith( 'apiURL' ), 'When guessing is disabled, ImageProvider is called with the API url' );
assert.strictEqual( promise.state(), 'resolved', 'When guessing is disabled, fetchThumbnail resolves' );
mw.config.get( 'wgMultimediaViewer' ).useThumbnailGuessing = oldUseThumbnailGuessing;
} );
QUnit.test( 'document.title', 2, function ( assert ) {
var viewer = new mw.mmv.MultimediaViewer( { get: $.noop } ),
var viewer = mw.mmv.testHelpers.getMultimediaViewer(),
bootstrap = new mw.mmv.MultimediaViewerBootstrap(),
title = new mw.Title( 'File:This_should_show_up_in_document_title.png' ),
oldDocumentTitle = document.title;

View file

@ -42,5 +42,20 @@
};
};
/**
* Returns a viewer object with all the appropriate placeholder functions.
* @returns {[type]} [description]
*/
MTH.getMultimediaViewer = function () {
return new mw.mmv.MultimediaViewer( {
imageQueryParameter: $.noop,
language: $.noop,
recordVirtualViewBeaconURI: $.noop,
extensions: function () {
return { 'jpg' : 'default' };
}
} );
};
mw.mmv.testHelpers = MTH;
} )( mediaWiki, jQuery );