mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/MultimediaViewer
synced 2024-11-28 01:50:09 +00:00
Merge "Router handling code should be centralized into mmv.bootstrap"
This commit is contained in:
commit
999893fd25
|
@ -17,6 +17,12 @@
|
||||||
|
|
||||||
( function () {
|
( function () {
|
||||||
var MMVB;
|
var MMVB;
|
||||||
|
var mwRouter = require( 'mediawiki.router' );
|
||||||
|
|
||||||
|
// We pass this to history.pushState/replaceState to indicate that we're controlling the page URL.
|
||||||
|
// Then we look for this marker on page load so that if the page is refreshed, we don't generate an
|
||||||
|
// extra history entry.
|
||||||
|
var MANAGED_STATE = 'MMV was here!';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrap code listening to thumb clicks checking the initial location.hash
|
* Bootstrap code listening to thumb clicks checking the initial location.hash
|
||||||
|
@ -60,10 +66,44 @@
|
||||||
// this will run initially and then every time the content changes,
|
// this will run initially and then every time the content changes,
|
||||||
// e.g. via a VE edit or pagination in a multipage file
|
// e.g. via a VE edit or pagination in a multipage file
|
||||||
mw.hook( 'wikipage.content' ).add( this.processThumbs.bind( this ) );
|
mw.hook( 'wikipage.content' ).add( this.processThumbs.bind( this ) );
|
||||||
|
|
||||||
|
// Setup the router
|
||||||
|
this.setupRouter( mwRouter );
|
||||||
}
|
}
|
||||||
|
|
||||||
MMVB = MultimediaViewerBootstrap.prototype;
|
MMVB = MultimediaViewerBootstrap.prototype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Routes to a given file.
|
||||||
|
*
|
||||||
|
* @param {string} fileName
|
||||||
|
*/
|
||||||
|
MMVB.route = function ( fileName ) {
|
||||||
|
this.loadViewer( true ).then( function ( viewer ) {
|
||||||
|
var fileTitle;
|
||||||
|
viewer.comingFromHashChange = true;
|
||||||
|
try {
|
||||||
|
fileName = decodeURIComponent( fileName );
|
||||||
|
fileTitle = new mw.Title( fileName );
|
||||||
|
viewer.loadImageByTitle( fileTitle );
|
||||||
|
} catch ( err ) {
|
||||||
|
// ignore routes to invalid titles
|
||||||
|
mw.log.warn( err );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the route handlers
|
||||||
|
*
|
||||||
|
* @param {OO.Router} router
|
||||||
|
*/
|
||||||
|
MMVB.setupRouter = function ( router ) {
|
||||||
|
router.addRoute( mw.mmv.ROUTE_REGEXP, this.route.bind( this ) );
|
||||||
|
router.addRoute( mw.mmv.LEGACY_ROUTE_REGEXP, this.route.bind( this ) );
|
||||||
|
this.router = router;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the mmv module asynchronously and passes the thumb data to it
|
* Loads the mmv module asynchronously and passes the thumb data to it
|
||||||
*
|
*
|
||||||
|
@ -284,7 +324,7 @@
|
||||||
caption: this.findCaption( $thumbContainer, $link ) } );
|
caption: this.findCaption( $thumbContainer, $link ) } );
|
||||||
|
|
||||||
$link.add( $enlarge ).on( 'click', function ( e ) {
|
$link.add( $enlarge ).on( 'click', function ( e ) {
|
||||||
return bs.click( this, e, title );
|
return bs.click( e, title );
|
||||||
} );
|
} );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -342,7 +382,7 @@
|
||||||
} );
|
} );
|
||||||
|
|
||||||
$link.on( 'click', function ( e ) {
|
$link.on( 'click', function ( e ) {
|
||||||
return bs.click( this, e, title );
|
return bs.click( e, title );
|
||||||
} );
|
} );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -516,28 +556,25 @@
|
||||||
/**
|
/**
|
||||||
* Opens MediaViewer and loads the given thumbnail. Requires processThumb() to be called first.
|
* Opens MediaViewer and loads the given thumbnail. Requires processThumb() to be called first.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} element Clicked element
|
|
||||||
* @param {mw.Title} title File title
|
* @param {mw.Title} title File title
|
||||||
* @return {jQuery.Promise}
|
* @return {jQuery.Promise}
|
||||||
*/
|
*/
|
||||||
MMVB.openImage = function ( element, title ) {
|
MMVB.openImage = function ( title ) {
|
||||||
this.ensureEventHandlersAreSetUp();
|
this.ensureEventHandlersAreSetUp();
|
||||||
|
var hash = mw.mmv.getMediaHash( title );
|
||||||
return this.loadViewer( true ).then( function ( viewer ) {
|
location.hash = hash;
|
||||||
viewer.loadImageByTitle( title, false );
|
history.replaceState( MANAGED_STATE, null, hash );
|
||||||
} );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a click event on a link
|
* Handles a click event on a link
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} element Clicked element
|
|
||||||
* @param {jQuery.Event} e jQuery event object
|
* @param {jQuery.Event} e jQuery event object
|
||||||
* @param {mw.Title} title File title
|
* @param {mw.Title} title File title
|
||||||
* @return {boolean} a value suitable for an event handler (ie. true if the click should be handled
|
* @return {boolean} a value suitable for an event handler (ie. true if the click should be handled
|
||||||
* by the browser).
|
* by the browser).
|
||||||
*/
|
*/
|
||||||
MMVB.click = function ( element, e, title ) {
|
MMVB.click = function ( e, title ) {
|
||||||
// Do not interfere with non-left clicks or if modifier keys are pressed.
|
// Do not interfere with non-left clicks or if modifier keys are pressed.
|
||||||
if ( ( e.button !== 0 && e.which !== 1 ) || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey ) {
|
if ( ( e.button !== 0 && e.which !== 1 ) || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey ) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -553,7 +590,8 @@
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.openImage( element, title );
|
// Mark the state so that if the page is refreshed, we don't generate an extra history entry
|
||||||
|
this.openImage( title );
|
||||||
|
|
||||||
// calling this late so that in case of errors users at least get to the file page
|
// calling this late so that in case of errors users at least get to the file page
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -576,22 +614,21 @@
|
||||||
* Handles the browser location hash on pageload or hash change
|
* Handles the browser location hash on pageload or hash change
|
||||||
*/
|
*/
|
||||||
MMVB.hash = function () {
|
MMVB.hash = function () {
|
||||||
var bootstrap = this;
|
var isViewerHash = this.isViewerHash();
|
||||||
|
|
||||||
// There is no point loading the mmv if it isn't loaded yet for hash changes unrelated to the mmv
|
// There is no point loading the mmv if it isn't loaded yet for hash changes unrelated to the mmv
|
||||||
// Such as anchor links on the page
|
// Such as anchor links on the page
|
||||||
if ( !this.viewerInitialized && !this.isViewerHash() ) {
|
if ( !this.viewerInitialized && !isViewerHash ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadViewer( this.isViewerHash() ).then( function ( viewer ) {
|
var hash = location.hash;
|
||||||
viewer.router.checkRoute();
|
if ( window.history.state !== MANAGED_STATE ) {
|
||||||
// this is an ugly temporary fix to avoid a black screen of death when
|
// First replace the current URL with a URL with a hash.
|
||||||
// the page is loaded with an invalid MMV url
|
history.replaceState( null, null, '#' );
|
||||||
if ( !viewer.isOpen ) {
|
history.pushState( MANAGED_STATE, null, hash );
|
||||||
bootstrap.cleanupOverlay();
|
}
|
||||||
}
|
this.router.checkRoute();
|
||||||
} );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -252,10 +252,9 @@
|
||||||
* Loads a specified image.
|
* Loads a specified image.
|
||||||
*
|
*
|
||||||
* @param {mw.mmv.LightboxImage} image
|
* @param {mw.mmv.LightboxImage} image
|
||||||
* @param {HTMLImageElement} initialImage A thumbnail to use as placeholder while the image loads
|
* @param {HTMLImageElement} initialImage A thumbnail to use as placeholder while the image loadsx
|
||||||
* @param {boolean} useReplaceState Whether to update history entry to avoid long history queues
|
|
||||||
*/
|
*/
|
||||||
MMVP.loadImage = function ( image, initialImage, useReplaceState ) {
|
MMVP.loadImage = function ( image, initialImage ) {
|
||||||
var imageWidths,
|
var imageWidths,
|
||||||
imagePromise,
|
imagePromise,
|
||||||
metadataPromise,
|
metadataPromise,
|
||||||
|
@ -279,7 +278,7 @@
|
||||||
this.ui.empty();
|
this.ui.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setMediaHash( useReplaceState );
|
this.setTitle();
|
||||||
|
|
||||||
// At this point we can't show the thumbnail because we don't
|
// At this point we can't show the thumbnail because we don't
|
||||||
// know what size it should be. We still assign it to allow for
|
// know what size it should be. We still assign it to allow for
|
||||||
|
@ -382,9 +381,8 @@
|
||||||
* Loads an image by its title
|
* Loads an image by its title
|
||||||
*
|
*
|
||||||
* @param {mw.Title} title
|
* @param {mw.Title} title
|
||||||
* @param {boolean} useReplaceState Whether to update history entry to avoid long history queues
|
|
||||||
*/
|
*/
|
||||||
MMVP.loadImageByTitle = function ( title, useReplaceState ) {
|
MMVP.loadImageByTitle = function ( title ) {
|
||||||
var i, thumb;
|
var i, thumb;
|
||||||
|
|
||||||
if ( !this.thumbs || !this.thumbs.length ) {
|
if ( !this.thumbs || !this.thumbs.length ) {
|
||||||
|
@ -394,7 +392,7 @@
|
||||||
for ( i = 0; i < this.thumbs.length; i++ ) {
|
for ( i = 0; i < this.thumbs.length; i++ ) {
|
||||||
thumb = this.thumbs[ i ];
|
thumb = this.thumbs[ i ];
|
||||||
if ( thumb.title.getPrefixedText() === title.getPrefixedText() ) {
|
if ( thumb.title.getPrefixedText() === title.getPrefixedText() ) {
|
||||||
this.loadImage( thumb.image, thumb.$thumb.clone()[ 0 ], useReplaceState );
|
this.loadImage( thumb.image, thumb.$thumb.clone()[ 0 ] );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -835,6 +833,10 @@
|
||||||
|
|
||||||
thumb = this.thumbs[ index ];
|
thumb = this.thumbs[ index ];
|
||||||
this.loadImage( thumb.image, thumb.$thumb.clone()[ 0 ] );
|
this.loadImage( thumb.image, thumb.$thumb.clone()[ 0 ] );
|
||||||
|
router.navigateTo( null, {
|
||||||
|
path: mw.mmv.getMediaHash( thumb.title ),
|
||||||
|
useReplaceState: true
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -877,21 +879,6 @@
|
||||||
* Sets up the route handlers
|
* Sets up the route handlers
|
||||||
*/
|
*/
|
||||||
MMVP.setupRouter = function () {
|
MMVP.setupRouter = function () {
|
||||||
function route( fileName ) {
|
|
||||||
var fileTitle;
|
|
||||||
comingFromHashChange = true;
|
|
||||||
try {
|
|
||||||
fileName = decodeURIComponent( fileName );
|
|
||||||
fileTitle = new mw.Title( fileName );
|
|
||||||
this.loadImageByTitle( fileTitle );
|
|
||||||
} catch ( err ) {
|
|
||||||
// ignore routes to invalid titles
|
|
||||||
mw.log.warn( err );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.router.addRoute( mw.mmv.ROUTE_REGEXP, route.bind( this ) );
|
|
||||||
this.router.addRoute( mw.mmv.LEGACY_ROUTE_REGEXP, route.bind( this ) );
|
|
||||||
|
|
||||||
// handle empty hashes, and anchor links (page sections)
|
// handle empty hashes, and anchor links (page sections)
|
||||||
this.router.addRoute( /^[^/]*$/, function () {
|
this.router.addRoute( /^[^/]*$/, function () {
|
||||||
if ( this.isOpen ) {
|
if ( this.isOpen ) {
|
||||||
|
@ -908,22 +895,9 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the hash to reflect an open image file
|
* Updates the page title to reflect the current title.
|
||||||
*
|
|
||||||
* @param {boolean} useReplaceState Whether to update history entry to avoid long history queues
|
|
||||||
*/
|
*/
|
||||||
MMVP.setMediaHash = function ( useReplaceState ) {
|
MMVP.setTitle = function () {
|
||||||
if ( useReplaceState === undefined ) {
|
|
||||||
useReplaceState = true;
|
|
||||||
}
|
|
||||||
if ( comingFromHashChange ) {
|
|
||||||
comingFromHashChange = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.router.navigateTo( document.title, {
|
|
||||||
path: mw.mmv.getMediaHash( this.currentImageFileTitle ),
|
|
||||||
useReplaceState: useReplaceState
|
|
||||||
} );
|
|
||||||
// update title after route change, see T225387
|
// update title after route change, see T225387
|
||||||
document.title = this.createDocumentTitle( this.currentImageFileTitle );
|
document.title = this.createDocumentTitle( this.currentImageFileTitle );
|
||||||
};
|
};
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
|
|
||||||
bootstrap.getViewer = function () {
|
bootstrap.getViewer = function () {
|
||||||
return viewer || {
|
return viewer || {
|
||||||
|
loadImageByTitle: function () {},
|
||||||
initWithThumbs: function () {},
|
initWithThumbs: function () {},
|
||||||
hash: function () {},
|
hash: function () {},
|
||||||
router: { checkRoute: function () {} }
|
router: { checkRoute: function () {} }
|
||||||
|
@ -91,7 +92,6 @@
|
||||||
// without us interfering with another immediate change
|
// without us interfering with another immediate change
|
||||||
setTimeout( function () {
|
setTimeout( function () {
|
||||||
location.hash = hash;
|
location.hash = hash;
|
||||||
bootstrap.hash();
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
return mw.mmv.testHelpers.waitForAsync().then( function () {
|
return mw.mmv.testHelpers.waitForAsync().then( function () {
|
||||||
|
@ -135,19 +135,10 @@
|
||||||
// trigger first click, which will cause MMV to be loaded (which we've
|
// trigger first click, which will cause MMV to be loaded (which we've
|
||||||
// set up to fail)
|
// set up to fail)
|
||||||
event = new $.Event( 'click', { button: 0, which: 1 } );
|
event = new $.Event( 'click', { button: 0, which: 1 } );
|
||||||
returnValue = bootstrap.click( {}, event, mw.Title.newFromText( 'Foo' ) );
|
returnValue = bootstrap.click( event, mw.Title.newFromText( 'Foo' ) );
|
||||||
clock.tick( 10 );
|
clock.tick( 10 );
|
||||||
assert.true( event.isDefaultPrevented(), 'First click is caught' );
|
assert.true( event.isDefaultPrevented(), 'First click is caught' );
|
||||||
assert.strictEqual( returnValue, false, 'First click is caught' );
|
assert.strictEqual( returnValue, false, 'First click is caught' );
|
||||||
|
|
||||||
// wait until MMW is loaded (or failed to load, in this case) before we
|
|
||||||
// trigger another click - which should then not be caught
|
|
||||||
event = new $.Event( 'click', { button: 0, which: 1 } );
|
|
||||||
returnValue = bootstrap.click( {}, event, mw.Title.newFromText( 'Foo' ) );
|
|
||||||
clock.tick( 10 );
|
|
||||||
assert.strictEqual( event.isDefaultPrevented(), false, 'Click after loading failure is not caught' );
|
|
||||||
assert.notStrictEqual( returnValue, false, 'Click after loading failure is not caught' );
|
|
||||||
|
|
||||||
clock.restore();
|
clock.restore();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
@ -307,15 +298,13 @@
|
||||||
QUnit.test( 'Ensure that the correct title is loaded when clicking', function ( assert ) {
|
QUnit.test( 'Ensure that the correct title is loaded when clicking', function ( assert ) {
|
||||||
var bootstrap,
|
var bootstrap,
|
||||||
viewer = { initWithThumbs: function () {}, loadImageByTitle: this.sandbox.stub() },
|
viewer = { initWithThumbs: function () {}, loadImageByTitle: this.sandbox.stub() },
|
||||||
$div = createGallery( 'foo.jpg' ),
|
|
||||||
$link = $div.find( 'a.image' ),
|
|
||||||
clock = this.sandbox.useFakeTimers();
|
clock = this.sandbox.useFakeTimers();
|
||||||
|
|
||||||
// Create a new bootstrap object to trigger the DOM scan, etc.
|
// Create a new bootstrap object to trigger the DOM scan, etc.
|
||||||
bootstrap = createBootstrap( viewer );
|
bootstrap = createBootstrap( viewer );
|
||||||
this.sandbox.stub( bootstrap, 'setupOverlay' );
|
this.sandbox.stub( bootstrap, 'setupOverlay' );
|
||||||
|
|
||||||
$link.trigger( { type: 'click', which: 1 } );
|
bootstrap.route( 'File:Foo.jpg' );
|
||||||
clock.tick( 10 );
|
clock.tick( 10 );
|
||||||
assert.true( bootstrap.setupOverlay.called, 'Overlay was set up' );
|
assert.true( bootstrap.setupOverlay.called, 'Overlay was set up' );
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
|
@ -324,6 +313,7 @@
|
||||||
'Titles are identical'
|
'Titles are identical'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
clock.tick( 10 );
|
||||||
clock.restore();
|
clock.restore();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
|
@ -696,9 +696,11 @@
|
||||||
title = new mw.Title( 'File:This_should_show_up_in_document_title.png' ),
|
title = new mw.Title( 'File:This_should_show_up_in_document_title.png' ),
|
||||||
oldDocumentTitle = document.title;
|
oldDocumentTitle = document.title;
|
||||||
|
|
||||||
|
this.sandbox.stub( bootstrap.router, 'back' );
|
||||||
|
this.sandbox.stub( mw.loader, 'using' ).returns( $.Deferred().resolve( viewer ) );
|
||||||
viewer.currentImageFileTitle = title;
|
viewer.currentImageFileTitle = title;
|
||||||
bootstrap.setupEventHandlers();
|
bootstrap.setupEventHandlers();
|
||||||
viewer.setMediaHash();
|
viewer.setTitle();
|
||||||
|
|
||||||
assert.notStrictEqual( document.title.match( title.getNameText() ), null, 'File name is visible in title' );
|
assert.notStrictEqual( document.title.match( title.getNameText() ), null, 'File name is visible in title' );
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue