Rewrite ajaxwatch.js to use the API watch action, and JQuery. Allows it to be used...
authorHappy-melon <happy-melon@users.mediawiki.org>
Tue, 27 Apr 2010 15:09:04 +0000 (15:09 +0000)
committerHappy-melon <happy-melon@users.mediawiki.org>
Tue, 27 Apr 2010 15:09:04 +0000 (15:09 +0000)
RELEASE-NOTES
includes/AjaxFunctions.php
includes/OutputPage.php
includes/Setup.php
includes/api/ApiWatch.php
skins/common/ajaxwatch.js

index 2d8dfbb..519b3ea 100644 (file)
@@ -58,6 +58,8 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
 * (bug 22858) $wgLocalStylePath is by default set to the same value as
   $wgStylePath but should never point to a different domain than the site is
   on, allowing skins to use .htc files which are not cross-domain friendly.
+* ajaxwatch now uses the API and JQuery, and can be used to animate arbitrary 
+  watch links, not just to watch the page the link is on.
 
 === Bug fixes in 1.17 ===
 * (bug 17560) Half-broken deletion moved image files to deletion archive
index e3180e0..b564f0a 100644 (file)
@@ -73,60 +73,6 @@ function code2utf( $num ) {
        return '';
 }
 
-/**
- * Called for AJAX watch/unwatch requests.
- * @param $pagename Prefixed title string for page to watch/unwatch
- * @param $watch String 'w' to watch, 'u' to unwatch
- * @return String '<w#>' or '<u#>' on successful watch or unwatch,
- *   respectively, followed by an HTML message to display in the alert box; or
- *   '<err#>' on error
- */
-function wfAjaxWatch( $pagename = "", $watch = "" ) {
-       if ( wfReadOnly() ) {
-               // redirect to action=(un)watch, which will display the database lock
-               // message
-               return '<err#>';
-       }
-
-       if ( 'w' !== $watch && 'u' !== $watch ) {
-               return '<err#>';
-       }
-       $watch = 'w' === $watch;
-
-       $title = Title::newFromDBkey( $pagename );
-       if ( !$title ) {
-               // Invalid title
-               return '<err#>';
-       }
-       $article = new Article( $title );
-       $watching = $title->userIsWatching();
-
-       if ( $watch ) {
-               if ( !$watching ) {
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->begin();
-                       $ok = $article->doWatch();
-                       $dbw->commit();
-               }
-       } else {
-               if ( $watching ) {
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->begin();
-                       $ok = $article->doUnwatch();
-                       $dbw->commit();
-               }
-       }
-       // Something stopped the change
-       if ( isset( $ok ) && !$ok ) {
-               return '<err#>';
-       }
-       if ( $watch ) {
-               return '<w#>' . wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() );
-       } else {
-               return '<u#>' . wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() );
-       }
-}
-
 /**
  * Called in some places (currently just extensions)
  * to get the thumbnail URL for a given file at a given resolution.
index 802740e..5bfde22 100644 (file)
@@ -1518,6 +1518,7 @@ class OutputPage {
                        wfRunHooks( 'AjaxAddScript', array( &$this ) );
 
                        if( $wgAjaxWatch && $wgUser->isLoggedIn() ) {
+                               $this->includeJQuery();
                                $this->addScriptFile( 'ajaxwatch.js' );
                        }
 
index b8ce9be..1c7ec74 100644 (file)
@@ -345,7 +345,6 @@ wfProfileIn( $fname.'-misc2' );
 $wgDeferredUpdateList = array();
 $wgPostCommitUpdateList = array();
 
-if ( $wgAjaxWatch ) $wgAjaxExportList[] = 'wfAjaxWatch';
 if ( $wgAjaxUploadDestCheck ) $wgAjaxExportList[] = 'SpecialUpload::ajaxGetExistsWarning';
 
 # Placeholders in case of DB error
index cf045c8..60c95d0 100644 (file)
@@ -57,9 +57,11 @@ class ApiWatch extends ApiBase {
 
                if ( $params['unwatch'] ) {
                        $res['unwatched'] = '';
+                       $res['message'] = wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() );
                        $success = $article->doUnwatch();
                } else {
                        $res['watched'] = '';
+                       $res['message'] = wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() );
                        $success = $article->doWatch();
                }
                if ( !$success ) {
index 7f54601..d2d24fa 100644 (file)
@@ -1,13 +1,10 @@
-// dependencies:
-// * ajax.js:
-  /*extern sajax_init_object, sajax_do_call */
-// * wikibits.js:
 /*extern changeText, hookEvent, jsMsg */
+/**
+ * Animate watch/unwatch links to use asynchronous API requests to
+ * watch pages, rather than clicking on links.  Requires JQuery.  
+ * Uses jsMsg() from wikibits.js.
+ */
 
-// These should have been initialized in the generated js
-/*extern wgAjaxWatch, wgPageName */
-
-if(typeof wgAjaxWatch === "undefined" || !wgAjaxWatch) {
+if( typeof wgAjaxWatch === "undefined" || !wgAjaxWatch ) {
        var wgAjaxWatch = {
                watchMsg: "Watch",
                unwatchMsg: "Unwatch",
@@ -18,163 +15,114 @@ if(typeof wgAjaxWatch === "undefined" || !wgAjaxWatch) {
        };
 }
 
-wgAjaxWatch.supported = true; // supported on current page and by browser
-wgAjaxWatch.watching = false; // currently watching page
-wgAjaxWatch.inprogress = false; // ajax request in progress
-wgAjaxWatch.timeoutID = null; // see wgAjaxWatch.ajaxCall
-wgAjaxWatch.watchLinks = []; // "watch"/"unwatch" links
-wgAjaxWatch.iconMode = false; // new icon driven functionality 
-wgAjaxWatch.imgBasePath = ""; // base img path derived from icons on load
-
-wgAjaxWatch.setLinkText = function( newText ) {
-       if( wgAjaxWatch.iconMode ) {
-               for ( i = 0; i < wgAjaxWatch.watchLinks.length; i++ ) {
-                       wgAjaxWatch.watchLinks[i].firstChild.alt = newText;
-                       if ( newText == wgAjaxWatch.watchingMsg || newText == wgAjaxWatch.unwatchingMsg ) {
-                               wgAjaxWatch.watchLinks[i].className += ' loading';
-                       } else if ( newText == wgAjaxWatch.watchMsg || newText == wgAjaxWatch.unwatchMsg ) {
-                               wgAjaxWatch.watchLinks[i].className = 
-                                       wgAjaxWatch.watchLinks[i].className.replace( /loading/i, '' );
-                               // update the title text on the link
-                               var keyCommand = wgAjaxWatch.watchLinks[i].title.match( /\[.*?\]$/ ) ? 
-                                       wgAjaxWatch.watchLinks[i].title.match( /\[.*?\]$/ )[0] : "";
-                               wgAjaxWatch.watchLinks[i].title = ( newText == wgAjaxWatch.watchMsg ? 
-                                       wgAjaxWatch['tooltip-ca-watchMsg'] : wgAjaxWatch['tooltip-ca-unwatchMsg'] )
-                                       + " " + keyCommand;
-                       }
-               }
-       } else {
-               for ( i = 0; i < wgAjaxWatch.watchLinks.length; i++ ) {
-                       changeText( wgAjaxWatch.watchLinks[i], newText );
-               }
+wgAjaxWatch.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', wgAjaxWatch['tooltip-ca-'+action+'Msg']+' '+keyCommand );
        }
-};
-
-wgAjaxWatch.setLinkID = function( newId ) {
-       // We can only set the first one
-       wgAjaxWatch.watchLinks[0].parentNode.setAttribute( 'id', newId );
-};
-
-wgAjaxWatch.setHref = function( string ) {
-       for( i = 0; i < wgAjaxWatch.watchLinks.length; i++ ) {
-               if( string == 'watch' ) {
-                       wgAjaxWatch.watchLinks[i].href = wgAjaxWatch.watchLinks[i].href
-                               .replace( /&action=unwatch/, '&action=watch' );
-               } else if( string == 'unwatch' ) {
-                       wgAjaxWatch.watchLinks[i].href = wgAjaxWatch.watchLinks[i].href
-                               .replace( /&action=watch/, '&action=unwatch' );
+       if( $link.data('icon') ) {
+               $link.attr( 'alt', wgAjaxWatch[action+'Msg'] );
+               if ( action == 'watching' || action == 'unwatching' ) {
+                       $link.addClass( 'loading' );
+               } else {
+                       $link.removeClass( 'loading' );
                }
+       } else {
+               $link.html( wgAjaxWatch[action+'Msg'] );
        }
-}
-
-wgAjaxWatch.ajaxCall = function() {
-       if(!wgAjaxWatch.supported) {
-               return true;
-       } else if (wgAjaxWatch.inprogress) {
-               return false;
-       }
-       if(!wfSupportsAjax()) {
-               // Lazy initialization so we don't toss up
-               // ActiveX warnings on initial page load
-               // for IE 6 users with security settings.
-               wgAjaxWatch.supported = false;
-               return true;
-       }
-
-       wgAjaxWatch.inprogress = true;
-       wgAjaxWatch.setLinkText( wgAjaxWatch.watching
-               ? wgAjaxWatch.unwatchingMsg : wgAjaxWatch.watchingMsg);
-       sajax_do_call(
-               "wfAjaxWatch",
-               [wgPageName, (wgAjaxWatch.watching ? "u" : "w")], 
-               wgAjaxWatch.processResult
-       );
-       // if the request isn't done in 10 seconds, allow user to try again
-       wgAjaxWatch.timeoutID = window.setTimeout(
-               function() { wgAjaxWatch.inprogress = false; },
-               10000
-       );
-       return false;
 };
 
-wgAjaxWatch.processResult = function(request) {
-       if(!wgAjaxWatch.supported) {
-               return;
-       }
-       var response = request.responseText;
-       if( response.match(/^<w#>/) ) {
-               wgAjaxWatch.watching = true;
-               wgAjaxWatch.setLinkText(wgAjaxWatch.unwatchMsg);
-               wgAjaxWatch.setLinkID("ca-unwatch");
-               wgAjaxWatch.setHref( 'unwatch' );
-       } else if( response.match(/^<u#>/) ) {
-               wgAjaxWatch.watching = false;
-               wgAjaxWatch.setLinkText(wgAjaxWatch.watchMsg);
-               wgAjaxWatch.setLinkID("ca-watch");
-               wgAjaxWatch.setHref( 'watch' );
+wgAjaxWatch.processResult = function( response ) {
+       response = response.watch;
+       var $link = $j(this); 
+       // 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( response.watched !== undefined ){
+               wgAjaxWatch.$links.trigger( 'mw-ajaxwatch', [response.title, 'watch'] );
+       } else if ( response.unwatched !== undefined ){
+               wgAjaxWatch.$links.trigger( 'mw-ajaxwatch', [response.title, 'unwatch'] );
        } else {
-               // Either we got a <err#> error code or it just plain broke.
-               window.location.href = wgAjaxWatch.watchLinks[0].href;
+               // Either we got an error code or it just plain broke.
+               window.location.href = $link.attr('href');
                return;
        }
-       jsMsg( response.substr(4), 'watch' );
-       wgAjaxWatch.inprogress = false;
-       if(wgAjaxWatch.timeoutID) {
-               window.clearTimeout(wgAjaxWatch.timeoutID);
-       }
-       // Bug 12395 - avoid some watch link confusion on edit
-       var watchthis = document.getElementById("wpWatchthis");
-       if( watchthis && response.match(/^<[uw]#>/) ) {
-               watchthis.checked = response.match(/^<w#>/) ? "checked" : "";
+       
+       jsMsg( response.message, 'watch' );
+       
+       // Bug 12395 - update the watch checkbox on edit pages when the 
+       // page is watched or unwatched via the tab.
+       if( response.watched !== undefined ){
+               $j("#wpWatchthis").attr( 'checked', '1' );
+       } else {
+               $j("#wpWatchthis").removeAttr( 'checked' );
        }
-       return;
 };
 
-wgAjaxWatch.onLoad = function() {
-       // This document structure hardcoding sucks.  We should make a class and
-       // toss all this out the window.
+$j(document).ready( function(){
+       var $links = $j( '.mw-watchlink a, a.mw-watchlink' );
+       //BC with older skins
+       $links = $links
+               .add( $j( '#ca-watch a, #ca-unwatch a, a#mw-unwatch-link1' ) )
+               .add( $j( '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 el1 = document.getElementById("ca-unwatch");
-       var el2 = null;
-       if ( !el1 ) {
-               el1 = document.getElementById("mw-unwatch-link1");
-               el2 = document.getElementById("mw-unwatch-link2");
-       }
-       if( el1 ) {
-               wgAjaxWatch.watching = true;
-       } else {
-               wgAjaxWatch.watching = false;
-               el1 = document.getElementById("ca-watch");
-               if ( !el1 ) {
-                       el1 = document.getElementById("mw-watch-link1");
-                       el2 = document.getElementById("mw-watch-link2");
-               }
-               if( !el1 ) {
-                       wgAjaxWatch.supported = false;
-                       return;
-               }
-       }
+       $links.each( function(){
+               var $link = $j(this);
+               $link
+                       .data( 'icon', $link.parent().hasClass( 'icon' ) )
+                       .data( 'action', $link.attr( 'href' ).match( /[\?\&]action=unwatch/i ) ? 'unwatch' : 'watch' );
+               var title = $link.attr( 'href' ).match( /[\?\&]title=(.*?)&/i )[1];
+               $link.data( 'target', decodeURIComponent( title ).replace( /_/g, ' ' ) );
        
-       // Detect if the watch/unwatch feature is in icon mode
-       if ( el1.className.match( /icon/i ) ) {
-               wgAjaxWatch.iconMode = true;
-       }
+       });
        
-       // The id can be either for the parent (Monobook-based) or the element
-       // itself (non-Monobook)
-       wgAjaxWatch.watchLinks.push( el1.tagName.toLowerCase() == "a"
-               ? el1 : el1.firstChild );
+       $links.click( function(event){
+               var $link = $j(this);
 
-       if( el2 ) {
-               wgAjaxWatch.watchLinks.push( el2 );
-       }
-
-       // I couldn't get for (watchLink in wgAjaxWatch.watchLinks) to work, if
-       // you can be my guest.
-       for( i = 0; i < wgAjaxWatch.watchLinks.length; i++ ) {
-               wgAjaxWatch.watchLinks[i].onclick = wgAjaxWatch.ajaxCall;
-       }
-       return;
-};
+               if( wgAjaxWatch.supported === false || !wfSupportsAjax() ) {
+                       // Lazy initialization so we don't toss up
+                       // ActiveX warnings on initial page load
+                       // for IE 6 users with security settings.
+                       wgAjaxWatch.$links.unbind( 'click' );
+                       return true;
+               }
+               
+               wgAjaxWatch.setLinkText( $link, $link.data('action')+'ing' );
+               $j.get( wgScriptPath 
+                               + '/api.php?action=watch&format=json&title='
+                               + $link.data('target')
+                               + ( $link.data('action')=='unwatch' ? '&unwatch' : '' ),
+                       {},
+                       wgAjaxWatch.processResult,
+                       'json'
+               );
 
-hookEvent("load", wgAjaxWatch.onLoad);
+               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 ){
+               var $link = $j(this);
+               var foo = $link.data( 'target' );
+               if( $link.data( 'target' ) == target ){
+                       var otheraction = action == 'watch'
+                               ? 'unwatch'
+                               : 'watch';
+                       
+                       $link.data( 'action', otheraction );
+                       wgAjaxWatch.setLinkText( $link, otheraction );
+                       $link.attr( 'href', $link.attr('href').replace( '/&action='+action+'/', '&action='+otheraction ) );
+                       if( $link.parent().attr('id') == 'ca-'+action ){
+                               $link.parent().attr( 'id', 'ca-'+otheraction );
+                       }
+               };
+               return false;
+       });
+       
+       wgAjaxWatch.$links = $links;
+});