2 * Animate watch/unwatch links to use asynchronous API requests to
3 * watch pages, rather than navigating to a different URI.
7 * var watch = require( 'mediawiki.page.watch.ajax' );
8 * watch.updateWatchLink(
14 * @class mw.plugin.page.watch.ajax
17 ( function ( mw
, $ ) {
19 // The name of the page to watch or unwatch
20 title
= mw
.config
.get( 'wgRelevantPageName' );
23 * Update the link text, link href attribute and (if applicable)
26 * @param {jQuery} $link Anchor tag of (un)watch link
27 * @param {string} action One of 'watch', 'unwatch'
28 * @param {string} [state="idle"] 'idle' or 'loading'. Default is 'idle'
30 function updateWatchLink( $link
, action
, state
) {
31 var msgKey
, $li
, otherAction
;
33 // A valid but empty jQuery object shouldn't throw a TypeError
34 if ( !$link
.length
) {
38 // Invalid actions shouldn't silently turn the page in an unrecoverable state
39 if ( action
!== 'watch' && action
!== 'unwatch' ) {
40 throw new Error( 'Invalid action' );
43 // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
44 msgKey
= state
=== 'loading' ? action
+ 'ing' : action
;
45 otherAction
= action
=== 'watch' ? 'unwatch' : 'watch';
46 $li
= $link
.closest( 'li' );
48 // Trigger a 'watchpage' event for this List item.
49 // Announce the otherAction value as the first param.
50 // Used to monitor the state of watch link.
51 // TODO: Revise when system wide hooks are implemented
52 if ( state
=== undefined ) {
53 $li
.trigger( 'watchpage.mw', otherAction
);
57 .text( mw
.msg( msgKey
) )
58 .attr( 'title', mw
.msg( 'tooltip-ca-' + action
) )
59 .updateTooltipAccessKeys()
60 .attr( 'href', mw
.util
.getUrl( title
, { action
: action
} ) );
62 // Most common ID style
63 if ( $li
.prop( 'id' ) === 'ca-' + otherAction
) {
64 $li
.prop( 'id', 'ca-' + action
);
67 if ( state
=== 'loading' ) {
68 $link
.addClass( 'loading' );
70 $link
.removeClass( 'loading' );
75 * TODO: This should be moved somewhere more accessible.
79 * @return {string} The extracted action, defaults to 'view'
81 function mwUriGetAction( url
) {
82 var action
, actionPaths
, key
, i
, m
, parts
;
84 // TODO: Does MediaWiki give action path or query param
85 // precedence? If the former, move this to the bottom
86 action
= mw
.util
.getParamValue( 'action', url
);
87 if ( action
!== null ) {
91 actionPaths
= mw
.config
.get( 'wgActionPaths' );
92 for ( key
in actionPaths
) {
93 if ( actionPaths
.hasOwnProperty( key
) ) {
94 parts
= actionPaths
[ key
].split( '$1' );
95 for ( i
= 0; i
< parts
.length
; i
++ ) {
96 parts
[ i
] = mw
.RegExp
.escape( parts
[ i
] );
98 m
= new RegExp( parts
.join( '(.+)' ) ).exec( url
);
109 // Expose public methods
111 updateWatchLink
: updateWatchLink
113 module
.exports
= watch
;
116 var $links
= $( '.mw-watchlink a[data-mw="interface"], a.mw-watchlink[data-mw="interface"]' );
117 if ( !$links
.length
) {
118 // Fallback to the class-based exclusion method for backwards-compatibility
119 $links
= $( '.mw-watchlink a, a.mw-watchlink' );
120 // Restrict to core interfaces, ignore user-generated content
121 $links
= $links
.filter( ':not( #bodyContent *, #content * )' );
124 $links
.click( function ( e
) {
125 var mwTitle
, action
, api
, $link
;
127 mwTitle
= mw
.Title
.newFromText( title
);
128 action
= mwUriGetAction( this.href
);
130 if ( !mwTitle
|| ( action
!== 'watch' && action
!== 'unwatch' ) ) {
131 // Let native browsing handle the link
139 if ( $link
.hasClass( 'loading' ) ) {
143 updateWatchLink( $link
, action
, 'loading' );
145 // Preload the notification module for mw.notify
146 mw
.loader
.load( 'mediawiki.notification' );
150 api
[ action
]( title
)
151 .done( function ( watchResponse
) {
152 var message
, otherAction
= action
=== 'watch' ? 'unwatch' : 'watch';
154 if ( mwTitle
.getNamespaceId() > 0 && mwTitle
.getNamespaceId() % 2 === 1 ) {
155 message
= action
=== 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
157 message
= action
=== 'watch' ? 'addedwatchtext' : 'removedwatchtext';
160 mw
.notify( mw
.message( message
, mwTitle
.getPrefixedText() ).parseDom(), {
164 // Set link to opposite
165 updateWatchLink( $link
, otherAction
);
167 // Update the "Watch this page" checkbox on action=edit when the
168 // page is watched or unwatched via the tab (T14395).
169 $( '#wpWatchthis' ).prop( 'checked', watchResponse
.watched
=== true );
174 // Reset link to non-loading mode
175 updateWatchLink( $link
, action
);
177 // Format error message
178 link
= mw
.html
.element(
180 href
: mw
.util
.getUrl( title
),
181 title
: mwTitle
.getPrefixedText()
182 }, mwTitle
.getPrefixedText()
184 msg
= mw
.message( 'watcherrortext', link
);
186 // Report to user about the error
195 }( mediaWiki
, jQuery
) );