From 05879eeb55b2412918b0b29509807a1d456336fe Mon Sep 17 00:00:00 2001 From: Happy-melon Date: Tue, 27 Apr 2010 15:09:04 +0000 Subject: [PATCH] Rewrite ajaxwatch.js to use the API watch action, and JQuery. Allows it to be used to asynchronise any watch links, not just the watch tab/links/spinny-starry-thing. Will make it nice and easy to add unwatch links to Special:Watchlist, etc. --- RELEASE-NOTES | 2 + includes/AjaxFunctions.php | 54 -------- includes/OutputPage.php | 1 + includes/Setup.php | 1 - includes/api/ApiWatch.php | 2 + skins/common/ajaxwatch.js | 254 +++++++++++++++---------------------- 6 files changed, 106 insertions(+), 208 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 2d8dfbb774..519b3eaae9 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -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 diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php index e3180e0ac4..b564f0a88e 100644 --- a/includes/AjaxFunctions.php +++ b/includes/AjaxFunctions.php @@ -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 '' or '' on successful watch or unwatch, - * respectively, followed by an HTML message to display in the alert box; or - * '' on error - */ -function wfAjaxWatch( $pagename = "", $watch = "" ) { - if ( wfReadOnly() ) { - // redirect to action=(un)watch, which will display the database lock - // message - return ''; - } - - if ( 'w' !== $watch && 'u' !== $watch ) { - return ''; - } - $watch = 'w' === $watch; - - $title = Title::newFromDBkey( $pagename ); - if ( !$title ) { - // Invalid title - return ''; - } - $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 ''; - } - if ( $watch ) { - return '' . wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() ); - } else { - return '' . 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. diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 802740e635..5bfde22353 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -1518,6 +1518,7 @@ class OutputPage { wfRunHooks( 'AjaxAddScript', array( &$this ) ); if( $wgAjaxWatch && $wgUser->isLoggedIn() ) { + $this->includeJQuery(); $this->addScriptFile( 'ajaxwatch.js' ); } diff --git a/includes/Setup.php b/includes/Setup.php index b8ce9be4de..1c7ec745f0 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -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 diff --git a/includes/api/ApiWatch.php b/includes/api/ApiWatch.php index cf045c876d..60c95d0a6a 100644 --- a/includes/api/ApiWatch.php +++ b/includes/api/ApiWatch.php @@ -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 ) { diff --git a/skins/common/ajaxwatch.js b/skins/common/ajaxwatch.js index 7f546014fe..d2d24fafd3 100644 --- a/skins/common/ajaxwatch.js +++ b/skins/common/ajaxwatch.js @@ -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(/^/) ) { - wgAjaxWatch.watching = true; - wgAjaxWatch.setLinkText(wgAjaxWatch.unwatchMsg); - wgAjaxWatch.setLinkID("ca-unwatch"); - wgAjaxWatch.setHref( 'unwatch' ); - } else if( response.match(/^/) ) { - 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 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(/^/) ? "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; +}); -- 2.20.1