MediaWiki:Gadget-ajaxrecentchanges.js

/* Ajax recent changes and patrolling framework, version [0.0.5a] Originally from: http://en.wikipedia.org/wiki/User:Splarka/ajaxrecentchanges.js

Note:
 * Patrol flags/links will sometimes show up where patrolling is not enabled.
 * This was a bug in the API that generated patrol tokens too often.
 * Fixed in http://svn.wikimedia.org/viewvc/mediawiki?view=rev&revision=49000

Todo:
 * Checkbox for batch patrolling

Wontdo:
 * Parse comments
 * Add (talk|contribs|block) links, click their name, lazy
 * Localize error messages or focus on the log message 'logaction' (wrong tense, but it works fine).

if(!window.arc_i18n) { var arc_i18n = { 'title'	:'Ajax recent changes', 'desc'	:'Paginated enhanced ajax recent changes and patrolling.', 'mypatrol'	:'My patrol log', 'startstamp'	:'Start timestamp (8601)', 'limit'	:'Limit', 'showapb'	:'Show ajax patrol buttons', 'filterflag'	:'Filter by flag', 'minor'	:'Minor', 'bot'		:'Bot', 'anon'	:'Anon', 'redirect'	:'Redirect', 'patrolled'	:'Patrolled', 'all'		:'All', 'filtertype'	:'Filter by type', 'edit'	:'Edits', 'new'		:'New pages', 'log'		:'Logs', 'filterns'	:'Filter by namespace', 'fetch'	:'Fetch', 'noresults'	:'Nothing found.', 'diff'	:'diff', 'hist'	:'hist', 'patrolbtn'	:'Patrol', 'logsuffix'	:' log', 'patroldone'	:'done', 'nsmain'	:'MAIN' } }

addOnloadHook(function { addPortletLink('p-tb','/wiki/Special:BlankPage?blankspecial=ajaxrc',arc_i18n['title'],'t-ajax-rc',arc_i18n['desc']); });

if(wgCanonicalSpecialPageName && wgCanonicalSpecialPageName.toLowerCase == 'blankpage' && queryString('blankspecial') == 'ajaxrc') { document.title = arc_i18n['title']; addOnloadHook(ajaxRcForm); }

function ajaxRcForm { addPortletLink('p-tb','/wiki/Special:Log/patrol?user=' + encodeURIComponent(wgUserName),arc_i18n['mypatrol']); //subvert this Special: page to our own needs. var con = document.getElementById('content') || document.getElementById('mw_content'); var bcon = document.getElementById('bodyContent') || document.getElementById('mw_contentholder'); var fh = getElementsByClassName(con,'h1','firstHeading')[0]; while(fh.firstChild) fh.removeChild(fh.firstChild) fh.appendChild(document.createTextNode(arc_i18n['title'])); for(var i=0;i<bcon.childNodes.length;i++) { bcur = bcon.childNodes[i]; if(bcur.id != 'siteSub' && bcur.id != 'contentSub' && bcur.className != 'visualClear') { while(bcur.firstChild) bcur.removeChild(bcur.firstChild) if(bcur.nodeType == 3) bcur.nodeValue = ''; } }

appendCSS(   '#arc-form {border:1px solid black;padding:.5em;margin:2em;} #arc-out {border:1px solid black;padding:.5em;margin:.5em;}'  + '#arc-fetch {padding:0 1em;margin:0 .5em;} .clear {clear:both;} .arc-box {border:1px solid #bbbbbb;padding:.2em;margin:.5em;}'  + '.arc-cbox {display:block;float:left;width:11em;white-space:nowrap;overflow:hidden;font-size:80%;margin:0 .f2em;}'  + '.arc-box-label {text-align:center;border-bottom:1px solid #bbbbbb;margin-bottom:.3em} .spacer {border:1px solid transparent;margin-right:.5em;}'  + '.arc-patrol {border:2px outset #bbbbbb;background-color:#bbbbbb;color:black;padding:2px;margin:3px;text-decoration:none;}'  );

var form = '' + '' + arc_i18n['startstamp'] + ':  ' + '' + arc_i18n['limit'] + ':  ' + '' + arc_i18n['showapb'] + ' ' + ' ' + arc_i18n['filterflag'] + ': ' + '' + arc_i18n['minor'] + ':  ' + '' + arc_i18n['bot'] + ':  ' + '' + arc_i18n['anon'] + ':  ' + '' + arc_i18n['redirect'] + ':  ' + '' + arc_i18n['patrolled'] + ': <input type="button" name="arc-f-patrolled" id="arc-f-patrolled" value="' + arc_i18n['all'] + '" onclick="ajaxRcFlagChange(this)" />' + ' '   + ' ' + arc_i18n['filtertype'] + ': ' + '<input type="checkbox" name="arc-t-edit" id="arc-t-edit" checked="checked" value="edit" /><label for="arc-t-edit">' + arc_i18n['edit'] + ' ' + '<input type="checkbox" name="arc-t-new" id="arc-t-new" checked="checked" value="new" /><label for="arc-t-new">' + arc_i18n['new'] + ' ' + '<input type="checkbox" name="arc-t-log" id="arc-t-log" checked="checked" value="log" /><label for="arc-t-log">' + arc_i18n['log'] + ' ' + ' '   + '  ' + arc_i18n['filterns'] + '  ' + ''    + '<input type="button" name="fetch" value="' + arc_i18n['fetch'] + '" id="arc-fetch" onclick="ajaxRcFetch" /> ' + ' '

bcon.innerHTML += form + ' '; importScriptURI(wgScriptPath + '/api.php?action=query&meta=siteinfo&siprop=namespaces&format=json&callback=ajaxRcFormNamespacesCB&smaxage=2678400&maxage=2678400'); }

function ajaxRcFetch(timestamp,direction) { document.getElementById('arc-fetch').setAttribute('disabled','disabled'); var nav = document.getElementById('arc-fetchnav'); if(nav) nav.style.visibility = 'hidden' injectSpinner(document.getElementById('arc-fetch'),'arc-spin');

//direction var rcdir = ''; if(direction) rcdir = '&rcdir=' + direction + '&requestid=' + direction

//start var rcstart = timestamp || document.getElementById('arc-start').value; rcstart = rcstart.replace(/[^\d]*/g,''); if(rcstart != '' && /^\d{14}$/.test(rcstart)) { rcstart = '&rcstart=' + rcstart; } else { rcstart = ''; }

//limit var rclimit = parseInt(document.getElementById('arc-limit').value); if(isNaN(rclimit)) rclimit = 100 rclimit = '&rclimit=' + rclimit;

//type var tb = document.getElementById('arc-t-boxen').getElementsByTagName('input'); var rctype = []; for(var i=0;i<tb.length;i++) if(tb[i].checked) rctype.push(tb[i].value) if(rctype.length > 0) { rctype = '&rctype=' + rctype.join('|'); } else { rctype = ''; }

//show (flags) var fb = document.getElementById('arc-f-boxen').getElementsByTagName('input'); var rcshow = []; for(var i=0;i<fb.length;i++) if(fb[i].value != arc_i18n['all']) rcshow.push(fb[i].value) if(rcshow.length > 0) { rcshow = '&rcshow=' + rcshow.join('|'); } else { rcshow = ''; }

//namespace var nsb = document.getElementById('arc-ns-boxen').getElementsByTagName('input'); var rcnamespace = []; for(var i=0;i<nsb.length;i++) if(nsb[i].checked) rcnamespace.push(nsb[i].value) if(rcnamespace.length > 0) { rcnamespace = '&rcnamespace=' + rcnamespace.join('|'); } else { rcnamespace = ''; }

//prop & token var rcprop = '&rcprop=user|comment|flags|timestamp|title|ids|sizes|redirect|patrolled|loginfo'; var rctoken = '&rctoken=patrol';

var url = wgScriptPath + '/api.php?action=query&format=json&list=recentchanges' + rcdir + rcstart + rclimit + rctype + rcshow + rcnamespace + rcprop + rctoken; var req = sajax_init_object; req.open('GET', url, true); req.onreadystatechange = function { if(req.readyState == 4 && req.status == 200) { eval("ajaxRcFetchHandler(" + req.responseText + ",'" + req.responseText.replace(/\'/g,"`") + "')"); } }  req.send(null); }

function ajaxRcFetchHandler(obj,txt) { document.getElementById('arc-fetch').removeAttribute('disabled'); removeSpinner('arc-spin'); var out = document.getElementById('arc-out'); var ajaxpatrol = document.getElementById('arc-patrol-enable').checked; while(out.firstChild) out.removeChild(out.firstChild) if(obj['error']) { out.appendChild(document.createTextNode('Api error: ' + obj['error']['code'] + ' - ' + obj['error']['info'] + '\n')); return; } if(!obj['query'] || !obj['query']['recentchanges']) { out.appendChild(document.createTextNode('Unexpected response: ' + txt + '\n')); return; } var rc = obj['query']['recentchanges']; if(rc.length == 0) { out.appendChild(document.createTextNode(arc_i18n['noresults'])); return; }

var backwards = false; if(obj['requestid'] && obj['requestid'] == 'newer') backwards = true

var nav = document.createElement('div'); nav.setAttribute('id','arc-fetchnav'); if(obj['query-continue'] && obj['query-continue']['recentchanges'] && obj['query-continue']['recentchanges']['rcstart']) { var rcstart = obj['query-continue']['recentchanges']['rcstart']; var rcstartnewer = rcstart; var rcstartolder = rcstart; if(!backwards) { rcstartnewer = rc[0]['timestamp']; } else { rcstartolder = rc[0]['timestamp']; }   addLinkChild(nav,'javascript:ajaxRcFetch("' + rcstartnewer + '","newer")','Newer'); addText(nav,' | '); addLinkChild(nav,'javascript:ajaxRcFetch("' + rcstartolder + '","older")','Older'); } else if(backwards) { addLinkChild(nav,'javascript:ajaxRcFetch','Older'); } out.appendChild(nav);

var ul = document.createElement('ul'); for(var i=0;i<rc.length;i++) { var r = rc[i]; var li = document.createElement('li'); if(r['type'] == 'edit') { var rcid = '' if(typeof r['patrolled'] == 'undefined' && r['rcid'] && r['patroltoken']) rcid = '&rcid=' + r['rcid'] addText(li,'(');     addLinkChild(li,wgScript + '?oldid=' + r['old_revid'] + '&diff=' + r['revid'] + rcid,arc_i18n['diff']);      addText(li,') (');      addLinkChild(li,wgScript + '?curid=' + r['pageid'] + '&action=history',arc_i18n['hist']);      addText(li,') . . '); if(typeof r['bot'] != 'undefined') addText(li,'b','span','bot') if(typeof r['minor'] != 'undefined') addText(li,'m','span','minor') if(rcid != '' && r['patroltoken'])addText(li,'!','span','unpatrolled'); addText(li,' '); addLinkChild(li,wgScript + '?curid=' + r['pageid'],r['title']); var size = '' + (parseInt(r['newlen']) - parseInt(r['oldlen'])); if(size.substring(0,1) != '-') size = '+' + size addText(li,'; ' + r['timestamp'].replace(/[TZ]/ig,' ') + ' . . (' + size + ') . . ' ); addLinkChild(li,wgScript + '?title=Special:Contributions&target=' + encodeURIComponent(r['user']),r['user']); if(r['comment']) addText(li,' (' + r['comment'] + ')','i') if(ajaxpatrol == true && rcid != '' && r['patroltoken']) addLinkChild(li,'javascript:ajaxRcDoPatrol("' + r['rcid'] + '","' + encodeURIComponent(encodeURIComponent(r['patroltoken'])) + '")',arc_i18n['patrolbtn'],'arc-patrol-' + r['rcid'],'arc-patrol'); } else if(r['type'] == 'new') { var rcid = '' if(typeof r['patrolled'] == 'undefined' && r['rcid']) rcid = '&rcid=' + r['rcid'] addText(li,'(' + arc_i18n['diff'] + ') (');     addLinkChild(li,wgScript + '?curid=' + r['pageid'] + '&action=history',arc_i18n['hist']);      addText(li,') . . '); addText(li,'N','span','newpage'); if(rcid != '' && r['patroltoken']) addText(li,'!','span','unpatrolled'); addText(li,' '); addLinkChild(li,wgScript + '?curid=' + r['pageid'] + rcid,r['title']); addText(li,'; ' + r['timestamp'].replace(/[TZ]/ig,' ') + ' . . (+' + r['newlen'] + ') . . ' ); addLinkChild(li,wgScript + '?title=Special:Contributions&target=' + encodeURIComponent(r['user']),r['user']); if(r['comment']) addText(li,' (' + r['comment'] + ')','i') if(ajaxpatrol == true && rcid != '' && r['patroltoken']) addLinkChild(li,'javascript:ajaxRcDoPatrol("' + r['rcid'] + '","' + encodeURIComponent(encodeURIComponent(r['patroltoken'])) + '")',arc_i18n['patrolbtn'],'arc-patrol-' + r['rcid'],'arc-patrol'); } else if(r['type'] == 'log') { addText(li,'(');     addLinkChild(li,wgScript + '?title=Special:Log&type=' + r['logtype'],r['logtype'] + arc_i18n['logsuffix']);      addText(li,'); ' + r['timestamp'].replace(/[TZ]/ig,' ') + ' . . ' ); addLinkChild(li,wgScript + '?title=Special:Contributions&target=' + encodeURIComponent(r['user']),r['user']); addText(li,' ' + r['logaction'] + ' '); addLinkChild(li,wgScript + '?title=' + encodeURIComponent(r['title']),r['title']); if(r['comment']) addText(li,' (' + r['comment'] + ')','i') }   if(backwards && ul.firstChild) { ul.insertBefore(li,ul.firstChild); } else { ul.appendChild(li); } }  out.appendChild(ul); }

function ajaxRcDoPatrol(rcid,token) { var params = 'action=patrol&format=json&requestid=' + rcid + '&rcid=' + rcid + '&token=' + token; var url = wgScriptPath + '/api.php'; var req = sajax_init_object; req.open('POST', url, true); req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); req.setRequestHeader('Content-length', params.length); req.setRequestHeader('Connection', 'close'); req.onreadystatechange = function { if(req.readyState == 4 && req.status == 200) { eval("ajaxRcDidPatrol(" + req.responseText + ",'" + req.responseText.replace(/\'/g,"`") + "')"); } }  req.send(params); }

function ajaxRcDidPatrol(obj,txt) { if(!obj['requestid']) return; if(obj['error']) { alert('Api error in patrolling rcid=' + obj['requestid'] + ' : ' + obj['error']['code'] + '\n' + obj['error']['info']); return; } var button = document.getElementById('arc-patrol-' + obj['requestid']); if(!button || !obj['patrol']) return button.setAttribute('href','javascript:alert("(' + arc_i18n['patroldone'] + ')");'); addText(button,' (' + arc_i18n['patroldone'] + ')'); //{"requestid":"80879","error":{"code":"permissiondenied","info":"Permission denied"}} //{"requestid":"80871","patrol":{"rcid":80871,"ns":2,"title":"Page Title Here"}} }

function ajaxRcFlagChange(obj) { var type = obj.getAttribute('id').substring(6); var val = obj.value; if(val == type) { obj.value = '!' + type; } else if(val == '!' + type) { obj.value = arc_i18n['all']; } else { obj.value = type; } }

function ajaxRcFormNamespacesCB(obj) { if(!obj['query'] || !obj['query']['namespaces']) return var ns = obj['query']['namespaces']; var nsb = document.getElementById('arc-ns-boxen'); for(var i in ns) { if(typeof i != 'string' || ns[i]['id'] < 0) continue var title = ns[i]['*']; if(ns[i]['id'] == '') title = arc_i18n['nsmain'] var canon = ns[i]['canonical'] || ''; addCheckboxChild(nsb,'arc-ns-' + ns[i]['id'],i,false,'arc-ns-' + ns[i]['id'],title,'arc-cbox',ns[i]['id'] + ' => ' + canon); //nsb.appendChild(document.createElement('br')); } var div = nsb.appendChild(document.createElement('div')); div.setAttribute('class','clear'); }

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

function addText(obj,txt,elem,classes) { if(elem) { var e = document.createElement(elem); e.appendChild(document.createTextNode(txt)); if(classes) e.setAttribute('class',classes); obj.appendChild(e); return e; } else { obj.appendChild(document.createTextNode(txt)); } }

function addLinkChild(obj,href,text,id,classes,title) { if(!obj || !href || !text) return false; var a = document.createElement('a'); a.setAttribute('href',href); a.appendChild(document.createTextNode(text)); if(id) a.setAttribute('id',id); if(classes) a.setAttribute('class',classes); if(title) a.setAttribute('title',title); obj.appendChild(a); return a; }

function addCheckboxChild(obj,name,value,checked,id,label,classes,title) { if(!obj || !name) return false; var span = document.createElement('span'); var c = document.createElement('input'); c.setAttribute('name',name); c.setAttribute('type','checkbox'); if(value) c.setAttribute('value',value) if(checked) c.setAttribute('checked','checked') if(title) c.setAttribute('title',title) span.appendChild(c); if(id) { c.setAttribute('id',id); if(label) { var l = document.createElement('label'); l.setAttribute('for',id); l.appendChild(document.createTextNode(label)); if(title) l.setAttribute('title',title) span.appendChild(l); } }  if(classes) span.setAttribute('class',classes); obj.appendChild(span); return span; }