Localisation updates from https://translatewiki.net.
[mediawiki.git] / resources / src / mediawiki.page.image.pagination.js
blobfc53b0bdac372ef0b711c67241ba1eff6fee7e71
1 /*!
2  * Implement AJAX navigation for multi-page images so the user may browse without a full page reload.
3  */
5 ( function () {
6         let jqXhr, $multipageimage, $spinner,
7                 cacheOrder = [];
8         const cache = {};
10         /* Fetch the next page, caching up to 10 last-loaded pages.
11          * @param {string} url
12          * @return {jQuery.Promise}
13          */
14         function fetchPageData( url ) {
15                 if ( jqXhr && jqXhr.abort ) {
16                         // Prevent race conditions and piling up pending requests
17                         jqXhr.abort();
18                 }
19                 jqXhr = undefined;
21                 // Try the cache
22                 if ( cache[ url ] ) {
23                         // Update access freshness
24                         cacheOrder.splice( cacheOrder.indexOf( url ), 1 );
25                         cacheOrder.push( url );
26                         return $.Deferred().resolve( cache[ url ] ).promise();
27                 }
29                 // TODO Don't fetch the entire page. Ideally we'd only fetch the content portion or the data
30                 // (thumbnail urls) and update the interface manually.
31                 jqXhr = $.ajax( url ).then( ( data ) => $( data ).find( '.mw-filepage-multipage' ).contents() );
33                 // Handle cache updates
34                 jqXhr.done( ( $contents ) => {
35                         jqXhr = undefined;
37                         // Cache the newly loaded page
38                         cache[ url ] = $contents;
39                         cacheOrder.push( url );
41                         // Remove the oldest entry if we're over the limit
42                         if ( cacheOrder.length > 10 ) {
43                                 delete cache[ cacheOrder[ 0 ] ];
44                                 cacheOrder = cacheOrder.slice( 1 );
45                         }
46                 } );
48                 return jqXhr.promise();
49         }
51         /* Fetch the next page and use jQuery to swap the table.multipageimage contents.
52          * @param {string} url
53          * @param {boolean} [hist=false] Whether this is a load triggered by history navigation (if
54          *   true, this function won't push a new history state, for the browser did so already).
55          */
56         function switchPage( url, hist ) {
57                 // Start fetching data (might be cached)
58                 const promise = fetchPageData( url );
60                 // Add a new spinner if one doesn't already exist and the data is not already ready
61                 if ( !$spinner && promise.state() !== 'resolved' ) {
62                         $spinner = $.createSpinner( {
63                                 size: 'large',
64                                 type: 'block'
65                         } )
66                                 // Copy the old content dimensions equal so that the current scroll position is not
67                                 // lost between emptying the table is and receiving the new contents.
68                                 .css( {
69                                         height: $multipageimage.outerHeight(),
70                                         width: $multipageimage.outerWidth()
71                                 } );
73                         $multipageimage.empty().append( $spinner );
74                 }
76                 promise.done( ( $contents ) => {
77                         $spinner = undefined;
79                         // Replace table contents
80                         $multipageimage.empty().append( $contents.clone() );
82                         bindPageNavigation( $multipageimage );
84                         // Fire hook because the page's content has changed
85                         mw.hook( 'wikipage.content' ).fire( $multipageimage );
87                         // Update browser history and address bar. But not if we came here from a history
88                         // event, in which case the url is already updated by the browser.
89                         if ( !hist ) {
90                                 history.pushState( { tag: 'mw-pagination' }, document.title, url );
91                         }
92                 } );
93         }
95         function bindPageNavigation( $container ) {
96                 $container.find( '.mw-filepage-multipage-navigation' ).one( 'click', 'a', function ( e ) {
97                         // Generate the same URL on client side as the one generated in ImagePage::openShowImage.
98                         // We avoid using the URL in the link directly since it could have been manipulated (T68608)
99                         const page = mw.util.getParamValue( 'page', this.href );
100                         const url = mw.util.getUrl( null, page ? { page: page } : {} );
102                         switchPage( url );
103                         e.preventDefault();
104                 } );
106                 $container.find( 'form[name="pageselector"]' ).one( 'change submit', function ( e ) {
107                         switchPage( this.action + '?' + $( this ).serialize() );
108                         e.preventDefault();
109                 } );
110         }
112         $( () => {
113                 if ( mw.config.get( 'wgCanonicalNamespace' ) !== 'File' ) {
114                         return;
115                 }
116                 $multipageimage = $( '.mw-filepage-multipage' );
117                 if ( !$multipageimage.length ) {
118                         return;
119                 }
121                 bindPageNavigation( $multipageimage );
123                 // Update the url using the History API
124                 history.replaceState( { tag: 'mw-pagination' }, '' );
125                 $( window ).on( 'popstate', ( e ) => {
126                         const state = e.originalEvent.state;
127                         if ( state && state.tag === 'mw-pagination' ) {
128                                 switchPage( location.href, true );
129                         }
130                 } );
131         } );
132 }() );