Merge "Merge MultimediaViewer bootstrap modules"

This commit is contained in:
jenkins-bot 2024-10-16 23:38:19 +00:00 committed by Gerrit Code Review
commit d882ec8a93
12 changed files with 99 additions and 178 deletions

View file

@ -18,15 +18,7 @@
},
{
"resourceModule": "mmv.bootstrap",
"maxSize": "5.9 kB"
},
{
"resourceModule": "mmv.bootstrap.autostart",
"maxSize": "1 kB"
},
{
"resourceModule": "mmv.head",
"maxSize": "1 kB"
"maxSize": "5.6 kB"
}
]
}

View file

@ -97,8 +97,7 @@
"mediawiki.user",
"mediawiki.util",
"mmv.bootstrap",
"mmv.codex",
"mmv.head"
"mmv.codex"
],
"messages": [
"license-header",
@ -279,6 +278,7 @@
"extensions": "MediaViewerExtensions"
}
},
"mmv.bootstrap/mmv.bootstrap.autostart.js",
"mmv.bootstrap/mmv.bootstrap.js",
"mmv.bootstrap/mmv.lightboximage.js",
"mmv.bootstrap/mmv.Config.js",
@ -293,8 +293,7 @@
"mediawiki.router",
"mediawiki.Title",
"mediawiki.user",
"mediawiki.storage",
"mmv.head"
"mediawiki.storage"
],
"messages": [
"multimediaviewer-loading",
@ -305,23 +304,12 @@
]
},
"mmv.bootstrap.autostart": {
"packageFiles": [
"mmv.bootstrap.autostart/mmv.bootstrap.autostart.js"
],
"dependencies": [
"mmv.head",
"mmv.bootstrap"
]
"deprecated": "Deprecated in 1.43, alias for mmw.bootstrap",
"dependencies": "mmv.bootstrap"
},
"mmv.head": {
"packageFiles": [
"mmv.head/mmv.head.js",
"mmv.head/base.js"
],
"dependencies": [
"mediawiki.user",
"mediawiki.storage"
]
"deprecated": "Deprecated in 1.43, alias for mmw.bootstrap",
"dependencies": "mmv.bootstrap"
}
},
"ResourceFileModulePaths": {
@ -369,7 +357,6 @@
"tests/qunit/mmv/mmv.testhelpers.js"
],
"dependencies": [
"mmv.head",
"mmv.bootstrap",
"mmv",
"mmv.ui.reuse"

View file

@ -111,7 +111,7 @@ class Hooks implements
$isMobileFrontendView = ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' ) &&
$this->mobileContext && $this->mobileContext->shouldDisplayMobileView();
if ( !$isMobileFrontendView ) {
$out->addModules( [ 'mmv.head', 'mmv.bootstrap.autostart' ] );
$out->addModules( [ 'mmv.bootstrap' ] );
}
}

View file

@ -15,13 +15,74 @@
* along with MediaViewer. If not, see <http://www.gnu.org/licenses/>.
*/
const { isMediaViewerEnabledOnClick } = require( 'mmv.head' );
const api = new mw.Api();
/**
* Contains/retrieves configuration/environment information for MediaViewer.
*/
class Config {
/**
* The media route prefix
*
* @return {string}
*/
static get ROUTE() {
return 'media';
}
/**
* RegExp representing the media route
*
* @return {RegExp}
*/
static get ROUTE_REGEXP() {
return /^\/media\/(.+)$/;
}
/**
* RegExp representing the media position as in "File:foo.jpg/3"
*
* @return {RegExp}
*/
static get POSITION_REGEXP() {
return /\/(\d+)$/;
}
/**
* Regular expression representing the legacy media route
*
* @return {RegExp}
*/
static get LEGACY_ROUTE_REGEXP() {
return /^mediaviewer\/(.+)$/;
}
/**
* Returns true if MediaViewer should handle thumbnail clicks.
*
* @return {boolean}
*/
static isMediaViewerEnabledOnClick() {
return mw.config.get( 'wgMediaViewer' ) && // global opt-out switch, can be set in user JS
mw.config.get( 'wgMediaViewerOnClick' ) && // thumbnail opt-out, can be set in preferences
( mw.user.isNamed() || !mw.storage.get( 'wgMediaViewerOnClick' ) || mw.storage.get( 'wgMediaViewerOnClick' ) === '1' ); // thumbnail opt-out for anons
}
/**
* Returns the location hash (route string) for the given file title.
*
* @param {string} imageFileTitle the file title
* @param {number} [position] the relative position of this image to others with same file
* @return {string} the location hash
* @member mw.mmv
*/
static getMediaHash( imageFileTitle, position ) {
return position > 1 ?
`#/${ this.ROUTE }/${ encodeURI( imageFileTitle ) }/${ position }` :
`#/${ this.ROUTE }/${ encodeURI( imageFileTitle ) }`;
}
/**
* (Semi-)permanently stores the setting whether MediaViewer should handle thumbnail clicks.
* - for logged-in users, we use preferences
@ -79,7 +140,7 @@ class Config {
* @return {boolean}
*/
static shouldShowStatusInfo() {
return !isMediaViewerEnabledOnClick( mw.config, mw.user, mw.storage ) && mw.storage.get( 'mmv-showStatusInfo' ) === '1';
return !this.isMediaViewerEnabledOnClick() && mw.storage.get( 'mmv-showStatusInfo' ) === '1';
}
/**
@ -112,4 +173,5 @@ class Config {
}
}
mw.mmv = Config;
module.exports = Config;

View file

@ -18,10 +18,13 @@
// This file is used to do the global initialization that we want on the real pages,
// but do not want in the tests.
const { MultimediaViewerBootstrap } = require( 'mmv.bootstrap' );
const Config = require( './mmv.Config.js' );
const MultimediaViewerBootstrap = require( './mmv.bootstrap.js' );
const LightboxImage = require( './mmv.lightboximage.js' );
const HtmlUtils = require( './mmv.HtmlUtils.js' );
const bootstrap = new MultimediaViewerBootstrap();
$( bootstrap.setupEventHandlers.bind( bootstrap ) );
module.exports = bootstrap;
module.exports = { MultimediaViewerBootstrap, LightboxImage, Config, HtmlUtils };

View file

@ -15,7 +15,6 @@
* along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>.
*/
const { getMediaHash, ROUTE_REGEXP, POSITION_REGEXP, LEGACY_ROUTE_REGEXP, isMediaViewerEnabledOnClick } = require( 'mmv.head' );
const Config = require( './mmv.Config.js' );
const HtmlUtils = require( './mmv.HtmlUtils.js' );
const LightboxImage = require( './mmv.lightboximage.js' );
@ -70,10 +69,10 @@ class MultimediaViewerBootstrap {
let fileTitle;
viewer.comingFromHashChange = true;
try {
let position = fileName.match( POSITION_REGEXP );
let position = fileName.match( Config.POSITION_REGEXP );
if ( position ) {
position = +position[ 1 ];
fileName = fileName.replace( POSITION_REGEXP, '' );
fileName = fileName.replace( Config.POSITION_REGEXP, '' );
} else {
position = undefined;
}
@ -93,8 +92,8 @@ class MultimediaViewerBootstrap {
* @param {OO.Router} router
*/
setupRouter( router ) {
router.addRoute( ROUTE_REGEXP, this.route.bind( this ) );
router.addRoute( LEGACY_ROUTE_REGEXP, this.route.bind( this ) );
router.addRoute( Config.ROUTE_REGEXP, this.route.bind( this ) );
router.addRoute( Config.LEGACY_ROUTE_REGEXP, this.route.bind( this ) );
this.router = router;
}
@ -206,9 +205,6 @@ class MultimediaViewerBootstrap {
this.$parsoidThumbs.each( ( i, thumb ) => this.processParsoidThumb( thumb ) );
} finally {
this.thumbsReadyDeferred.resolve();
// now that we have set up our real click handler we can remove the temporary
// handler added in mmv.head.js which just replays clicks to the real handler
$( document ).off( 'click.mmv-head' );
}
}
@ -248,7 +244,7 @@ class MultimediaViewerBootstrap {
$thumbContainer.on( {
mouseenter: () => {
// There is no point preloading if clicking the thumb won't open Media Viewer
if ( !isMediaViewerEnabledOnClick() ) {
if ( !Config.isMediaViewerEnabledOnClick() ) {
return;
}
this.preloadOnHoverTimer = setTimeout( () => {
@ -507,7 +503,7 @@ class MultimediaViewerBootstrap {
*/
openImage( image ) {
this.ensureEventHandlersAreSetUp();
const hash = getMediaHash( image.filePageTitle, image.position );
const hash = Config.getMediaHash( image.filePageTitle, image.position );
location.hash = hash;
history.replaceState( MANAGED_STATE, null, hash );
}
@ -527,7 +523,7 @@ class MultimediaViewerBootstrap {
}
// Don't load if someone has specifically stopped us from doing so
if ( !isMediaViewerEnabledOnClick() ) {
if ( !Config.isMediaViewerEnabledOnClick() ) {
return true;
}
@ -553,7 +549,7 @@ class MultimediaViewerBootstrap {
*/
isViewerHash() {
const path = location.hash.slice( 1 );
return path.match( ROUTE_REGEXP ) || path.match( LEGACY_ROUTE_REGEXP );
return path.match( Config.ROUTE_REGEXP ) || path.match( Config.LEGACY_ROUTE_REGEXP );
}
/**
@ -685,4 +681,4 @@ class MultimediaViewerBootstrap {
}
}
module.exports = { MultimediaViewerBootstrap, LightboxImage, Config, HtmlUtils };
module.exports = MultimediaViewerBootstrap;

View file

@ -1,70 +0,0 @@
/*
* 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/>.
*/
// Included on every page which has images so keep it lightweight.
module.exports = {
/**
* The media route prefix
*
* @member mw.mmv
*/
ROUTE: 'media',
/**
* RegExp representing the media route
*
* @member mw.mmv
*/
ROUTE_REGEXP: /^\/media\/(.+)$/,
/**
* RegExp representing the media position as in "File:foo.jpg/3"
*
* @member mw.mmv
*/
POSITION_REGEXP: /\/(\d+)$/,
/**
* @property {RegExp}
* Regular expression representing the legacy media route
* @member mw.mmv
*/
LEGACY_ROUTE_REGEXP: /^mediaviewer\/(.+)$/,
/**
* Returns true if MediaViewer should handle thumbnail clicks.
*
* @param {Map} mwConfig
* @param {Object} mwUser
* @param {mw.SafeStorage} mwStorage
* @return {boolean}
*/
isMediaViewerEnabledOnClick( mwConfig = mw.config, mwUser = mw.user, mwStorage = mw.storage ) {
return mwConfig.get( 'wgMediaViewer' ) && // global opt-out switch, can be set in user JS
mwConfig.get( 'wgMediaViewerOnClick' ) && // thumbnail opt-out, can be set in preferences
( mwUser.isNamed() || !mwStorage.get( 'wgMediaViewerOnClick' ) || mwStorage.get( 'wgMediaViewerOnClick' ) === '1' ); // thumbnail opt-out for anons
},
/**
* Returns the location hash (route string) for the given file title.
*
* @param {string} imageFileTitle the file title
* @param {number} [position] the relative position of this image to others with same file
* @return {string} the location hash
* @member mw.mmv
*/
getMediaHash: ( imageFileTitle, position ) => position > 1 ?
`#/media/${ encodeURI( imageFileTitle ) }/${ position }` :
`#/media/${ encodeURI( imageFileTitle ) }`
};

View file

@ -1,46 +0,0 @@
/*
* 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/>.
*/
const base = require( './base.js' );
mw.mmv = base;
module.exports = base;
// If MediaViewer is disabled by the user, do not set up click handling.
// This is loaded before user JS so we cannot check wgMediaViewer.
if ( base.isMediaViewerEnabledOnClick() ) {
( $( document ) ).on( 'click.mmv-head', 'a.image, a.mw-file-description', ( e ) => {
// Do not interfere with non-left clicks or if modifier keys are pressed.
// Also, make sure we do not get in a loop.
if ( ( e.button !== 0 && e.which !== 1 ) || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey || e.replayed ) {
return;
}
// We wait for document readiness because mw.loader.using writes to the DOM
// which can cause a blank page if it happens before DOM readiness
$( () => {
mw.loader.using( [ 'mmv.bootstrap.autostart' ], ( req ) => {
const bootstrap = req( 'mmv.bootstrap.autostart' );
bootstrap.whenThumbsReady().then( () => {
// We have to copy the properties, passing e doesn't work. Probably because of preventDefault()
$( e.target ).trigger( { type: 'click', which: 1, replayed: true } );
} );
} );
} );
e.preventDefault();
} );
}

View file

@ -15,8 +15,7 @@
* along with MediaViewer. If not, see <http://www.gnu.org/licenses/>.
*/
const { getMediaHash } = require( 'mmv.head' );
const { HtmlUtils } = require( 'mmv.bootstrap' );
const { Config, HtmlUtils } = require( 'mmv.bootstrap' );
/**
* Converts data in various formats needed by the Embed sub-dialog
@ -173,7 +172,7 @@ class EmbedFileFormatter {
return HtmlUtils.jqueryToHtml(
$( '<p>' ).append(
$( '<a>' )
.attr( 'href', info.imageInfo.descriptionUrl + getMediaHash( info.imageInfo.title ) )
.attr( 'href', info.imageInfo.descriptionUrl + Config.getMediaHash( info.imageInfo.title ) )
.append(
$( '<img>' )
.attr( 'src', imgUrl )

View file

@ -15,7 +15,7 @@
* along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>.
*/
const { getMediaHash } = require( 'mmv.head' );
const { Config } = require( 'mmv.bootstrap' );
const Utils = require( './mmv.ui.utils.js' );
const { UiElement } = require( 'mmv' );
@ -56,7 +56,7 @@ class Share extends UiElement {
* @param {ImageModel} image
*/
set( image ) {
const url = image.descriptionUrl + getMediaHash( image.title );
const url = image.descriptionUrl + Config.getMediaHash( image.title );
this.$pageInput.val( url );
}

View file

@ -16,7 +16,6 @@
*/
const { Config } = require( 'mmv.bootstrap' );
const { getMediaHash } = require( 'mmv.head' );
const ViewLogger = require( './logging/mmv.logging.ViewLogger.js' );
const Api = require( './provider/mmv.provider.Api.js' );
const GuessedThumbnailInfo = require( './provider/mmv.provider.GuessedThumbnailInfo.js' );
@ -639,7 +638,7 @@ class MultimediaViewer {
const thumb = this.thumbs[ index ];
this.loadImage( thumb );
router.navigateTo( null, {
path: getMediaHash( thumb.filePageTitle, thumb.position ),
path: Config.getMediaHash( thumb.filePageTitle, thumb.position ),
useReplaceState: true
} );
}

View file

@ -15,7 +15,6 @@
* along with MediaViewer. If not, see <http://www.gnu.org/licenses/>.
*/
const { isMediaViewerEnabledOnClick } = require( 'mmv.head' );
const { Config } = require( 'mmv.bootstrap' );
const { createLocalStorage, getFakeLocalStorage } = require( './mmv.testhelpers.js' );
const config0 = mw.config;
@ -46,39 +45,39 @@ const saveOption = mw.Api.prototype.saveOption;
mw.user.isNamed.returns( true );
mw.config.get.withArgs( 'wgMediaViewer' ).returns( true );
mw.config.get.withArgs( 'wgMediaViewerOnClick' ).returns( true );
assert.strictEqual( isMediaViewerEnabledOnClick( mw.config, mw.user ), true, 'Returns true for logged-in with standard settings' );
assert.strictEqual( Config.isMediaViewerEnabledOnClick(), true, 'Returns true for logged-in with standard settings' );
mw.user.isNamed.returns( true );
mw.config.get.withArgs( 'wgMediaViewer' ).returns( false );
mw.config.get.withArgs( 'wgMediaViewerOnClick' ).returns( true );
assert.strictEqual( isMediaViewerEnabledOnClick( mw.config, mw.user ), false, 'Returns false if opted out via user JS flag' );
assert.strictEqual( Config.isMediaViewerEnabledOnClick(), false, 'Returns false if opted out via user JS flag' );
mw.user.isNamed.returns( true );
mw.config.get.withArgs( 'wgMediaViewer' ).returns( true );
mw.config.get.withArgs( 'wgMediaViewerOnClick' ).returns( false );
assert.strictEqual( isMediaViewerEnabledOnClick( mw.config, mw.user ), false, 'Returns false if opted out via preferences' );
assert.strictEqual( Config.isMediaViewerEnabledOnClick(), false, 'Returns false if opted out via preferences' );
mw.user.isNamed.returns( false );
mw.config.get.withArgs( 'wgMediaViewer' ).returns( false );
mw.config.get.withArgs( 'wgMediaViewerOnClick' ).returns( true );
assert.strictEqual( isMediaViewerEnabledOnClick( mw.config, mw.user ), false, 'Returns false if anon user opted out via user JS flag' );
assert.strictEqual( Config.isMediaViewerEnabledOnClick(), false, 'Returns false if anon user opted out via user JS flag' );
mw.user.isNamed.returns( false );
mw.config.get.withArgs( 'wgMediaViewer' ).returns( true );
mw.config.get.withArgs( 'wgMediaViewerOnClick' ).returns( false );
assert.strictEqual( isMediaViewerEnabledOnClick( mw.config, mw.user ), false, 'Returns false if anon user opted out in some weird way' ); // apparently someone created a browser extension to do this
assert.strictEqual( Config.isMediaViewerEnabledOnClick(), false, 'Returns false if anon user opted out in some weird way' ); // apparently someone created a browser extension to do this
mw.user.isNamed.returns( false );
mw.config.get.withArgs( 'wgMediaViewer' ).returns( true );
mw.config.get.withArgs( 'wgMediaViewerOnClick' ).returns( true );
mw.storage.store.getItem.withArgs( 'wgMediaViewerOnClick' ).returns( null );
assert.strictEqual( isMediaViewerEnabledOnClick( mw.config, mw.user ), true, 'Returns true for anon with standard settings' );
assert.strictEqual( Config.isMediaViewerEnabledOnClick(), true, 'Returns true for anon with standard settings' );
mw.user.isNamed.returns( false );
mw.config.get.withArgs( 'wgMediaViewer' ).returns( true );
mw.config.get.withArgs( 'wgMediaViewerOnClick' ).returns( true );
mw.storage.store.getItem.withArgs( 'wgMediaViewerOnClick' ).returns( '0' );
assert.strictEqual( isMediaViewerEnabledOnClick( mw.config, mw.user ), false, 'Returns true for anon opted out via localSettings' );
assert.strictEqual( Config.isMediaViewerEnabledOnClick(), false, 'Returns true for anon opted out via localSettings' );
} );
QUnit.test( 'setMediaViewerEnabledOnClick sense check', function ( assert ) {