[mediawiki.action.watch.ajax.js] Rewrite using mw.Api and fixes bug 27146
authorKrinkle <krinkle@users.mediawiki.org>
Tue, 27 Dec 2011 01:21:56 +0000 (01:21 +0000)
committerKrinkle <krinkle@users.mediawiki.org>
Tue, 27 Dec 2011 01:21:56 +0000 (01:21 +0000)
* 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

resources/mediawiki.action/mediawiki.action.watch.ajax.js
resources/mediawiki.page/mediawiki.page.startup.js
resources/mediawiki/mediawiki.util.js

index 3986d80..2611530 100644 (file)
 /**
  * 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 );
index 6217070..d7b9a55 100644 (file)
@@ -2,9 +2,8 @@
 
        mw.page = {};
 
-       /* Client profile classes for <html> */
-       /* Allows for easy hiding/showing of JS or no-JS-specific UI elements */
-
+       // Client profile classes for <html>
+       // Allows for easy hiding/showing of JS or no-JS-specific UI elements
        $( 'html' )
                .addClass('client-js' )
                .removeClass( 'client-nojs' );
index 8e8e88a..26e3331 100644 (file)
                 * 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 ) {
 
                                if ( typeof message === 'object' ) {
                                        $messageDiv.empty();
-                                       $messageDiv.append( message ); // Append new content
+                                       $messageDiv.append( message );
                                } else {
                                        $messageDiv.html( message );
                                }