summaryrefslogtreecommitdiff
path: root/www/wiki/resources/src/mediawiki.special/mediawiki.special.watchlist.js
blob: 565ed2c974dc12d917cf5e72144d36bc66f8225b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*!
 * JavaScript for Special:Watchlist
 */
( function ( mw, $, OO ) {
	$( function () {
		var api = new mw.Api(), $progressBar, $resetForm = $( '#mw-watchlist-resetbutton' );

		// If the user wants to reset their watchlist, use an API call to do so (no reload required)
		// Adapted from a user script by User:NQ of English Wikipedia
		// (User:NQ/WatchlistResetConfirm.js)
		$resetForm.submit( function ( event ) {
			var $button = $resetForm.find( 'input[name=mw-watchlist-reset-submit]' );

			event.preventDefault();

			// Disable reset button to prevent multiple concurrent requests
			$button.prop( 'disabled', true );

			if ( !$progressBar ) {
				$progressBar = new OO.ui.ProgressBarWidget( { progress: false } ).$element;
				$progressBar.css( {
					position: 'absolute', width: '100%'
				} );
			}
			// Show progress bar
			$resetForm.append( $progressBar );

			// Use action=setnotificationtimestamp to mark all as visited,
			// then set all watchlist lines accordingly
			api.postWithToken( 'csrf', {
				formatversion: 2, action: 'setnotificationtimestamp', entirewatchlist: true
			} ).done( function () {
				// Enable button again
				$button.prop( 'disabled', false );
				// Hide the button because further clicks can not generate any visual changes
				$button.css( 'visibility', 'hidden' );
				$progressBar.detach();
				$( '.mw-changeslist-line-watched' )
					.removeClass( 'mw-changeslist-line-watched' )
					.addClass( 'mw-changeslist-line-not-watched' );
			} ).fail( function () {
				// On error, fall back to server-side reset
				// First remove this submit listener and then re-submit the form
				$resetForm.off( 'submit' ).submit();
			} );
		} );

		// if the user wishes to reload the watchlist whenever a filter changes
		if ( mw.user.options.get( 'watchlistreloadautomatically' ) ) {
			// add a listener on all form elements in the header form
			$( '#mw-watchlist-form input, #mw-watchlist-form select' ).on( 'change', function () {
				// submit the form when one of the input fields is modified
				$( '#mw-watchlist-form' ).submit();
			} );
		}

		if ( mw.user.options.get( 'watchlistunwatchlinks' ) ) {
			// Watch/unwatch toggle link:
			// If a page is on the watchlist, a '×' is shown which, when clicked, removes the page from the watchlist.
			// After unwatching a page, the '×' becomes a '+', which if clicked re-watches the page.
			// Unwatched page entries are struck through and have lowered opacity.
			$( '.mw-changeslist' ).on( 'click', '.mw-unwatch-link, .mw-watch-link', function ( event ) {
				var $unwatchLink = $( this ), // EnhancedChangesList uses <table> for each row, while OldChangesList uses <li> for each row
					$watchlistLine = $unwatchLink.closest( 'li, table' )
						.find( '[data-target-page]' ),
					pageTitle = $watchlistLine.data( 'targetPage' ),
					isTalk = mw.Title.newFromText( pageTitle ).getNamespaceId() % 2 === 1;

				// Utility function for looping through each watchlist line that matches
				// a certain page or its associated page (e.g. Talk)
				function forEachMatchingTitle( title, callback ) {

					var titleObj = mw.Title.newFromText( title ),
						pageNamespaceId = titleObj.getNamespaceId(),
						isTalk = pageNamespaceId % 2 === 1,
						associatedTitle = mw.Title.makeTitle( isTalk ? pageNamespaceId - 1 : pageNamespaceId + 1,
							titleObj.getMainText() ).getPrefixedText();
					$( '.mw-changeslist-line' ).each( function () {
						var $this = $( this ), $row, $unwatchLink;

						$this.find( '[data-target-page]' ).each( function () {
							var $this = $( this ), rowTitle = $this.data( 'targetPage' );
							if ( rowTitle === title || rowTitle === associatedTitle ) {

								// EnhancedChangesList groups log entries by performer rather than target page. Therefore...
								// * If using OldChangesList, use the <li>
								// * If using EnhancedChangesList and $this is part of a grouped log entry, use the <td> sub-entry
								// * If using EnhancedChangesList and $this is not part of a grouped log entry, use the <table> grouped entry
								$row =
									$this.closest(
										'li, table.mw-collapsible.mw-changeslist-log td[data-target-page], table' );
								$unwatchLink = $row.find( '.mw-unwatch-link, .mw-watch-link' );

								callback( rowTitle, $row, $unwatchLink );
							}
						} );
					} );
				}

				// Preload the notification module for mw.notify
				mw.loader.load( 'mediawiki.notification' );

				// Depending on whether we are watching or unwatching, for each entry of the page (and its associated page i.e. Talk),
				// change the text, tooltip, and non-JS href of the (un)watch button, and update the styling of the watchlist entry.
				if ( $unwatchLink.hasClass( 'mw-unwatch-link' ) ) {
					api.unwatch( pageTitle )
						.done( function () {
							forEachMatchingTitle( pageTitle,
								function ( rowPageTitle, $row, $rowUnwatchLink ) {
									$rowUnwatchLink
										.text( mw.msg( 'watchlist-unwatch-undo' ) )
										.attr( 'title', mw.msg( 'tooltip-ca-watch' ) )
										.attr( 'href',
											mw.util.getUrl( rowPageTitle, { action: 'watch' } ) )
										.removeClass( 'mw-unwatch-link loading' )
										.addClass( 'mw-watch-link' );
									$row.find(
										'.mw-changeslist-line-inner, .mw-enhanced-rc-nested' )
										.addBack( '.mw-enhanced-rc-nested' ) // For matching log sub-entry
										.addClass( 'mw-changelist-line-inner-unwatched' );
								} );

							mw.notify(
								mw.message( isTalk ? 'removedwatchtext-talk' : 'removedwatchtext',
									pageTitle ), { tag: 'watch-self' } );
						} );
				} else {
					api.watch( pageTitle )
						.then( function () {
							forEachMatchingTitle( pageTitle,
								function ( rowPageTitle, $row, $rowUnwatchLink ) {
									$rowUnwatchLink
										.text( mw.msg( 'watchlist-unwatch' ) )
										.attr( 'title', mw.msg( 'tooltip-ca-unwatch' ) )
										.attr( 'href',
											mw.util.getUrl( rowPageTitle, { action: 'unwatch' } ) )
										.removeClass( 'mw-watch-link loading' )
										.addClass( 'mw-unwatch-link' );
									$row.find( '.mw-changelist-line-inner-unwatched' )
										.addBack( '.mw-enhanced-rc-nested' )
										.removeClass( 'mw-changelist-line-inner-unwatched' );
								} );

							mw.notify(
								mw.message( isTalk ? 'addedwatchtext-talk' : 'addedwatchtext',
									pageTitle ), { tag: 'watch-self' } );
						} );
				}

				event.preventDefault();
				event.stopPropagation();
				$unwatchLink.blur();
			} );
		}
	} );

}( mediaWiki, jQuery, OO )
);