If the exposed function is called from a gadget or user script but
no watch/unwatch link was found the function fails even if the
first parameter is a valid jQuery object as required by the
documentation. $link.attr(...) returns null and null.match(...)
fails.
A very simple example why this can happen is as follows:
mw.page.watch.updateWatchLink( $( '#ca-watch a' ), 'watch', 'loading' );
mw.page.watch.updateWatchLink( $( '#ca-watch a.loading' ), 'unwatch' );
(starts the spinning loading animation and tries to stop it
afterwards but may fail if something went wrong, e.g. the user
clicked the star).
The action parameter needs to be checked because it is used to
build an ID and a message key. Bad values turn the page in an
unrecoverable state (the watch link gets an empty action=, the
label turns into something <undefined> and no script can recover
that broken state since the ID turned into whatever). While such a
check is not necesarry in most cases it is here, because the
function is exposed.
Change-Id: I6ee9a7ee6b7c0fc7a5444674afd1ed6f8cacc858
function updateWatchLink( $link, action, state ) {
var accesskeyTip, msgKey, $li, otherAction;
function updateWatchLink( $link, action, state ) {
var accesskeyTip, msgKey, $li, otherAction;
+ // A valid but empty jQuery object shouldn't throw a TypeError
+ if ( !$link.length ) {
+ return;
+ }
+
+ // Invalid actions shouldn't silently turn the page in an unrecoverable state
+ if ( action !== 'watch' && action !== 'unwatch' ) {
+ throw new Error( 'Invalid action' );
+ }
+
// message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
msgKey = state === 'loading' ? action + 'ing' : action;
otherAction = action === 'watch' ? 'unwatch' : 'watch';
// message keys 'watch', 'watching', 'unwatch' or 'unwatching'.
msgKey = state === 'loading' ? action + 'ing' : action;
otherAction = action === 'watch' ? 'unwatch' : 'watch';
function mwUriGetAction( url ) {
var action, actionPaths, key, i, m, parts;
function mwUriGetAction( url ) {
var action, actionPaths, key, i, m, parts;
- actionPaths = mw.config.get( 'wgActionPaths' );
-
// TODO: Does MediaWiki give action path or query param
// precedence ? If the former, move this to the bottom
action = mw.util.getParamValue( 'action', url );
// TODO: Does MediaWiki give action path or query param
// precedence ? If the former, move this to the bottom
action = mw.util.getParamValue( 'action', url );
+ actionPaths = mw.config.get( 'wgActionPaths' );
for ( key in actionPaths ) {
if ( actionPaths.hasOwnProperty( key ) ) {
parts = actionPaths[key].split( '$1' );
for ( key in actionPaths ) {
if ( actionPaths.hasOwnProperty( key ) ) {
parts = actionPaths[key].split( '$1' );