Localisation updates from https://translatewiki.net.
[mediawiki.git] / resources / src / mediawiki.special.watchlist / watchlist.js
blob867cb5f495139583e4191febdc58c25b5e4f38cd
1 /*!
2  * JavaScript for Special:Watchlist
3  */
4 ( function () {
5         function trimStart( s ) {
6                 return s.replace( /^ /, '' );
7         }
9         function trimEnd( s ) {
10                 return s.endsWith( ' ' ) ? s.slice( 0, -1 ) : s;
11         }
13         $( () => {
14                 const api = new mw.Api();
15                 const $resetForm = $( '#mw-watchlist-resetbutton' );
16                 let $progressBar;
18                 // If the user wants to reset their watchlist, use an API call to do so (no reload required)
19                 // Adapted from a user script by User:NQ of English Wikipedia
20                 // (User:NQ/WatchlistResetConfirm.js)
21                 $resetForm.on( 'submit', ( event ) => {
22                         const $button = $resetForm.find( 'input[name=mw-watchlist-reset-submit]' );
24                         event.preventDefault();
26                         // Disable reset button to prevent multiple concurrent requests
27                         $button.prop( 'disabled', true );
29                         if ( !$progressBar ) {
30                                 $progressBar = new OO.ui.ProgressBarWidget( { progress: false } ).$element;
31                                 $progressBar.css( {
32                                         position: 'absolute', width: '100%'
33                                 } );
34                         }
35                         // Show progress bar
36                         $resetForm.append( $progressBar );
38                         // Use action=setnotificationtimestamp to mark all as visited,
39                         // then set all watchlist lines accordingly
40                         api.postWithToken( 'csrf', {
41                                 formatversion: 2, action: 'setnotificationtimestamp', entirewatchlist: true
42                         } ).done( () => {
43                                 // Enable button again
44                                 $button.prop( 'disabled', false );
45                                 // Hide the button because further clicks can not generate any visual changes
46                                 $button.css( 'visibility', 'hidden' );
47                                 $progressBar.detach();
48                                 $( '.mw-changeslist-line-watched' )
49                                         .removeClass( 'mw-changeslist-line-watched' )
50                                         .addClass( 'mw-changeslist-line-not-watched' );
51                         } ).fail( () => {
52                                 // On error, fall back to server-side reset
53                                 // First remove this submit listener and then re-submit the form
54                                 $resetForm.off( 'submit' ).trigger( 'submit' );
55                         } );
56                 } );
58                 // if the user wishes to reload the watchlist whenever a filter changes
59                 if ( mw.user.options.get( 'watchlistreloadautomatically' ) ) {
60                         // add a listener on all form elements in the header form
61                         $( '#mw-watchlist-form input, #mw-watchlist-form select' ).on( 'change', () => {
62                                 // submit the form when one of the input fields is modified
63                                 $( '#mw-watchlist-form' ).trigger( 'submit' );
64                         } );
65                 }
67                 if ( mw.user.options.get( 'watchlistunwatchlinks' ) ) {
68                         // Watch/unwatch toggle link:
69                         // If a page is on the watchlist, a '×' is shown which, when clicked, removes the page from the watchlist.
70                         // After unwatching a page, the '×' becomes a '+', which if clicked re-watches the page.
71                         // Unwatched page entries are struck through and have lowered opacity.
72                         $( '.mw-changeslist' ).on( 'click', '.mw-unwatch-link, .mw-watch-link', function ( event ) {
73                                 const $unwatchLink = $( this ), // EnhancedChangesList uses <table> for each row, while OldChangesList uses <li> for each row
74                                         $watchlistLine = $unwatchLink.closest( 'li, table' )
75                                                 .find( '[data-target-page]' ),
76                                         pageTitle = String( $watchlistLine.data( 'targetPage' ) ),
77                                         isTalk = mw.Title.newFromText( pageTitle ).isTalkPage();
79                                 // Utility function for looping through each watchlist line that matches
80                                 // a certain page or its associated page (e.g. Talk)
81                                 function forEachMatchingTitle( title, callback ) {
83                                         const titleObj = mw.Title.newFromText( title ),
84                                                 associatedTitleObj = titleObj.isTalkPage() ? titleObj.getSubjectPage() : titleObj.getTalkPage(),
85                                                 associatedTitle = associatedTitleObj.getPrefixedText();
86                                         $( '.mw-changeslist-line' ).each( function () {
87                                                 const $line = $( this );
89                                                 $line.find( '[data-target-page]' ).each( function () {
90                                                         const $this = $( this ), rowTitle = String( $this.data( 'targetPage' ) );
91                                                         if ( rowTitle === title || rowTitle === associatedTitle ) {
93                                                                 // EnhancedChangesList groups log entries by performer rather than target page. Therefore...
94                                                                 // * If using OldChangesList, use the <li>
95                                                                 // * If using EnhancedChangesList and $this is part of a grouped log entry, use the <td> sub-entry
96                                                                 // * If using EnhancedChangesList and $this is not part of a grouped log entry, use the <table> grouped entry
97                                                                 const $row =
98                                                                         $this.closest(
99                                                                                 'li, .mw-enhancedchanges-checkbox + table.mw-changeslist-log td[data-target-page], table' );
100                                                                 const $link = $row.find( '.mw-unwatch-link, .mw-watch-link' );
102                                                                 callback( rowTitle, $row, $link );
103                                                         }
104                                                 } );
105                                         } );
106                                 }
108                                 // Preload the notification module for mw.notify
109                                 mw.loader.load( 'mediawiki.notification' );
111                                 // Depending on whether we are watching or unwatching, for each entry of the page (and its associated page i.e. Talk),
112                                 // change the text, tooltip, and non-JS href of the (un)watch button, and update the styling of the watchlist entry.
113                                 // eslint-disable-next-line no-jquery/no-class-state
114                                 if ( $unwatchLink.hasClass( 'mw-unwatch-link' ) ) {
115                                         api.unwatch( pageTitle )
116                                                 .done( () => {
117                                                         forEachMatchingTitle( pageTitle,
118                                                                 ( rowPageTitle, $row, $rowUnwatchLink ) => {
119                                                                         $rowUnwatchLink
120                                                                                 .text( mw.msg( 'watchlist-unwatch-undo' ) )
121                                                                                 .attr( 'title', mw.msg( 'tooltip-ca-watch' ) )
122                                                                                 .attr( 'href',
123                                                                                         mw.util.getUrl( rowPageTitle, { action: 'watch' } ) )
124                                                                                 .removeClass( 'mw-unwatch-link loading' )
125                                                                                 .addClass( 'mw-watch-link' );
126                                                                         $row.find(
127                                                                                 '.mw-changeslist-line-inner, .mw-enhanced-rc-nested' )
128                                                                                 .addBack( '.mw-enhanced-rc-nested' ) // For matching log sub-entry
129                                                                                 .addClass( 'mw-changelist-line-inner-unwatched' );
130                                                                 } );
132                                                         mw.notify(
133                                                                 mw.message( isTalk ? 'removedwatchtext-talk' : 'removedwatchtext',
134                                                                         pageTitle ), { tag: 'watch-self' } );
135                                                 } );
136                                 } else {
137                                         api.watch( pageTitle )
138                                                 .then( () => {
139                                                         forEachMatchingTitle( pageTitle,
140                                                                 ( rowPageTitle, $row, $rowUnwatchLink ) => {
141                                                                         $rowUnwatchLink
142                                                                                 .text( mw.msg( 'watchlist-unwatch' ) )
143                                                                                 .attr( 'title', mw.msg( 'tooltip-ca-unwatch' ) )
144                                                                                 .attr( 'href',
145                                                                                         mw.util.getUrl( rowPageTitle, { action: 'unwatch' } ) )
146                                                                                 .removeClass( 'mw-watch-link loading' )
147                                                                                 .addClass( 'mw-unwatch-link' );
148                                                                         $row.find( '.mw-changelist-line-inner-unwatched' )
149                                                                                 .addBack( '.mw-enhanced-rc-nested' )
150                                                                                 .removeClass( 'mw-changelist-line-inner-unwatched' );
151                                                                         $row.find( '.mw-changesList-watchlistExpiry' ).each( function () {
152                                                                                 // Add the missing semicolon (T266747)
153                                                                                 const $expiry = $( this );
154                                                                                 $expiry.next( '.mw-changeslist-separator' )
155                                                                                         .addClass( 'mw-changeslist-separator--semicolon' )
156                                                                                         .removeClass( 'mw-changeslist-separator' );
157                                                                                 // Remove the spaces before and after the expiry icon
158                                                                                 this.nextSibling.nodeValue = trimStart( this.nextSibling.nodeValue );
159                                                                                 this.previousSibling.nodeValue = trimEnd( this.previousSibling.nodeValue );
160                                                                                 // Remove the icon
161                                                                                 $expiry.remove();
162                                                                         } );
163                                                                 } );
165                                                         mw.notify(
166                                                                 mw.message( isTalk ? 'addedwatchtext-talk' : 'addedwatchtext',
167                                                                         pageTitle ), { tag: 'watch-self' } );
168                                                 } );
169                                 }
171                                 event.preventDefault();
172                                 event.stopPropagation();
173                                 $unwatchLink.trigger( 'blur' );
174                         } );
175                 }
176         } );
178 }() );