Split /resources into /resources/lib and /resources/src
[lhc/web/wiklou.git] / resources / src / mediawiki.page / mediawiki.page.watch.ajax.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 // The name of the page to watch or unwatch
9 var title = mw.config.get( 'wgRelevantPageName', mw.config.get( 'wgPageName' ) );
10
11 /**
12 * Update the link text, link href attribute and (if applicable)
13 * "loading" class.
14 *
15 * @param {jQuery} $link Anchor tag of (un)watch link
16 * @param {string} action One of 'watch', 'unwatch'
17 * @param {string} [state="idle"] 'idle' or 'loading'. Default is 'idle'
18 */
19 function updateWatchLink( $link, action, state ) {
20 var accesskeyTip, msgKey, $li, otherAction;
21
22 // A valid but empty jQuery object shouldn't throw a TypeError
23 if ( !$link.length ) {
24 return;
25 }
26
27 // Invalid actions shouldn't silently turn the page in an unrecoverable state
28 if ( action !== 'watch' && action !== 'unwatch' ) {
29 throw new Error( 'Invalid action' );
30 }
31
32 // message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
33 msgKey = state === 'loading' ? action + 'ing' : action;
34 otherAction = action === 'watch' ? 'unwatch' : 'watch';
35 accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp );
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 ( accesskeyTip ? ' ' + accesskeyTip[0] : '' )
50 )
51 .attr( 'href', mw.util.wikiScript() + '?' + $.param( {
52 title: title,
53 action: action
54 } )
55 );
56
57 // Most common ID style
58 if ( $li.prop( 'id' ) === 'ca-' + otherAction ) {
59 $li.prop( 'id', 'ca-' + action );
60 }
61
62 // Special case for vector icon
63 if ( $li.hasClass( 'icon' ) ) {
64 if ( state === 'loading' ) {
65 $link.addClass( 'loading' );
66 } else {
67 $link.removeClass( 'loading' );
68 }
69 }
70 }
71
72 /**
73 * TODO: This should be moved somewhere more accessible.
74 *
75 * @private
76 * @param {string} url
77 * @return {string} The extracted action, defaults to 'view'
78 */
79 function mwUriGetAction( url ) {
80 var action, actionPaths, key, i, m, parts;
81
82 // TODO: Does MediaWiki give action path or query param
83 // precedence? If the former, move this to the bottom
84 action = mw.util.getParamValue( 'action', url );
85 if ( action !== null ) {
86 return action;
87 }
88
89 actionPaths = mw.config.get( 'wgActionPaths' );
90 for ( key in actionPaths ) {
91 if ( actionPaths.hasOwnProperty( key ) ) {
92 parts = actionPaths[key].split( '$1' );
93 for ( i = 0; i < parts.length; i++ ) {
94 parts[i] = $.escapeRE( parts[i] );
95 }
96 m = new RegExp( parts.join( '(.+)' ) ).exec( url );
97 if ( m && m[1] ) {
98 return key;
99 }
100
101 }
102 }
103
104 return 'view';
105 }
106
107 // Expose public methods
108 mw.page.watch = {
109 updateWatchLink: updateWatchLink
110 };
111
112 $( function () {
113 var $links = $( '.mw-watchlink a, a.mw-watchlink, ' +
114 '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' +
115 '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' );
116
117 // Allowing people to add inline animated links is a little scary
118 $links = $links.filter( ':not( #bodyContent *, #content * )' );
119
120 $links.click( function ( e ) {
121 var action, api, $link;
122
123 // Start preloading the notification module (normally loaded by mw.notify())
124 mw.loader.load( ['mediawiki.notification'], null, true );
125
126 action = mwUriGetAction( this.href );
127
128 if ( action !== 'watch' && action !== 'unwatch' ) {
129 // Could not extract target action from link url,
130 // let native browsing handle it further
131 return true;
132 }
133 e.preventDefault();
134 e.stopPropagation();
135
136 $link = $( this );
137
138 updateWatchLink( $link, action, 'loading' );
139
140 api = new mw.Api();
141
142 api[action]( title )
143 .done( function ( watchResponse ) {
144 var otherAction = action === 'watch' ? 'unwatch' : 'watch';
145
146 mw.notify( $.parseHTML( watchResponse.message ), {
147 tag: 'watch-self'
148 } );
149
150 // Set link to opposite
151 updateWatchLink( $link, otherAction );
152
153 // Update the "Watch this page" checkbox on action=edit when the
154 // page is watched or unwatched via the tab (bug 12395).
155 $( '#wpWatchthis' ).prop( 'checked', watchResponse.watched !== undefined );
156 } )
157 .fail( function () {
158 var cleanTitle, msg, link;
159
160 // Reset link to non-loading mode
161 updateWatchLink( $link, action );
162
163 // Format error message
164 cleanTitle = title.replace( /_/g, ' ' );
165 link = mw.html.element(
166 'a', {
167 href: mw.util.getUrl( title ),
168 title: cleanTitle
169 }, cleanTitle
170 );
171 msg = mw.message( 'watcherrortext', link );
172
173 // Report to user about the error
174 mw.notify( msg, { tag: 'watch-self' } );
175 } );
176 } );
177 } );
178
179 }( mediaWiki, jQuery ) );