/* 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'
}
}
$(function() {
mw.util.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() {
mw.util.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 = '<form id="arc-form" action="javascript:void(0)">'
+ '<label for="arc-start">' + arc_i18n['startstamp'] + ':</label> <input type="text" name="arc-start" id="arc-start" value="" size="25" maxlength="20"/><span class="spacer"></span>'
+ '<label for="arc-limit">' + arc_i18n['limit'] + ':</label> <input type="text" name="arc-limit" id="arc-limit" value="50" size="5" maxlength="3"/><span class="spacer"></span>'
+ '<input type="checkbox" name="arc-patrol-enable" id="arc-patrol-enable" /><label for="arc-patrol-enable">' + arc_i18n['showapb'] + '</label><br/>'
+ '<div class="arc-box" id="arc-f-boxen">' + arc_i18n['filterflag'] + ': <span class="spacer"></span>'
+ '<label for="arc-f-minor">' + arc_i18n['minor'] + ':</label> <input type="button" name="arc-f-minor" id="arc-f-minor" value="' + arc_i18n['all'] + '" onclick="ajaxRcFlagChange(this)" /><span class="spacer"></span>'
+ '<label for="arc-f-bot">' + arc_i18n['bot'] + ':</label> <input type="button" name="arc-f-bot" id="arc-f-bot" value="!bot" onclick="ajaxRcFlagChange(this)" /><span class="spacer"></span>'
+ '<label for="arc-f-anon">' + arc_i18n['anon'] + ':</label> <input type="button" name="arc-f-anon" id="arc-f-anon" value="' + arc_i18n['all'] + '" onclick="ajaxRcFlagChange(this)" /><span class="spacer"></span>'
+ '<label for="arc-f-redirect">' + arc_i18n['redirect'] + ':</label> <input type="button" name="arc-f-redirect" id="arc-f-redirect" value="' + arc_i18n['all'] + '" onclick="ajaxRcFlagChange(this)" /><span class="spacer"></span>'
+ '<label for="arc-f-patrolled">' + arc_i18n['patrolled'] + ':</label> <input type="button" name="arc-f-patrolled" id="arc-f-patrolled" value="' + arc_i18n['all'] + '" onclick="ajaxRcFlagChange(this)" />'
+ '</div>'
+ '<div class="arc-box" id="arc-t-boxen">' + arc_i18n['filtertype'] + ': <span class="spacer"></span>'
+ '<input type="checkbox" name="arc-t-edit" id="arc-t-edit" checked="checked" value="edit" /><label for="arc-t-edit">' + arc_i18n['edit'] + '</label><span class="spacer"></span>'
+ '<input type="checkbox" name="arc-t-new" id="arc-t-new" checked="checked" value="new" /><label for="arc-t-new">' + arc_i18n['new'] + '</label><span class="spacer"></span>'
+ '<input type="checkbox" name="arc-t-log" id="arc-t-log" checked="checked" value="log" /><label for="arc-t-log">' + arc_i18n['log'] + '</label><span class="spacer"></span>'
+ '</div>'
+ '<div class="arc-box" id="arc-ns-boxen"><div class="arc-box-label">' + arc_i18n['filterns'] + '</div></div>'
+ ''
+ '<input type="button" name="fetch" value="' + arc_i18n['fetch'] + '" id="arc-fetch" onclick="ajaxRcFetch()" /><div class="clear"></div>'
+ '</form>'
bcon.innerHTML += form + '<div id="arc-out"></div>';
mw.loader.load(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&rawcontinue=&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;
}