Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/* Ajax user contributions stalker, version [0.0.2]
Originally from: http://en.wikipedia.org/wiki/User:Splarka/stalkcontribs.js

Does stuff.
*/

if(mw.config.get('wgCanonicalSpecialPageName') && mw.config.get('wgCanonicalSpecialPageName') == 'Contributions') addOnloadHook(stalkContribsInit)
//globals
var stalkingRefreshSeconds = '60';
var stalkingTS = '';

function stalkContribsInit() {
  var ucfrm = document.getElementsByTagName('form')[0];
  var target = queryString('target') || encodeURIComponent(ucfrm.target.value) || false;
  if(!target || target == '') return
  if(queryString('stalk')) {
    stalkContribs(target);
  } else {
    mw.util.addPortletLink('p-cactions',mw.config.get('wgScript') + '?title=' + mw.config.get('wgPageName') + '&target=' + target + '&stalk=true','Stalk','ca-stalk','Stalk user for new contribs.');
  }
}

function stalkContribs(target) {
  mw.notify('Fetching timestamp of latest contribution for: ' + decodeURIComponent(target) + ' ...');
  var url = mw.config.get('wgScriptPath') + '/api.php?action=query&format=json&list=usercontribs&uclimit=1&ucprop=timestamp&ucuser=' + target;
  var req = new XMLHttpRequest();
  req.open('GET', url, true);
  req.onreadystatechange = function() {
    if(req.readyState == 4 && req.status == 200) {
      eval("stalkContribsCB(" + req.responseText + ",'" + req.responseText.replace(/\'/g,"`") + "')");
    }
  }
  req.send(null);
}

function stalkContribsCB(obj,txt) {
  if(obj['error']) {
    mw.notify('Api error: ' + obj['error']['code'] + ' - ' + obj['error']['info']);
    return;
  }
  if(!obj['query'] || !obj['query']['usercontribs']) {
    mw.notify('Error: Unexpected response: ' + txt);
    return;
  }
  if(!obj['query']['usercontribs'][0] || !obj['query']['usercontribs'][0]['timestamp'] || !obj['query']['usercontribs'][0]['user']) {
    // user has no contribs?
    var ts = '0';
    var target = queryString('target') || encodeURIComponent(ucfrm.target.value) || false;
    if(!target) {
      mw.notify('Error: no target found');
      return;
    }
  } else {
    var ts = obj['query']['usercontribs'][0]['timestamp'];
    var target = obj['query']['usercontribs'][0]['user'];
  }
  if(stalkingTS == '') stalkingTS = ts

  if(stalkingTS != ts) {
    var msg = 'New contributions detected for ' + target;
    mw.notify(msg); document.title = msg; alert(msg);
    //window.location.reload(); //a bit agressive, purges css/js too
    window.location.replace(window.location.href);
  } else {
    mw.notify('Timestamp of latest contribution for ' + decodeURIComponent(target) + ' is: ' + ts);
    setTimeout('stalkContribs("' + encodeURIComponent(target) + '")', stalkingRefreshSeconds * 1000);
  }
}

function queryString(p) {
  var re = RegExp('[&?]' + p + '=([^&]*)');
  var matches;
  if (matches = re.exec(document.location)) {
    try { 
      return decodeURI(matches[1]);
    } catch (e) {
    }
  }
  return null;
}