| Index: trunk/phase3/skins/common/ajaxwatch.js |
| — | — | @@ -0,0 +1,127 @@ |
| | 2 | +// dependencies: |
| | 3 | +// * ajax.js: |
| | 4 | + /*extern sajax_init_object, sajax_do_call */ |
| | 5 | +// * wikibits.js: |
| | 6 | + /*extern changeText, akeytt, hookEvent */ |
| | 7 | + |
| | 8 | +// These should have been initialized in the generated js |
| | 9 | +/*extern wgAjaxWatch, wgArticleId */ |
| | 10 | + |
| | 11 | +if(typeof wgAjaxWatch === "undefined" || !wgAjaxWatch) { |
| | 12 | + var wgAjaxWatch = { |
| | 13 | + watchMsg: "Watch", |
| | 14 | + unwatchMsg: "Unwatch", |
| | 15 | + watchingMsg: "Watching...", |
| | 16 | + unwatchingMsg: "Unwatching..." |
| | 17 | + }; |
| | 18 | +} |
| | 19 | + |
| | 20 | +wgAjaxWatch.supported = true; // supported on current page and by browser |
| | 21 | +wgAjaxWatch.watching = false; // currently watching page |
| | 22 | +wgAjaxWatch.inprogress = false; // ajax request in progress |
| | 23 | +wgAjaxWatch.timeoutID = null; // see wgAjaxWatch.ajaxCall |
| | 24 | +wgAjaxWatch.watchLink1 = null; // "watch"/"unwatch" link |
| | 25 | +wgAjaxWatch.watchLink2 = null; // second one, for (some?) non-Monobook-based |
| | 26 | +wgAjaxWatch.oldHref = null; // url for action=watch/action=unwatch |
| | 27 | + |
| | 28 | +wgAjaxWatch.setLinkText = function(newText) { |
| | 29 | + changeText(wgAjaxWatch.watchLink1, newText); |
| | 30 | + if (wgAjaxWatch.watchLink2) { |
| | 31 | + changeText(wgAjaxWatch.watchLink2, newText); |
| | 32 | + } |
| | 33 | +}; |
| | 34 | + |
| | 35 | +wgAjaxWatch.setLinkID = function(newId) { |
| | 36 | + wgAjaxWatch.watchLink1.id = newId; |
| | 37 | + akeytt(newId); // update tooltips for Monobook |
| | 38 | +}; |
| | 39 | + |
| | 40 | +wgAjaxWatch.ajaxCall = function() { |
| | 41 | + if(!wgAjaxWatch.supported || wgAjaxWatch.inprogress) { |
| | 42 | + return; |
| | 43 | + } |
| | 44 | + wgAjaxWatch.inprogress = true; |
| | 45 | + wgAjaxWatch.setLinkText(wgAjaxWatch.watching ? wgAjaxWatch.unwatchingMsg : wgAjaxWatch.watchingMsg); |
| | 46 | + sajax_do_call("wfAjaxWatch", [wgArticleId, (wgAjaxWatch.watching ? "u" : "w")], wgAjaxWatch.processResult); |
| | 47 | + // if the request isn't done in 10 seconds, allow user to try again |
| | 48 | + wgAjaxWatch.timeoutID = window.setTimeout(function() { wgAjaxWatch.inprogress = false; }, 10000); |
| | 49 | + return; |
| | 50 | +}; |
| | 51 | + |
| | 52 | +wgAjaxWatch.processResult = function(request) { |
| | 53 | + if(!wgAjaxWatch.supported) { |
| | 54 | + return; |
| | 55 | + } |
| | 56 | + var response = request.responseText; |
| | 57 | + if(response == "<err#>") { |
| | 58 | + window.location.href = wgAjaxWatch.oldHref; |
| | 59 | + return; |
| | 60 | + } else if(response == "<w#>") { |
| | 61 | + wgAjaxWatch.watching = true; |
| | 62 | + wgAjaxWatch.setLinkText(wgAjaxWatch.unwatchMsg); |
| | 63 | + wgAjaxWatch.setLinkID("ca-unwatch"); |
| | 64 | + wgAjaxWatch.oldHref = wgAjaxWatch.oldHref.replace(/action=watch/, "action=unwatch"); |
| | 65 | + } else if(response == "<u#>") { |
| | 66 | + wgAjaxWatch.watching = false; |
| | 67 | + wgAjaxWatch.setLinkText(wgAjaxWatch.watchMsg); |
| | 68 | + wgAjaxWatch.setLinkID("ca-watch"); |
| | 69 | + wgAjaxWatch.oldHref = wgAjaxWatch.oldHref.replace(/action=unwatch/, "action=watch"); |
| | 70 | + } |
| | 71 | + wgAjaxWatch.inprogress = false; |
| | 72 | + if(wgAjaxWatch.timeoutID) { |
| | 73 | + window.clearTimeout(wgAjaxWatch.timeoutID); |
| | 74 | + } |
| | 75 | + return; |
| | 76 | +}; |
| | 77 | + |
| | 78 | +wgAjaxWatch.onLoad = function() { |
| | 79 | + var el1 = document.getElementById("ca-unwatch"); |
| | 80 | + var el2 = null; |
| | 81 | + if (!el1) { |
| | 82 | + el1 = document.getElementById("mw-unwatch-link1"); |
| | 83 | + el2 = document.getElementById("mw-unwatch-link2"); |
| | 84 | + } |
| | 85 | + if(el1) { |
| | 86 | + wgAjaxWatch.watching = true; |
| | 87 | + } else { |
| | 88 | + wgAjaxWatch.watching = false; |
| | 89 | + el1 = document.getElementById("ca-watch"); |
| | 90 | + if (!el1) { |
| | 91 | + el1 = document.getElementById("mw-watch-link1"); |
| | 92 | + el2 = document.getElementById("mw-watch-link2"); |
| | 93 | + } |
| | 94 | + if(!el1) { |
| | 95 | + wgAjaxWatch.supported = false; |
| | 96 | + return; |
| | 97 | + } |
| | 98 | + } |
| | 99 | + |
| | 100 | + if(!wfSupportsAjax()) { |
| | 101 | + wgAjaxWatch.supported = false; |
| | 102 | + return; |
| | 103 | + } |
| | 104 | + |
| | 105 | + // The id can be either for the parent (Monobook-based) or the element |
| | 106 | + // itself (non-Monobook) |
| | 107 | + wgAjaxWatch.watchLink1 = el1.tagName.toLowerCase() == "a" ? el1 : el1.firstChild; |
| | 108 | + wgAjaxWatch.watchLink2 = el2 ? el2 : null; |
| | 109 | + |
| | 110 | + wgAjaxWatch.oldHref = wgAjaxWatch.watchLink1.getAttribute("href"); |
| | 111 | + wgAjaxWatch.watchLink1.setAttribute("href", "javascript:wgAjaxWatch.ajaxCall()"); |
| | 112 | + if (wgAjaxWatch.watchLink2) { |
| | 113 | + wgAjaxWatch.watchLink2.setAttribute("href", "javascript:wgAjaxWatch.ajaxCall()"); |
| | 114 | + } |
| | 115 | + return; |
| | 116 | +}; |
| | 117 | + |
| | 118 | +hookEvent("load", wgAjaxWatch.onLoad); |
| | 119 | + |
| | 120 | +/** |
| | 121 | + * @return boolean whether the browser supports XMLHttpRequest |
| | 122 | + */ |
| | 123 | +function wfSupportsAjax() { |
| | 124 | + var request = sajax_init_object(); |
| | 125 | + var supportsAjax = request ? true : false; |
| | 126 | + delete request; |
| | 127 | + return supportsAjax; |
| | 128 | +} |
| \ No newline at end of file |
| Property changes on: trunk/phase3/skins/common/ajaxwatch.js |
| ___________________________________________________________________ |
| Name: svn:eol-style |
| 1 | 129 | + native |
| Index: trunk/phase3/skins/common/wikibits.js |
| — | — | @@ -473,11 +473,16 @@ |
| 474 | 474 | } |
| 475 | 475 | } |
| 476 | 476 | |
| 477 | | -function akeytt() { |
| | 477 | +/** |
| | 478 | + * Set up accesskeys/tooltips. If doId is specified, only set up for that id. |
| | 479 | + * |
| | 480 | + * @param mixed doId string or null |
| | 481 | + */ |
| | 482 | +function akeytt( doId ) { |
| 478 | 483 | if (typeof ta == "undefined" || !ta) { |
| 479 | 484 | return; |
| 480 | 485 | } |
| 481 | | - |
| | 486 | + |
| 482 | 487 | var pref; |
| 483 | 488 | if (is_safari || navigator.userAgent.toLowerCase().indexOf('mac') + 1 |
| 484 | 489 | || navigator.userAgent.toLowerCase().indexOf('konqueror') + 1 ) { |
| — | — | @@ -492,6 +497,10 @@ |
| 493 | 498 | pref = 'alt-'; |
| 494 | 499 | } |
| 495 | 500 | |
| | 501 | + if ( doId ) { |
| | 502 | + ta = [ta[doId]]; |
| | 503 | + } |
| | 504 | + |
| 496 | 505 | for (var id in ta) { |
| 497 | 506 | var n = document.getElementById(id); |
| 498 | 507 | if (n) { |
| — | — | @@ -881,7 +890,7 @@ |
| 882 | 891 | histrowinit(); |
| 883 | 892 | unhidetzbutton(); |
| 884 | 893 | tabbedprefs(); |
| 885 | | - akeytt(); |
| | 894 | + akeytt( null ); |
| 886 | 895 | scrollEditBox(); |
| 887 | 896 | setupCheckboxShiftClick(); |
| 888 | 897 | sortableTables(); |
| Index: trunk/phase3/includes/AjaxDispatcher.php |
| — | — | @@ -15,7 +15,7 @@ |
| 16 | 16 | var $args; |
| 17 | 17 | |
| 18 | 18 | function AjaxDispatcher() { |
| 19 | | - wfProfileIn( 'AjaxDispatcher::AjaxDispatcher' ); |
| | 19 | + wfProfileIn( __METHOD__ ); |
| 20 | 20 | |
| 21 | 21 | $this->mode = ""; |
| 22 | 22 | |
| — | — | @@ -42,7 +42,7 @@ |
| 43 | 43 | $this->args = array(); |
| 44 | 44 | } |
| 45 | 45 | } |
| 46 | | - wfProfileOut( 'AjaxDispatcher::AjaxDispatcher' ); |
| | 46 | + wfProfileOut( __METHOD__ ); |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | 49 | function performAction() { |
| — | — | @@ -51,7 +51,7 @@ |
| 52 | 52 | if ( empty( $this->mode ) ) { |
| 53 | 53 | return; |
| 54 | 54 | } |
| 55 | | - wfProfileIn( 'AjaxDispatcher::performAction' ); |
| | 55 | + wfProfileIn( __METHOD__ ); |
| 56 | 56 | |
| 57 | 57 | if (! in_array( $this->func_name, $wgAjaxExportList ) ) { |
| 58 | 58 | header( 'Status: 400 Bad Request', true, 400 ); |
| — | — | @@ -72,7 +72,7 @@ |
| 73 | 73 | $result->sendHeaders(); |
| 74 | 74 | $result->printText(); |
| 75 | 75 | } |
| 76 | | - |
| | 76 | + |
| 77 | 77 | } catch (Exception $e) { |
| 78 | 78 | if (!headers_sent()) { |
| 79 | 79 | header( 'Status: 500 Internal Error', true, 500 ); |
| — | — | @@ -83,7 +83,7 @@ |
| 84 | 84 | } |
| 85 | 85 | } |
| 86 | 86 | |
| 87 | | - wfProfileOut( 'AjaxDispatcher::performAction' ); |
| | 87 | + wfProfileOut( __METHOD__ ); |
| 88 | 88 | $wgOut = null; |
| 89 | 89 | } |
| 90 | 90 | } |
| Index: trunk/phase3/includes/Setup.php |
| — | — | @@ -171,6 +171,7 @@ |
| 172 | 172 | $wgPostCommitUpdateList = array(); |
| 173 | 173 | |
| 174 | 174 | if ( $wgAjaxSearch ) $wgAjaxExportList[] = 'wfSajaxSearch'; |
| | 175 | +if ( $wgAjaxWatch ) $wgAjaxExportList[] = 'wfAjaxWatch'; |
| 175 | 176 | |
| 176 | 177 | wfSeedRandom(); |
| 177 | 178 | |
| Index: trunk/phase3/includes/OutputPage.php |
| — | — | @@ -524,8 +524,8 @@ |
| 525 | 525 | public function output() { |
| 526 | 526 | global $wgUser, $wgOutputEncoding, $wgRequest; |
| 527 | 527 | global $wgContLanguageCode, $wgDebugRedirects, $wgMimeType; |
| 528 | | - global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgAjaxSearch, $wgServer; |
| 529 | | - global $wgStyleVersion; |
| | 528 | + global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgAjaxSearch, $wgAjaxWatch; |
| | 529 | + global $wgServer, $wgStyleVersion; |
| 530 | 530 | |
| 531 | 531 | if( $this->mDoNothing ){ |
| 532 | 532 | return; |
| — | — | @@ -536,11 +536,14 @@ |
| 537 | 537 | |
| 538 | 538 | if ( $wgUseAjax ) { |
| 539 | 539 | $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajax.js?$wgStyleVersion\"></script>\n" ); |
| 540 | | - } |
| | 540 | + if( $wgAjaxSearch ) { |
| | 541 | + $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxsearch.js\"></script>\n" ); |
| | 542 | + $this->addScript( "<script type=\"{$wgJsMimeType}\">hookEvent(\"load\", sajax_onload);</script>\n" ); |
| | 543 | + } |
| 541 | 544 | |
| 542 | | - if ( $wgUseAjax && $wgAjaxSearch ) { |
| 543 | | - $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxsearch.js?$wgStyleVersion\"></script>\n" ); |
| 544 | | - $this->addScript( "<script type=\"{$wgJsMimeType}\">hookEvent(\"load\", sajax_onload);</script>\n" ); |
| | 545 | + if( $wgAjaxWatch && $wgUser->isLoggedIn() ) { |
| | 546 | + $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxwatch.js\"></script>\n" ); |
| | 547 | + } |
| 545 | 548 | } |
| 546 | 549 | |
| 547 | 550 | if ( '' != $this->mRedirect ) { |
| Index: trunk/phase3/includes/AjaxFunctions.php |
| — | — | @@ -129,4 +129,43 @@ |
| 130 | 130 | return $response; |
| 131 | 131 | } |
| 132 | 132 | |
| | 133 | +/** |
| | 134 | + * Called for AJAX watch/unwatch requests. |
| | 135 | + * @param $pageID Integer ID of the page to be watched/unwatched |
| | 136 | + * @param $watch String 'w' to watch, 'u' to unwatch |
| | 137 | + * @return String '<w#>' or '<u#>' on successful watch or unwatch, respectively, or '<err#>' on error (invalid XML in case we want to add HTML sometime) |
| | 138 | + */ |
| | 139 | +function wfAjaxWatch($pageID, $watch) { |
| | 140 | + if(wfReadOnly()) |
| | 141 | + return '<err#>'; // redirect to action=(un)watch, which will display the database lock message |
| | 142 | + |
| | 143 | + if(('w' !== $watch && 'u' !== $watch) || !is_numeric($pageID)) |
| | 144 | + return '<err#>'; |
| | 145 | + $watch = 'w' === $watch; |
| | 146 | + $pageID = intval($pageID); |
| | 147 | + |
| | 148 | + $title = Title::newFromID($pageID); |
| | 149 | + if(!$title) |
| | 150 | + return '<err#>'; |
| | 151 | + $article = new Article($title); |
| | 152 | + $watching = $title->userIsWatching(); |
| | 153 | + |
| | 154 | + if($watch) { |
| | 155 | + if(!$watching) { |
| | 156 | + $dbw =& wfGetDB(DB_MASTER); |
| | 157 | + $dbw->begin(); |
| | 158 | + $article->doWatch(); |
| | 159 | + $dbw->commit(); |
| | 160 | + } |
| | 161 | + } else { |
| | 162 | + if($watching) { |
| | 163 | + $dbw =& wfGetDB(DB_MASTER); |
| | 164 | + $dbw->begin(); |
| | 165 | + $article->doUnwatch(); |
| | 166 | + $dbw->commit(); |
| | 167 | + } |
| | 168 | + } |
| | 169 | + |
| | 170 | + return $watch ? '<w#>' : '<u#>'; |
| | 171 | +} |
| 133 | 172 | ?> |
| Index: trunk/phase3/includes/DefaultSettings.php |
| — | — | @@ -1042,7 +1042,7 @@ |
| 1043 | 1043 | * to ensure that client-side caches don't keep obsolete copies of global |
| 1044 | 1044 | * styles. |
| 1045 | 1045 | */ |
| 1046 | | -$wgStyleVersion = '37'; |
| | 1046 | +$wgStyleVersion = '38'; |
| 1047 | 1047 | |
| 1048 | 1048 | |
| 1049 | 1049 | # Server-side caching: |
| — | — | @@ -2254,6 +2254,13 @@ |
| 2255 | 2255 | $wgAjaxExportList = array( ); |
| 2256 | 2256 | |
| 2257 | 2257 | /** |
| | 2258 | + * Enable watching/unwatching pages using AJAX. |
| | 2259 | + * Requires $wgUseAjax to be true too. |
| | 2260 | + * Causes wfAjaxWatch to be added to $wgAjaxExportList |
| | 2261 | + */ |
| | 2262 | +$wgAjaxWatch = false; |
| | 2263 | + |
| | 2264 | +/** |
| 2258 | 2265 | * Allow DISPLAYTITLE to change title display |
| 2259 | 2266 | */ |
| 2260 | 2267 | $wgAllowDisplayTitle = false ; |
| Index: trunk/phase3/includes/Skin.php |
| — | — | @@ -23,6 +23,7 @@ |
| 24 | 24 | var $rc_cache ; # Cache for Enhanced Recent Changes |
| 25 | 25 | var $rcCacheIndex ; # Recent Changes Cache Counter for visibility toggle |
| 26 | 26 | var $rcMoveIndex; |
| | 27 | + var $mWatchLinkNum = 0; // Appended to end of watch link id's |
| 27 | 28 | /**#@-*/ |
| 28 | 29 | |
| 29 | 30 | /** Constructor, call parent constructor */ |
| — | — | @@ -392,7 +393,7 @@ |
| 393 | 394 | */ |
| 394 | 395 | function getUserJs() { |
| 395 | 396 | $fname = 'Skin::getUserJs'; |
| 396 | | - wfProfileIn( $fname ); |
| | 397 | + wfProfileIn( __METHOD__ ); |
| 397 | 398 | |
| 398 | 399 | global $wgStylePath; |
| 399 | 400 | $s = "/* generated javascript */\n"; |
| — | — | @@ -403,7 +404,20 @@ |
| 404 | 405 | $s .= $commonJs; |
| 405 | 406 | } |
| 406 | 407 | |
| 407 | | - wfProfileOut( $fname ); |
| | 408 | + global $wgUseAjax, $wgAjaxWatch; |
| | 409 | + if($wgUseAjax && $wgAjaxWatch) { |
| | 410 | + $s .= " |
| | 411 | + |
| | 412 | +/* AJAX (un)watch (see /skins/common/ajaxwatch.js) */ |
| | 413 | +var wgAjaxWatch = { |
| | 414 | + watchMsg: '". str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'watch', array() ) )."', |
| | 415 | + unwatchMsg: '". str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'unwatch', array() ) )."', |
| | 416 | + watchingMsg: '". str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'watching', array() ) )."', |
| | 417 | + unwatchingMsg: '". str_replace( array("'", "\n"), array("\\'", ' '), wfMsgExt( 'unwatching', array() ) )."' |
| | 418 | +};"; |
| | 419 | + } |
| | 420 | + |
| | 421 | + wfProfileOut( __METHOD__ ); |
| 408 | 422 | return $s; |
| 409 | 423 | } |
| 410 | 424 | |
| — | — | @@ -1272,16 +1286,19 @@ |
| 1273 | 1287 | |
| 1274 | 1288 | function watchThisPage() { |
| 1275 | 1289 | global $wgOut, $wgTitle; |
| | 1290 | + ++$this->mWatchLinkNum; |
| 1276 | 1291 | |
| 1277 | 1292 | if ( $wgOut->isArticleRelated() ) { |
| 1278 | 1293 | if ( $wgTitle->userIsWatching() ) { |
| 1279 | 1294 | $t = wfMsg( 'unwatchthispage' ); |
| 1280 | 1295 | $q = 'action=unwatch'; |
| | 1296 | + $id = "mw-unwatch-link".$this->mWatchLinkNum; |
| 1281 | 1297 | } else { |
| 1282 | 1298 | $t = wfMsg( 'watchthispage' ); |
| 1283 | 1299 | $q = 'action=watch'; |
| | 1300 | + $id = 'mw-watch-link'.$this->mWatchLinkNum; |
| 1284 | 1301 | } |
| 1285 | | - $s = $this->makeKnownLinkObj( $wgTitle, $t, $q ); |
| | 1302 | + $s = $this->makeKnownLinkObj( $wgTitle, $t, $q, '', '', " id=\"$id\"" ); |
| 1286 | 1303 | } else { |
| 1287 | 1304 | $s = wfMsg( 'notanarticle' ); |
| 1288 | 1305 | } |
| Index: trunk/phase3/languages/messages/MessagesEn.php |
| — | — | @@ -1633,6 +1633,9 @@ |
| 1634 | 1634 | 'wlhideshowown' => '$1 my edits', |
| 1635 | 1635 | 'wlhideshowbots' => '$1 bot edits', |
| 1636 | 1636 | 'wldone' => 'Done.', |
| | 1637 | +# Displayed when you click the "watch" button and it's in the process of watching |
| | 1638 | +'watching' => 'Watching...', |
| | 1639 | +'unwatching' => 'Unwatching...', |
| 1637 | 1640 | |
| 1638 | 1641 | 'enotif_mailer' => '{{SITENAME}} Notification Mailer', |
| 1639 | 1642 | 'enotif_reset' => 'Mark all pages visited', |