User:Splarka/ajaxrecentchanges.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:Splarka/ajaxrecentchanges. |
/* Ajax recent changes and patrolling framework, version [0.0.5a]
Originally from: http://wiki.riteme.site/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;
}