From b2733fa44c5658201debaab170d1729611baae34 Mon Sep 17 00:00:00 2001 From: Krinkle Date: Tue, 27 Dec 2011 01:21:56 +0000 Subject: [PATCH] [mediawiki.action.watch.ajax.js] Rewrite using mw.Api and fixes bug 27146 * Use mw.Api and new it's new .watch() as of r107350 * No longer get title from url, use wgPageName instead * No longer simple queryParam check for action in url, now supports wgActionPaths as well. * Simplification and speed up (less back and forth between functions and jQuery-ism). Previously it had $(..) with several .add() calls. Now doing one call. * Uses mw.util.tooltipAccessKeyRegexp instead of local regex * Uses jQuery.fn.text instead of jQuery.fn.html for link text message * Should fix bug 27146 (previously a failed attempt in r82498) * Previousy worked on in r88527, r88511, r78150, r78147 * minor whitespace/comment fix in mediawiki.util.js/mediawiki.page.startup.js --- .../mediawiki.action.watch.ajax.js | 266 ++++++++---------- .../mediawiki.page/mediawiki.page.startup.js | 5 +- resources/mediawiki/mediawiki.util.js | 8 +- 3 files changed, 127 insertions(+), 152 deletions(-) diff --git a/resources/mediawiki.action/mediawiki.action.watch.ajax.js b/resources/mediawiki.action/mediawiki.action.watch.ajax.js index 3986d80b2c..2611530250 100644 --- a/resources/mediawiki.action/mediawiki.action.watch.ajax.js +++ b/resources/mediawiki.action/mediawiki.action.watch.ajax.js @@ -1,174 +1,150 @@ /** * Animate watch/unwatch links to use asynchronous API requests to - * watch pages, rather than clicking on links. Requires jQuery. + * watch pages, rather than navigating to a different URI. */ -( function( $ ) { -var $links; - -var setLinkText = function( $link, action ) { - if ( action == 'watch' || action == 'unwatch' ) { - // save the accesskey from the title - var keyCommand = $link.attr( 'title' ).match( /\[.*?\]$/ ) ? $link.attr( 'title' ).match( /\[.*?\]$/ )[0] : ''; - $link.attr( 'title', mw.msg( 'tooltip-ca-' + action ) + ' ' + keyCommand ); - } - if ( $link.data( 'icon' ) ) { - $link.attr( 'alt', mw.msg( action ) ); - if ( action == 'watching' || action == 'unwatching' ) { +( function ( $, mw, undefined ) { + +/** + * Update the link text, link href attribute and (if applicable) + * "loading" class. + * + * @param $link {jQuery} Anchor tag of (un)watch link + * @param action {String} One of 'watch', 'unwatch'. + * @param state {String} [optional] 'idle' or 'loading'. Default is 'idle'. + */ +function updateWatchLink( $link, action, state ) { + // message keys 'watch', 'watching', 'unwatch' or 'unwatching'. + var msgKey = state === 'loading' ? action + 'ing' : action, + accesskeyTip = $link.attr( 'title' ).match( mw.util.tooltipAccessKeyRegexp ), + $li = $link.closest( 'li' ); + + $link + .text( mw.msg( msgKey ) ) + .attr( 'title', mw.msg( 'tooltip-ca-' + action ) + + ( accesskeyTip ? ' ' + accesskeyTip[0] : '' ) + ) + .attr( 'href', mw.util.wikiScript() + '?' + $.param({ + title: mw.config.get( 'wgPageName' ), + action: action + }) + ); + + // Special case for vector icon + if ( $li.hasClass( 'icon' ) ) { + if ( state === 'loading' ) { $link.addClass( 'loading' ); } else { $link.removeClass( 'loading' ); } - } else { - $link.html( mw.msg( action ) ); } -}; - -var errorHandler = function( $link ) { - - // Reset link text to whatever it was before we switching it to the '(un)watch'+ing message. - setLinkText( $link, $link.data( 'action' ) ); - - // Format error message - var cleanTitle = mw.config.get( 'wgPageName' ).replace( /_/g, ' ' ); - var link = mw.html.element( - 'a', { - 'href': mw.util.wikiGetlink( mw.config.get( 'wgPageName' ) ), - 'title': cleanTitle - }, cleanTitle - ); - var msg = mw.msg( 'watcherrortext', link ); - - // Report to user about the error - mw.util.jsMessage( msg, 'watch' ); -}; +} /** - * Process the result of the API watch action. - * - * @param response Data object from API request. - * @param $link jQuery object of the watch link. - * @return Boolean true on success, false otherwise. + * @todo This should be moved somewhere more accessible. + * @param url {String} + * @return {String} The extracted action, defaults to 'view'. */ -var processResult = function( response, $link ) { - - if ( ( 'error' in response ) || !response.watch ) { - errorHandler( $link ); - return false; +function mwUriGetAction( url ) { + var actionPaths = mw.config.get( 'wgActionPaths' ), + key, parts, m, action; + + // @todo: Does MediaWiki give action path or query param + // precedence ? If the former, move this to the bottom + action = mw.util.getParamValue( 'action', url ); + if ( action !== null ) { + return action; } - var watchResponse = response.watch; - - // To ensure we set the same status for all watch links with the - // same target we trigger a custom event on *all* watch links. - if ( watchResponse.watched !== undefined ) { - $links.trigger( 'mw-ajaxwatch', [watchResponse.title, 'watch', $link] ); - } else if ( watchResponse.unwatched !== undefined ) { - $links.trigger( 'mw-ajaxwatch', [watchResponse.title, 'unwatch', $link] ); - } else { - // Either we got an error code or it just plain broke. - window.location.href = $link[0].href; - return false; + for ( key in actionPaths ) { + if ( actionPaths.hasOwnProperty( key ) ) { + parts = actionPaths[key].split( '$1' ); + for ( i = 0; i < parts.length; i += 1 ) { + parts[i] = $.escapeRE( parts[i] ); + } + m = new RegExp( parts.join( '(.+)' ) ).exec( url ); + if ( m && m[1] ) { + return key; + } + + } } - mw.util.jsMessage( watchResponse.message, 'watch' ); - - // Bug 12395 - update the watch checkbox on edit pages when the - // page is watched or unwatched via the tab. - if ( watchResponse.watched !== undefined ) { - $( '#wpWatchthis' ).prop( 'checked', 'checked' ); - } else { - $( '#wpWatchthis' ).removeProp( 'checked' ); - } - return true; -}; + return 'view'; +} $( document ).ready( function() { - $links = $( '.mw-watchlink a, a.mw-watchlink' ); - // BC with older skins - $links = $links - .add( '#ca-watch a, #ca-unwatch a, a#mw-unwatch-link1, ' + - 'a#mw-unwatch-link2, a#mw-watch-link2, a#mw-watch-link1' ); - // allowing people to add inline animated links is a little scary - $links = $links.filter( ':not( #bodyContent *, #content * )' ); + var $links = $( '.mw-watchlink a, a.mw-watchlink' + + '#ca-watch a, #ca-unwatch a, #mw-unwatch-link1, ' + + '#mw-unwatch-link2, #mw-watch-link2, #mw-watch-link1' ); - $links.each( function() { - var $link = $( this ); - var link = this; - $link - .data( 'icon', $link.closest( 'li' ).hasClass( 'icon' ) ) - .data( 'action', mw.util.getParamValue( 'action', link.href ) == 'unwatch' ? 'unwatch' : 'watch' ); - var title = mw.util.getParamValue( 'title', link.href ); - $link.data( 'target', title.replace( /_/g, ' ' ) ); - }); + // Allowing people to add inline animated links is a little scary + $links = $links.filter( ':not( #bodyContent *, #content * )' ); - $links.click( function( event ) { - var $link = $( this ); + $links.click( function( e ) { + var $link, api, + action = mwUriGetAction( this.href ); - if ( !mw.config.get( 'wgEnableWriteAPI' ) ) { - // Lazy initialization so we don't toss up - // ActiveX warnings on initial page load - // for IE 6 users with security settings. - $links.unbind( 'click' ); + if ( action !== 'watch' && action !== 'unwatch' ) { + // Could not extract target action from link url, + // let native browsing handle it further return true; } - - setLinkText( $link, $link.data( 'action' ) + 'ing' ); - - var reqData = { - 'action': 'watch', - 'format': 'json', - 'title': $link.data( 'target' ), - 'token': mw.user.tokens.get( 'watchToken' ), - // API return contains a localized data.watch.message string. - 'uselang': mw.config.get( 'wgUserLanguage' ) - }; - - if ( $link.data( 'action' ) == 'unwatch' ) { - reqData.unwatch = ''; - } - - $.ajax({ - url: mw.util.wikiScript( 'api' ), - dataType: 'json', - type: 'POST', - data: reqData, - success: function( data, textStatus, xhr ) { - processResult( data, $link ); + e.preventDefault(); + e.stopPropagation(); + + $link = $( this ); + + updateWatchLink( $link, action, 'loading' ); + + api = new mw.Api(); + api[action]( + mw.config.get( 'wgPageName' ), + // Success + function( watchResponse ) { + var otherAction = action === 'watch' ? 'unwatch' : 'watch', + $li = $link.closest( 'li' ); + + mw.util.jsMessage( watchResponse.message, 'ajaxwatch' ); + + // Set link to opposite + updateWatchLink( $link, otherAction ); + + // Most common ID style + if ( $li.prop( 'id' ) === 'ca-' + otherAction || $li.prop( 'id' ) === 'ca-' + action ) { + $li.prop( 'id', 'ca-' + otherAction ); + } + + // Bug 12395 - update the watch checkbox on edit pages when the + // page is watched or unwatched via the tab. + if ( watchResponse.watched !== undefined ) { + $( '#wpWatchthis' ).prop( 'checked', true ); + } else { + $( '#wpWatchthis' ).removeProp( 'checked' ); + } }, - error: function(){ - processResult( {}, $link ); - } - }); + // Error + function(){ + + // Reset link to non-loading mode + updateWatchLink( $link, action ); + + // Format error message + var cleanTitle = mw.config.get( 'wgPageName' ).replace( /_/g, ' ' ); + var link = mw.html.element( + 'a', { + 'href': mw.util.wikiGetlink( mw.config.get( 'wgPageName' ) ), + 'title': cleanTitle + }, cleanTitle + ); + var html = mw.msg( 'watcherrortext', link ); + + // Report to user about the error + mw.util.jsMessage( html, 'ajaxwatch' ); - return false; - }); - - // When a request returns, a custom event 'mw-ajaxwatch' is triggered - // on *all* watch links, so they can be updated if necessary - $links.bind( 'mw-ajaxwatch', function( event, target, action, $link ) { - var foo = $link.data( 'target' ); - if ( $link.data( 'target' ) == target ) { - var otheraction = action == 'watch' - ? 'unwatch' - : 'watch'; - - $link.data( 'action', otheraction ); - setLinkText( $link, otheraction ); - $link.attr( 'href', - mw.config.get( 'wgScript' ) - + '?title=' + mw.util.wikiUrlencode( mw.config.get( 'wgPageName' ) ) - + '&action=' + otheraction - ); - if ( $link.closest( 'li' ).attr( 'id' ) == 'ca-' + action ) { - $link.closest( 'li' ).attr( 'id', 'ca-' + otheraction ); - // update the link text with the new message - $link.text( mw.msg( otheraction ) ); } - } - - return false; + ); }); }); -})( jQuery ); +})( jQuery, mediaWiki ); diff --git a/resources/mediawiki.page/mediawiki.page.startup.js b/resources/mediawiki.page/mediawiki.page.startup.js index 6217070a78..d7b9a559f0 100644 --- a/resources/mediawiki.page/mediawiki.page.startup.js +++ b/resources/mediawiki.page/mediawiki.page.startup.js @@ -2,9 +2,8 @@ mw.page = {}; - /* Client profile classes for */ - /* Allows for easy hiding/showing of JS or no-JS-specific UI elements */ - + // Client profile classes for + // Allows for easy hiding/showing of JS or no-JS-specific UI elements $( 'html' ) .addClass('client-js' ) .removeClass( 'client-nojs' ); diff --git a/resources/mediawiki/mediawiki.util.js b/resources/mediawiki/mediawiki.util.js index 8e8e88a612..26e3331d26 100644 --- a/resources/mediawiki/mediawiki.util.js +++ b/resources/mediawiki/mediawiki.util.js @@ -413,10 +413,10 @@ * something, replacing any previous message. * Calling with no arguments, with an empty string or null will hide the message * - * @param message mixed The DOM-element or HTML-string to be put inside the message box. - * @param className string Used in adding a class; should be different for each call + * @param message {mixed} The DOM-element, jQuery object or HTML-string to be put inside the message box. + * @param className {String} Used in adding a class; should be different for each call * to allow CSS/JS to hide different boxes. null = no class used. - * @return boolean True on success, false on failure. + * @return {Boolean} True on success, false on failure. */ jsMessage: function( message, className ) { if ( !arguments.length || message === '' || message === null ) { @@ -443,7 +443,7 @@ if ( typeof message === 'object' ) { $messageDiv.empty(); - $messageDiv.append( message ); // Append new content + $messageDiv.append( message ); } else { $messageDiv.html( message ); } -- 2.20.1