Merge "Fix incomplete Language::getDatePreferences() documentation"
[lhc/web/wiklou.git] / resources / src / mediawiki / page / watch.js
1 /**
2 * Animate watch/unwatch links to use asynchronous API requests to
3 * watch pages, rather than navigating to a different URI.
4 *
5 * @class mw.page.watch.ajax
6 */
7 ( function ( mw, $ ) {
8 var watch,
9 // The name of the page to watch or unwatch
10 title = mw.config.get( 'wgRelevantPageName' );
11
12 /**
13 * Update the link text, link href attribute and (if applicable)
14 * "loading" class.
15 *
16 * @param {jQuery} $link Anchor tag of (un)watch link
17 * @param {string} action One of 'watch', 'unwatch'
18 * @param {string} [state="idle"] 'idle' or 'loading'. Default is 'idle'
19 */
20 function updateWatchLink( $link, action, state ) {
21 var msgKey, $li, otherAction;
22
23 // A valid but empty jQuery object shouldn't throw a TypeError
24 if ( !$link.length ) {
25 return;
26 }
27
28 // Invalid actions shouldn't silently turn the page in an unrecoverable state
29 if ( action !== 'watch' && action !== 'unwatch' ) {
30 throw new Error( 'Invalid action' );
31 }
32
33 // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
34 msgKey = state === 'loading' ? action + 'ing' : action;
35 otherAction = action === 'watch' ? 'unwatch' : 'watch';
36 $li = $link.closest( 'li' );
37
38 // Trigger a 'watchpage' event for this List item.
39 // Announce the otherAction value as the first param.
40 // Used to monitor the state of watch link.
41 // TODO: Revise when system wide hooks are implemented
42 if ( state === undefined ) {
43 $li.trigger( 'watchpage.mw', otherAction );
44 }
45
46 $link
47 .text( mw.msg( msgKey ) )
48 .attr( 'title', mw.msg( 'tooltip-ca-' + action ) )
49 .updateTooltipAccessKeys()
50 .attr( 'href', mw.util.getUrl( title, { action: action } ) );
51
52 // Most common ID style
53 if ( $li.prop( 'id' ) === 'ca-' + otherAction ) {
54 $li.prop( 'id', 'ca-' + action );
55 }
56
57 if ( state === 'loading' ) {
58 $link.addClass( 'loading' );
59 } else {
60 $link.removeClass( 'loading' );
61 }
62 }
63
64 /**
65 * TODO: This should be moved somewhere more accessible.
66 *
67 * @private
68 * @param {string} url
69 * @return {string} The extracted action, defaults to 'view'
70 */
71 function mwUriGetAction( url ) {
72 var action, actionPaths, key, i, m, parts;
73
74 // TODO: Does MediaWiki give action path or query param
75 // precedence? If the former, move this to the bottom
76 action = mw.util.getParamValue( 'action', url );
77 if ( action !== null ) {
78 return action;
79 }
80
81 actionPaths = mw.config.get( 'wgActionPaths' );
82 for ( key in actionPaths ) {
83 if ( actionPaths.hasOwnProperty( key ) ) {
84 parts = actionPaths[ key ].split( '$1' );
85 for ( i = 0; i < parts.length; i++ ) {
86 parts[ i ] = mw.RegExp.escape( parts[ i ] );
87 }
88 m = new RegExp( parts.join( '(.+)' ) ).exec( url );
89 if ( m && m[ 1 ] ) {
90 return key;
91 }
92
93 }
94 }
95
96 return 'view';
97 }
98
99 // Expose public methods
100 watch = {
101 updateWatchLink: updateWatchLink
102 };
103 module.exports = watch;
104
105 // Deprecated since 1.30
106 mw.log.deprecate( mw, 'page',
107 { watch: watch },
108 'Use require( \'mediawiki.page.watch.ajax\' ) instead.',
109 'mw.page'
110 );
111
112 $( function () {
113 var $links = $( '.mw-watchlink a, a.mw-watchlink' );
114 // Restrict to core interfaces, ignore user-generated content
115 $links = $links.filter( ':not( #bodyContent *, #content * )' );
116
117 $links.click( function ( e ) {
118 var mwTitle, action, api, $link;
119
120 mwTitle = mw.Title.newFromText( title );
121 action = mwUriGetAction( this.href );
122
123 if ( !mwTitle || ( action !== 'watch' && action !== 'unwatch' ) ) {
124 // Let native browsing handle the link
125 return true;
126 }
127 e.preventDefault();
128 e.stopPropagation();
129
130 $link = $( this );
131
132 if ( $link.hasClass( 'loading' ) ) {
133 return;
134 }
135
136 updateWatchLink( $link, action, 'loading' );
137
138 // Preload the notification module for mw.notify
139 mw.loader.load( 'mediawiki.notification' );
140
141 api = new mw.Api();
142
143 api[ action ]( title )
144 .done( function ( watchResponse ) {
145 var message, otherAction = action === 'watch' ? 'unwatch' : 'watch';
146
147 if ( mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
148 message = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
149 } else {
150 message = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
151 }
152
153 mw.notify( mw.message( message, mwTitle.getPrefixedText() ).parseDom(), {
154 tag: 'watch-self'
155 } );
156
157 // Set link to opposite
158 updateWatchLink( $link, otherAction );
159
160 // Update the "Watch this page" checkbox on action=edit when the
161 // page is watched or unwatched via the tab (T14395).
162 $( '#wpWatchthis' ).prop( 'checked', watchResponse.watched === true );
163 } )
164 .fail( function () {
165 var msg, link;
166
167 // Reset link to non-loading mode
168 updateWatchLink( $link, action );
169
170 // Format error message
171 link = mw.html.element(
172 'a', {
173 href: mw.util.getUrl( title ),
174 title: mwTitle.getPrefixedText()
175 }, mwTitle.getPrefixedText()
176 );
177 msg = mw.message( 'watcherrortext', link );
178
179 // Report to user about the error
180 mw.notify( msg, {
181 tag: 'watch-self',
182 type: 'error'
183 } );
184 } );
185 } );
186 } );
187
188 }( mediaWiki, jQuery ) );