summaryrefslogtreecommitdiff
path: root/www/wiki/resources/src/mediawiki/page/image-pagination.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/resources/src/mediawiki/page/image-pagination.js')
-rw-r--r--www/wiki/resources/src/mediawiki/page/image-pagination.js143
1 files changed, 143 insertions, 0 deletions
diff --git a/www/wiki/resources/src/mediawiki/page/image-pagination.js b/www/wiki/resources/src/mediawiki/page/image-pagination.js
new file mode 100644
index 00000000..06c34a50
--- /dev/null
+++ b/www/wiki/resources/src/mediawiki/page/image-pagination.js
@@ -0,0 +1,143 @@
+/*!
+ * Implement AJAX navigation for multi-page images so the user may browse without a full page reload.
+ */
+
+/* eslint-disable no-use-before-define */
+
+( function ( mw, $ ) {
+ var jqXhr, $multipageimage, $spinner,
+ cache = {},
+ cacheOrder = [];
+
+ /* Fetch the next page, caching up to 10 last-loaded pages.
+ * @param {string} url
+ * @return {jQuery.Promise}
+ */
+ function fetchPageData( url ) {
+ if ( jqXhr && jqXhr.abort ) {
+ // Prevent race conditions and piling up pending requests
+ jqXhr.abort();
+ }
+ jqXhr = undefined;
+
+ // Try the cache
+ if ( cache[ url ] ) {
+ // Update access freshness
+ cacheOrder.splice( cacheOrder.indexOf( url ), 1 );
+ cacheOrder.push( url );
+ return $.Deferred().resolve( cache[ url ] ).promise();
+ }
+
+ // TODO Don't fetch the entire page. Ideally we'd only fetch the content portion or the data
+ // (thumbnail urls) and update the interface manually.
+ jqXhr = $.ajax( url ).then( function ( data ) {
+ return $( data ).find( 'table.multipageimage' ).contents();
+ } );
+
+ // Handle cache updates
+ jqXhr.done( function ( $contents ) {
+ jqXhr = undefined;
+
+ // Cache the newly loaded page
+ cache[ url ] = $contents;
+ cacheOrder.push( url );
+
+ // Remove the oldest entry if we're over the limit
+ if ( cacheOrder.length > 10 ) {
+ delete cache[ cacheOrder[ 0 ] ];
+ cacheOrder = cacheOrder.slice( 1 );
+ }
+ } );
+
+ return jqXhr.promise();
+ }
+
+ /* Fetch the next page and use jQuery to swap the table.multipageimage contents.
+ * @param {string} url
+ * @param {boolean} [hist=false] Whether this is a load triggered by history navigation (if
+ * true, this function won't push a new history state, for the browser did so already).
+ */
+ function switchPage( url, hist ) {
+ var $tr, promise;
+
+ // Start fetching data (might be cached)
+ promise = fetchPageData( url );
+
+ // Add a new spinner if one doesn't already exist and the data is not already ready
+ if ( !$spinner && promise.state() !== 'resolved' ) {
+ $tr = $multipageimage.find( 'tr' );
+ $spinner = $.createSpinner( {
+ size: 'large',
+ type: 'block'
+ } )
+ // Copy the old content dimensions equal so that the current scroll position is not
+ // lost between emptying the table is and receiving the new contents.
+ .css( {
+ height: $tr.outerHeight(),
+ width: $tr.outerWidth()
+ } );
+
+ $multipageimage.empty().append( $spinner );
+ }
+
+ promise.done( function ( $contents ) {
+ $spinner = undefined;
+
+ // Replace table contents
+ $multipageimage.empty().append( $contents.clone() );
+
+ bindPageNavigation( $multipageimage );
+
+ // Fire hook because the page's content has changed
+ mw.hook( 'wikipage.content' ).fire( $multipageimage );
+
+ // Update browser history and address bar. But not if we came here from a history
+ // event, in which case the url is already updated by the browser.
+ if ( history.pushState && !hist ) {
+ history.pushState( { tag: 'mw-pagination' }, document.title, url );
+ }
+ } );
+ }
+
+ function bindPageNavigation( $container ) {
+ $container.find( '.multipageimagenavbox' ).one( 'click', 'a', function ( e ) {
+ var page, url;
+
+ // Generate the same URL on client side as the one generated in ImagePage::openShowImage.
+ // We avoid using the URL in the link directly since it could have been manipulated (T68608)
+ page = Number( mw.util.getParamValue( 'page', this.href ) );
+ url = mw.util.getUrl( mw.config.get( 'wgPageName' ), { page: page } );
+
+ switchPage( url );
+ e.preventDefault();
+ } );
+
+ $container.find( 'form[name="pageselector"]' ).one( 'change submit', function ( e ) {
+ switchPage( this.action + '?' + $( this ).serialize() );
+ e.preventDefault();
+ } );
+ }
+
+ $( function () {
+ if ( mw.config.get( 'wgCanonicalNamespace' ) !== 'File' ) {
+ return;
+ }
+ $multipageimage = $( 'table.multipageimage' );
+ if ( !$multipageimage.length ) {
+ return;
+ }
+
+ bindPageNavigation( $multipageimage );
+
+ // Update the url using the History API (if available)
+ if ( history.pushState && history.replaceState ) {
+ history.replaceState( { tag: 'mw-pagination' }, '' );
+ $( window ).on( 'popstate', function ( e ) {
+ var state = e.originalEvent.state;
+ if ( state && state.tag === 'mw-pagination' ) {
+ switchPage( location.href, true );
+ }
+ } );
+ }
+ } );
+}( mediaWiki, jQuery ) );