Knowledge (XXG)

User:PleaseStand/recent2.js

Source 📝

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 Knowledge (XXG):Bypass your cache.
/*

	This tool hits the RSS feed for recent changes every 30 seconds or
	so and checks for common vandalism. It does not make a separate
	server request for every edit.

	Navigation popups have to be turned on for the "fake" rollback to work.

	Originally created by Lupin, maintained by others
	URL:	http://en.wikipedia.org/User:Lupin/recent2.js
	Modified by PleaseStand
	URL:	http://en.wikipedia.org/User:PleaseStand/recent2.js

*/


// <pre><nowiki>

recent2={
// Edit these to your liking.
// Make sure there's a comma at the end of each line.
badwordsUrl:'User:Lupin/badwords',
filterPage:'User:Lupin/Filter_recent_changes',
allRecentPage:'User:Lupin/All_recent_changes',
recentIPPage:'User:Lupin/Recent_IP_edits',
monitorWatchlistPage:'User:Lupin/Monitor_my_watchlist',
spelldictUrl:'Knowledge (XXG):Lists_of_common_misspellings/For_machines',
spelldictPage:'User:Lupin/Live_spellcheck',
huggleWhitelistPage:'index.php?title=Knowledge (XXG):Huggle/Whitelist&action=raw&ctype=text/css',
safePages:'(ikipedia:(ntroduction|andbox|utorial*/sandbox)|emplate:(X|Template sandbox))',
linkify:true,
updateSeconds:15,
ipUserRegex:RegExp('(User:)?((25|2|1||)\\.){3}'+
'(25|2|1||)'),
outputSeparator:'<hr>',
templateNamespace:'Template',
namespaces:{'Media':1,"Special":1,"User":1,"User talk":1,"Knowledge (XXG)":1,
"Knowledge (XXG) talk":1,"File":1,"File talk":1,"MediaWiki":1,
"MediaWiki talk":1,"Template":1,"Template talk":1,"Help":1,
"Help talk":1,"Category":1,"Category talk":1,"Portal":1,"Portal talk":1,
"Book":1,"Book talk":1},
apiAulimitUser:500,
apiAulimitSysop:5000,
backgroundWindowsMax:6,
dummy:null// leave this last one alone
};

recent2.download=function(bundle){
// mandatory: bundle.url
// optional:  bundle.onSuccess (xmlhttprequest, bundle)
// optional:  bundle.onFailure (xmlhttprequest, bundle)
// optional:  bundle.otherStuff OK too, passed to onSuccess and onFailure

varx=sajax_init_object();
if(x){
x.onreadystatechange=function(){
x.readyState==4&&recent2.downloadComplete(x,bundle);
};
x.open("GET",bundle.url,true);
x.send(null);
}
returnx;
}

recent2.downloadComplete=function(x,bundle){
x.status==200&&(bundle.onSuccess&&bundle.onSuccess(x,bundle)||true)
||(bundle.onFailure&&bundle.onFailure(x,bundle)||alert(x.statusText));
};

if(!recent2.outputPosition){recent2.outputPosition='';}
recent2.loading=false;
window.badWords=null;

// paths
recent2.articlePath=mw.config.get('wgServer')+mw.config.get('wgArticlePath').replace(/\$1/,'');
recent2.scriptPath=mw.config.get('wgServer')+mw.config.get('wgScriptPath')+'/';

recent2.getBadWords=function(){
recent2.loading=true;
recent2.download({url:recent2.scriptPath+'index.php?title='+
recent2.badwordsUrl+'&action=raw&ctype=text/css&maxage=7200',// reload every 2 h
onSuccess:recent2.processBadWords,
onFailure:function(){setTimeout(recent2.getBadWords,15000);returntrue;}});
}

window.diffCellRe=RegExp("<td class=\\\"diff-marker\\\">\\+<\\/td>\\s*"+
"<td\\b*>\\s*<div>\\s*(.*?)\\s*<\\/div>\\s*<\\/td>",'gi');


// processBadWords: generate the badWords RegExp from
// the downloaded data.
// d is the xmlhttprequest object from the download
recent2.processBadWords=function(d){
vardata=d.responseText.split('\n');
varphrase=;
varstring=;
for(vari=0;i<data.length;++i){
vars=data;

// ignore empty lines, whitespace-only lines and lines starting with '<'
if(/^\s*$|^</.test(s)){continue;}

// lines beginning and ending with a (back-)slash (and possibly trailing
// whitespace) are treated as regexps
if(/^().*\1\s*$/.test(s)){
varisPhrase=(s.charAt(0)=='/');
// remove slashes and trailing whitespace
s=s.replace(/^()|(\s*$)/g,'');
// escape opening parens: ( -> (?:
s=s.replace(/\(?!\?/g,'(?:');
// check that s represents a valid regexp
try{varr=newRegExp(s);}
catch(err){
varerrDiv=newOutputDiv('recent2_error',recent2.outputPosition);
errDiv.innerHTML='Warning: ignoring odd-looking regexp on line '+i
+' of <a href="/'+recent2.badwordsUrl+'">badwords</a>:<pre>'+s+'</pre>';
continue;
}
if(isPhrase)phrase.push(s);elsestring.push(s);
}else{
// treat this line as a non-regexp and escape it.
phrase.push(s.replace(RegExp('(])','g'),'\\$1'));
}
}
//                      123                                3       2|4                        4|5         56                        67        71
//                      (((    repeated char               )       )|(   ... | strings | ...  )|( border  )(   ... | phrases | ...  )( border ))
window.badWords=RegExp("(((.\\s'=wI:*#0-9a-f])\\3{2,})|("+string.join('|')+")|(^|)("+phrase.join('|')+")(?!))",'gi');
recent2.loading=false;
};

recent2.watchlist=null;

recent2.getWatchlist=function(){
recent2.loading=true;
recent2.download({url:recent2.articlePath+'Special:Watchlist/raw',
onSuccess:recent2.processWatchlist,
onFailure:function(){setTimeout(getWatchlist,15000);returntrue;}});
};

recent2.processWatchlist=function(req,bundle){
varwatchlist={};
varlines=req.responseText.split('\n');
varinList=false;
vararticle='';
for(vari=0;i<lines.length;++i){
if(inList||lines.indexOf('<textarea id="titles"')==0){
if(inList&&lines.indexOf('</textarea>')==0){
window.watchlist=watchlist;
return;
}
if(!inList){
inList=true;
article=lines.replace(/^.*>/,'');
}else{
article=lines;
}
watchlist=true;
}
}
};

recent2.loading=false;
window.spelldict=null;

recent2.getSpelldict=function(){
recent2.loading=false;
recent2.download({url:recent2.scriptPath+'index.php?title='+recent2.spelldictUrl+'&action=raw&ctype=text/css',
onSuccess:recent2.processSpelldict,
onFailure:function(){setTimeout(getSpelldict,15000);returntrue;}});
};

recent2.processSpelldict=function(req,bundle){
varspelldict={};
varlines=req.responseText.split('\n');
vara=;
for(vari=0;i<lines.length;++i){
varsplit=lines.split('->');
if(split.length<2){continue;}
split=split.slice(1).join('->').split(/, */);
split=split.toLowerCase().replace(/^\s*/,'');
spelldict]=split;
a.push(split);
}
window.spelldict=spelldict;
window.spellRe=RegExp('\\b('+a.join('|')+')\\b','i');
recent2.loading=false;
};

recent2.feed=recent2.scriptPath+'index.php?title=Special:RecentChanges&feed=rss&action=purge';

window.newOutputDiv=function(klass,position,immortal){
varh1=document.getElementsByTagName('h1');
varret=document.createElement('div');
if(klass){ret.className=klass;}
if(!position){position='bottom';}
switch(position){
case'top':
h1.parentNode.insertBefore(ret,h1.nextSibling);
break;
case'bottom':
h1.parentNode.appendChild(ret);
break;
default:
if(!newOutputDiv.alerted){
alert('Unknown position '+position+' in recent2.js, newOutputDiv');
window.newOutputDiv.alerted=true;
}
returnnewOutputDiv(klass,'bottom');
}
if(!immortal){ret.id=newOutputDiv.uid++;}
window.outputDivs.push(ret);
returnret;
};
window.newOutputDiv.alerted=false;
window.newOutputDiv.uid=0;
window.outputDivs=;

window.grabRecentChanges=function(feed){
// If initialization data is still loading, do not load any others. Try again in 500 ms.
if(recent2.loading)returnsetTimeout(function(){grabRecentChanges(feed);},500);

if(!window.badWords&&recent2.filter_badwords){
recent2.getBadWords();
returnsetTimeout(function(){grabRecentChanges(feed);},500);
}
if(!window.watchlist&&recent2.filter_watchlist){
recent2.getWatchlist();
returnsetTimeout(function(){grabRecentChanges(feed);},500);
}
if(!window.spelldict&&recent2.filter_spelling){
recent2.getSpelldict();
returnsetTimeout(function(){grabRecentChanges(feed);},500);
}
if(typeof(recent2.sysopRegExp)=='undefined'){
recent2.getSysops();
returnsetTimeout(function(){grabRecentChanges(feed);},500);
}
if(typeof(recent2.rollbackerRegExp)=='undefined'){
recent2.getRollbackers();
returnsetTimeout(function(){grabRecentChanges(feed);},500);
}
if(typeof(recent2.huggledRegExp)=='undefined'){
recent2.getHuggleds();
returnsetTimeout(function(){grabRecentChanges(feed);},500);
}

varpos=recent2.outputPosition;
if(pos=='top'){
varoutput=newOutputDiv('recent2.lines',pos);
varstatus=newOutputDiv('recent2.status',pos);
}else{
varstatus=newOutputDiv('recent2.status',pos);
varoutput=newOutputDiv('recent2.lines',pos);
}
status.style.borderStyle='solid';
status.style.borderColor='orange';
status.innerHTML=greyFont+'('+recent2.count+') updating...</font>';

// this abort stuff doesn't work properly for some reason...
//recent2.lastFeedDownload && recent2.lastFeedDownload.abort(); // } catch (summatNasty) { /* do nothing */ }
recent2.lastFeedDownload=recent2.download({url:feed,
onSuccess:processRecentChanges,
output:output,status:status,onFailure:feedFailed});
};

vargreyFont='<font color="#777">';

window.feedFailed=function(x,bundle){
try{bundle.status.innerHTML+=greyFont+'failed: '+x.statusText+'</font>';}
catch(err){bundle.status.innerHTML+=greyFont+'failed badly: '+err+'</font>';}
returntrue;
};

recent2.newWindows=true;

window.linkmaker=function(url,text){
vars='<a href="'+url+'"';
recent2.newWindows&&(s+=' target="_blank"');
s+='>'+text+'</a>';
returns;
};

recent2.delayedLines={};
recent2.delay=0;

window.processRecentChanges=function(req,bundle){
recent2.initialId=processRecentChanges.id;
recent2.latest=processRecentChanges.lastDate;
vardoc;
if(doc=req.responseXML.documentElement){
if(recent2.items=doc.getElementsByTagName('item')){
if((recent2.itemsCurrent=recent2.items.length)>0){
recent2.bundleRef=bundle;
processRecentChangesSingle();// start processing one diff every 5 ms
return;
}
}
}
processRecentChangesDisplay(bundle);
return;
}

recent2.safePagesRe=newRegExp('^'+recent2.safePages+'$');
recent2.changeDelay=5;// delay between processing each diff, in ms

window.nextChangeSoon=function(rightNow){
setTimeout(processRecentChangesSingle,rightNow?0:recent2.changeDelay);
};

// process single diff items delayed by a short timespan
window.processRecentChangesSingle=function(){
recent2.itemsCurrent--;
vari=recent2.itemsCurrent;
varitems=recent2.items;
if(i<0){processRecentChangesDisplay(recent2.bundleRef);return;}

vartimestamp=Date.parse(getFirstTagContent(items,'pubDate'));
if(timestamp<=processRecentChanges.lastDate){nextChangeSoon(true);return;}
recent2.latest=(timestamp>recent2.latest)?timestamp:recent2.latest;

vardiffText=getFirstTagContent(items,'description').split('</tr>').join('</tr>\n');
vareditSummary=diffText.replace(RegExp('^<p>(.*?)</p>*'),'$1');
vareditor=getFirstTagContent(items,'creator')||getFirstTagContent(items,'dc:creator');

if(recent2.ignore_my_edits&&wgUserName==editor){return;}

// NB article is the link attribute - a fully qualified URL
// strip out the &diff=...&oldid=...  bit to leave only ?title=...
vararticle=getFirstTagContent(items,'link').split('&');
if(recent2.delayedLines&&recent2.delayedLines.editor!=editor){
deleterecent2.delayedLines;
}

if(recent2.filter_anonsOnly&&!recent2.ipUserRegex.test(editor)){nextChangeSoon(true);return;}

// articleTitle is the wgTitle thingy with spaces and all that
vararticleTitle=getFirstTagContent(items,'title');
//console.info('articleTitle=%s', articleTitle);

if(recent2.ignore_safe_pages&&recent2.safePagesRe.test(articleTitle)){
//console.warn('Ignoring safe page %s', article);
nextChangeSoon(true);return;
}

if(recent2.hideNonArticles){
varnamespace=articleTitle.replace(/:.*/,'');
if(recent2.namespaces&&
((recent2.showTemplates&&namespace!=recent2.templateNamespace)||
!recent2.showTemplates)){
nextChangeSoon(true);return;
}
}

// perhaps ignore talk pages
if(!recent2.show_talkpages&&articleTitle
&&/^Talk:|^*?talk:/.test(articleTitle)){
nextChangeSoon(true);return;
}

// perhaps restrict to watchlist articles
if(recent2.filter_watchlist&&articleTitle&&
!window.watchlisttalk:/,':')]){
nextChangeSoon(true);return;
}

// filter against badwords regexp
if(recent2.filter_badwords){
varbadMatch=null;
vardiffCell=null;
varpreviousVandal=window.vandals;
varmatchesRe='';
varmatchesPlain='';
diffCellRe.lastIndex=0;
while(diffCell=diffCellRe.exec(diffText)){
// get content of addition table cells, faster than direct fulltext search
badWords.lastIndex=0;
// .test() is meant to be faster than a full match
if(badMatch=badWords.test(diffCell)){break;}
}
if(badMatch===true||previousVandal){
badWords.lastIndex=0;
varreMatch;
while(diffCell&&(reMatch=badWords.exec(diffCell))){
varbadWord=reMatch||reMatch||reMatch||'';
if(articleTitle.toLowerCase().indexOf(badWord.toLowerCase())<0){// avoid legit article title occurrences
badWord=badWord.replace(/^\s+|\s+$/g,'');
if(badWord!=''){
matchesPlain+=badWord+', ';
badWord=badWord.replace(/()/g,'\\$1');
matchesRe+=badWord+'|';
}
}
}
matchesRe=matchesRe.replace(/\|$/,'');
matchesPlain=matchesPlain.replace(/, $/,'');
if(!previousVandal&&matchesRe==''){nextChangeSoon();return;}
// highlighting
varhighlighted=diffCell&&diffCell;
if(matchesRe){
highlighted=highlighted.replace(RegExp('('+matchesRe+')','g'),
'<span style="background-color: #FF6">$1</span>');
}
articleTitle=getFirstTagContent(items,'title');
// linkify
highlighted=recent2.doLinkify(highlighted);
diffText=recent2.doLinkify(diffText);

if(previousVandal){
matchesPlain=' '+matchesPlain;
}

recent2.delayedLines={
timestamp:timestamp,article:article,count:recent2.count,articleTitle:articleTitle,
editor:editor,badWord:matchesPlain,badDiffFragment:highlighted,diff:diffText,summary:editSummary
};
}
}elseif(recent2.filter_spelling){
varsplMatch=null;
while(diffCell=diffCellRe.exec(diffText)){
if(splMatch=spellRe.test(diffCell)){break;}
}
if(splMatch){
splMatch=diffCell.match(spellRe);
varmisspelling=splMatch;//.replace(/^\s*/, '');
varbadWord='<a href=\'javascript:recent2.correctSpelling("'+articleTitle.split("'").join("%27")+
'","'+misspelling.split("'").join("%27")+'")\'>'+misspelling+'</a>';
diffText=diffText.replace(RegExp('('+misspelling+')','gi'),
'<span style="background-color: #FF6">$1</span>');
// linkify
diffText=recent2.doLinkify(diffText);
recent2.delayedLines={
timestamp:timestamp,article:article,count:recent2.count,articleTitle:articleTitle,
editor:editor,badWord:badWord,badDiffFragment:'',diff:diffText,summary:editSummary
};
}
}else{
vararticle=getFirstTagContent(items,'link');
vararticleTitle=getFirstTagContent(items,'title');
if(recent2.CustomFilter&&
!recent2.CustomFilter({timestamp:timestamp,article:article,articleTitle:articleTitle,
editor:editor,diff:diffText,summary:editSummary})){nextChangeSoon();return;}
// linkify
diffText=recent2.doLinkify(diffText);
recent2.delayedLines={
timestamp:timestamp,article:article,count:recent2.count,articleTitle:articleTitle,
editor:editor,diff:diffText,summary:editSummary
};
}
// schedule next iteration
nextChangeSoon();
return;
}



window.processRecentChangesDisplay=function(bundle){
varoutput=recent2.getDelayedLineOutput();
//console.log(output);
varoutputString='';
if(recent2.outputPosition=='top'){
outputString=output.join(recent2.outputSeparator);
}
else{
for(vari=output.length-1;i>=0;--i){
outputString+=output+(i>0?recent2.outputSeparator:'');
}
}
bundle.output.innerHTML+=outputString;
if(recent2.wait_for_output){recent2.pauseOutput();}
setTimeout(function(){recent2.doPopups(bundle.output)},300);
// overlap better than missing some out, i think; FIXME do this properly
processRecentChanges.lastDate=recent2.latest;// - 1;
varstatusTail=greyFont+'done up to '+formatTime(recent2.latest)+'</font>';
if(processRecentChanges.id>recent2.initialId){
statusTail+=' <a href="javascript:showHideDetailRange('+recent2.initialId+','+
processRecentChanges.id+')">toggle these details</a> |';
if(recent2.autoexpand){
setTimeout(function(){
/* document.title=initialId+' '+processRecentChanges.id; */
showHideDetailRange(recent2.initialId,processRecentChanges.id);},250);
}
}
statusTail+=' <a href="javascript:deleteEarlierOutputDivs('+bundle.status.id+')">remove earlier output</a>';
if(recent2.wait_for_output){
statusTail+=' | <a href="javascript:recent2.unpauseOutputOnce()">show new output</a>';
}
statusTail+='<br>';
bundle.status.innerHTML+=statusTail;
return;
}

// linkify and popupsify wikilinks
recent2.doLinkify=function(txt){
if(!txt||!recent2.linkify){returntxt;}

varinheritColor='color:inherit;color:expression(parentElement.currentStyle.color)';
varexternalLinkStyle='text-decoration:none;'
varinternalLinkStyle='text-decoration:none;'

externalLinkStyle=internalLinkStyle='text-decoration:none;border-bottom: 1px dotted;';

txt=txt.replace(/((https?|ftp):(\/\/\{\}\(\)<>\s&=\?#%]+|<*>)+)/g,function(p,p1){
p1=p1.replace(/<*>/g,'');
varurl=encodeURI(p1);
url=url.replace(/\"/g,'%22');
url=url.replace(/\'/g,'%27');
url=url.replace(/#/g,'%23');
varti=p1.replace(/\"/g,'&quot;');
return('<a href="'+url+'" style="'+externalLinkStyle+inheritColor+'" title="'+ti+'">'+p+'</a>');
});

// BUG: doLinkify('] badword</span> blah blah')
// gives '<a href=/123 ... >]</a> badword</span> blah blah'
// and the browser closes the <span> inside the </a>, so the badword is not red!

txt=txt.replace(/((\\{\}\n]*)(\n]*)(]\]))/g,function(p,p1,p2,p3){
p3=p3.replace(/<*>/g,'');
varurl=encodeURI(p3);
url=url.replace(/\"/g,'%22');
url=url.replace(/\'/g,'%27');
url=url.replace(/#/g,'%23');
url=recent2.articlePath+url;
varti=p3.replace(/\"/g,'&quot;');
return('<a href="'+url+'" style="'+internalLinkStyle+inheritColor+'" title="'+ti+'">'+p+'</a>');
});
return(txt);
}

processRecentChanges.lastDate=0;
processRecentChanges.id=0;

recent2.getDelayedLineOutput=function(){
varret=;
varid=processRecentChanges.id;
for(varainrecent2.delayedLines){
if(recent2.delayedLines&&typeofrecent2.delayedLines.count==typeof1&&
recent2.count-recent2.delayedLines.count>=recent2.delay){
recent2.delayedLines.id=id++;
varline=(recent2.doLine(recent2.delayedLines));
if(line){ret.push(line);}
deleterecent2.delayedLines;
}
}
processRecentChanges.id=id;
returnret;
}

window.deleteEarlierOutputDivs=function(cur){
for(vari=0;i<outputDivs.length;++i){
if(!outputDivs||!outputDivs.id)continue;
if(outputDivs.id>=0&&outputDivs.id<cur){
// FIXME BUG: if we go from the bottom up, then we'll delete one too many or too few, or something :-)
outputDivs.parentNode.removeChild(outputDivs);
outputDivs=null;
}
}
// scroll to the top if we're appending output to the bottom, to keep the div we've clicked visible after the deletions
if(recent2.outputPosition!='top')document.location='#';
}

window.showHideDetailRange=function(start,end){
// use the first div to see if we should show or hide
vardiv=document.getElementById('diff_div_'+start);
if(!div){alert('no such div: diff_div_'+start);return;}
varstate=false;// hide
if(div.style.display=='none')state=true;// show
for(vari=start;i<end;++i){
showHideDetail(i,true,state);
}
}

window.hideSysopEdits=function(hide){
vardivs=document.getElementsByTagName('div');
for(vari=0;i<divs.length;++i){
if(divs.className.indexOf('sysop_edit_line')>=0)divs.style.display=(hide?'none':'inline');
}
}
// mostly c/p of above
window.hideRollbackerEdits=function(hide){
vardivs=document.getElementsByTagName('div');
for(vari=0;i<divs.length;++i){
if(divs.className.indexOf('rollbacker_edit_line')>=0)divs.style.display=(hide?'none':'inline');
}
}
window.hideHuggledEdits=function(hide){
vardivs=document.getElementsByTagName('div');
for(vari=0;i<divs.length;++i){
if(divs.className.indexOf('huggled_edit_line')>=0)divs.style.display=(hide?'none':'inline');
}
}


window.bundles={};

window.vandalColour=function(vandal){
varnum=window.vandals;
if(!num)return'';
switch(num){
case1:return'#DDFFDD';
case2:return'#BBFFBB';
}
vari=9-(num-3)*2;
if(i<0)i=0;
return'#'+i+i+'FF'+i+i;
}

window.clickDetails=function(action,max){
if(!action)action='show';
if(!max)max=document.links.length;
varcount=0;
for(vari=0;i<document.links.length&&count<max;++i){
if(document.links.innerHTML==action+' details'&&document.links.href.indexOf('javascript:')==0){
++count;
eval(document.links.href.replace('javascript:',''));
}
}
}


recent2.pendingLines=;

recent2.unpauseOutputOnce=function(){
//console.log('unpausing once');
if(recent2.pausedOutput){
recent2.togglePausedOutput();
recent2.togglePausedOutput();
}
};

recent2.pauseOutput=function(){
//console.log('pausing');
if(!recent2.pausedOutput){recent2.togglePausedOutput();}
//console.log(recent2.pausedOutput);
}

recent2.unpauseOutput=function(){
//console.log('unpausing');
if(recent2.pausedOutput){recent2.togglePausedOutput();}
//console.log(recent2.pausedOutput);
}

recent2.togglePausedOutput=function(){
if(!recent2.pausedOutput){recent2.pausedOutput=true;returntrue;}
elserecent2.pausedOutput=false;
varoutputBuffer='';
while(recent2.pendingLines.length){
outputBuffer+=recent2.doLine(recent2.pendingLines.pop());
if(recent2.pendingLines.length){outputBuffer+=recent2.outputSeparator;}
}
varpos=recent2.outputPosition;
varoutput=newOutputDiv('recent2.lines',pos);
output.innerHTML=outputBuffer;
setTimeout(function(){recent2.doPopups(output)},300);
returnfalse;
}

recent2.togglePaused=function(){
if(!recent2.paused){recent2.paused=true;returntrue;}
recent2.paused=false;
loopRecentChanges(loopRecentChanges.url,loopRecentChanges.iterations);
returnfalse;
}

recent2.doLine=function(bundle){
if(recent2.pausedOutput){
recent2.pendingLines.push(bundle);
return'';
}
//if (recent2.filter_spelling) { return recent2.doSpellLine(bundle); }
varsysop=null;
if(typeof(recent2.sysopRegExp!='undefined')){
sysop=recent2.sysopRegExp.test(bundle.editor);
}
varrollbacker=null;
if(typeof(recent2.rollbackerRegExp!='undefined')){
rollbacker=recent2.rollbackerRegExp.test(bundle.editor);
}
varhuggled=null;
if(typeof(recent2.huggledRegExp!='undefined')){
huggled=recent2.huggledRegExp.test(bundle.editor);
}
varlastDiffPage=bundle.article+'&diff=cur&oldid=prev';
bundle.url=lastDiffPage;
saveBundle(bundle);

vardiv='';
vargroup='';
if(window.vandals){
if(window.vandals>0){
div='<div style="background-color:'+vandalColour(bundle.editor)+'">';
}
}
elseif(sysop){
group=' <i>(Admin)</i>';
if(recent2.hide_sysop_edits){
div='<div class="sysop_edit_line" style="display: none;">';
}
else{
div='<div class="sysop_edit_line">';
}
}elseif(rollbacker&&huggled){
group=' <i>(RBer, Whitelisted)</i>';
if(recent2.hide_rollbacker_edits||recent2.hide_huggled_edits){
div='<div class="rollbacker_edit_line huggled_edit_line" style="display: none;">';
}
else{
div='<div class="rollbacker_edit_line huggled_edit_line">';
}
}elseif(rollbacker){
group=' <i>(RBer)</i>';
if(recent2.hide_rollbacker_edits){
div='<div class="rollbacker_edit_line" style="display: none;">';
}
else{
div='<div class="rollbacker_edit_line">';
}
}elseif(huggled){
group=' <i>(Whitelisted)</i>';
if(recent2.hide_huggled_edits){
div='<div class="huggled_edit_line" style="display: none;">';
}
else{
div='<div class="huggled_edit_line">';
}
}
return(
div+
'<li>'+
' '+
formatTime(bundle.timestamp)+' '+
//latest + ' ' + processRecentChanges.lastDate + ' ' +
' '+linkmaker(bundle.article,bundle.articleTitle)+
(bundle.badWord?' matched <b>'+bundle.badWord+'</b> . . ':' . . ')+
linkmaker(recent2.articlePath+'User:'+bundle.editor,bundle.editor)+
group+' ('+
linkmaker(recent2.articlePath+'User_talk:'+bundle.editor,'talk')+' | '+
linkmaker(recent2.scriptPath+'index.php?title=User_talk:'+bundle.editor+'&action=edit&preview=yes&vanarticle='+encodeURIComponent(bundle.articleTitle),'warn')+' | '+
linkmaker(recent2.articlePath+'Special:Contributions/'+bundle.editor,'contribs')+' | '+
// linkmaker(recent2.articlePath + 'Special:Blockip/' + bundle.editor, 'block') + ') . . ' +
(bundle.summary?'<i>('+bundle.summary+')</i> . . ':'')+
'('+linkmaker(lastDiffPage,'last')+')'+
' ('+linkmaker(bundle.article+'&action=history','hist')+')'+
''+
'<p><div id="diff_div_'+bundle.id+'" style="display: none">'+
'</div></li>'+
(div?'</div>':'')
);
}

recent2.correctSpelling=function(article,badword){
varurl=recent2.articlePath+article+'?action=edit&autoclick=wpDiff&autominor=true';
varwl=badword.toLowerCase();
varcor=spelldict;
if(!cor||!cor.length){alert('Could not find an entry for '+wl);return;}
if(cor.length>1){
varq='Which correction should I use?\nPlease either type a number or another correction.\n';
for(vari=0;i<cor.length;++i){q+='\n'+i+': '+cor;}
varans=prompt(q);
if(!ans){return;}
varnum=parseInt(ans,10);
if(num>-1&&num<cor.length){cor=cor;}
else{cor=ans;}
}else{
cor=cor;
}
cor=cor.replace(/^ *| *$/g,'');
url+='&autosummary=Correcting%20spelling:%20'+wl+'->'+cor;
url+='&autoedit=';
c0=cor.charAt(0);
wl0=wl.charAt(0);
b='\\b';
url+=.join('#');
wl=wl0.toUpperCase()+wl.substring(1);
cor=c0.toUpperCase()+cor.substring(1);
url+=.join('#');
wl=wl.toUpperCase();
cor=cor.toUpperCase();
url+=.join('#');
window.open(url);
};

window.saveBundle=function(bundle){
varz={};
for(varpropinbundle){z=bundle;}
window.bundles=z;
}

window.vandals={};

window.tryRollback=function(id){
if(recent2.non_admin_rollback){recent2.tryNonAdminRollback(id);}
else{recent2.tryAdminRollback(id);}
};

recent2.getBundleVandal=function(id){
varb=window.bundles;
if(!b){
alert('No bundle! Please tell Lupin how to reproduce this error - it should not really happen.');
returnnull;
}
varvandal=b.editor;
if(window.vandals==null){window.vandals=1;}
else{window.vandals++;}
returnb;
}

recent2.tryAdminRollback=function(id){
varb=recent2.getBundleVandal(id);
if(!b){return;}
varvandal=b.editor;
varonSuccess=function(x,bundle){
varrollRe=RegExp('<a href="(/search/*?action=rollback*?from=(*)*?)".*?(<span class="comment">(.*?)</span>)?');
// match: useless
// match: url (escaped)
// match: last editor (escaped)
// match: last edit summary (wikiText - FIXME strip this to plain text)
varmatch=rollRe.exec(x.responseText);
if(!match){
alert('No rollback link found.'+
'\nMaybe you should try the "fake" rollback by checking the checkbox above?\n'+
'Alternatively, this may be a bug.');
return;
}
varlastEditor=match.split('+').join(' ');
varlastSummary=match||'';
if(lastEditor!=vandal){
varsummary=lastSummary.replace(RegExp('<*?>','g'),'');
if(!summary)summary=lastSummary;
alert('Could not rollback - someone else has edited since the vandal.\n\nPage: '+b.articleTitle+
'\nVandal: '+vandal+'\nLast editor: '+lastEditor+'\nEdit summary: '+summary);
return;
}
varrollbackUrl=match.split('&amp;').join('&');
recent2.openBackgroundWindow(rollbackUrl);
}
varonFailure=function(x,bundle){
alert('HTTP failed when trying to get rollback link in url\n'+bundle.url+
'\n\nHTTP status text: '+x.statusText);
returntrue;
}
recent2.download({url:b.url,onSuccess:onSuccess,id:b.id,onFailure:onFailure});
};

recent2.backgroundWindows=;
recent2.openBackgroundWindow=function(url){
varnewWindow=window.open(url);
self.focus();
recent2.backgroundWindows.push(newWindow);
if(recent2.backgroundWindows.length>recent2.backgroundWindowsMax){
if(!recent2.backgroundWindows.closed){
recent2.backgroundWindows.close();
}
recent2.backgroundWindows.shift();
}
return;
}

recent2.tryNonAdminRollback=function(id){
varb=recent2.getBundleVandal(id);
if(!b){return;}
varurl=recent2.scriptPath+'api.php?action=query&format=json&titles='+b.articleTitle+'&prop=revisions&rvlimit=30';
varonSuccess=function(x,y){recent2.processHistoryQuery(x,y,b);}
recent2.download({url:url,onSuccess:onSuccess,id:b.id});// fixme: onFailure
};

recent2.processHistoryQuery=function(x,downloadBundle,bundle){
varjson=x.responseText;
try{
eval('var o='+json);
varedits=recent2.anyChild(o.query.pages).revisions;

}
catch(someError){alert('JSON business failed.\n\n'+json.substring(0,200)
+'\n\nCannot rollback.');return;}
vari;
for(i=0;i<edits.length;++i){
if(edits!=bundle.editor){break;}
}
if(i===0){
alert('Could not rollback - someone else has edited since the vandal.\n\nPage: '+
bundle.articleTitle+
'\nVandal: '+bundle.editor+'\nLast editor: '+edits+
'\nEdit summary: '+edits);
return;
}
if(i==edits.length){
alert(bundle.editor+' seems to be the only editor to '+bundle.articleTitle+
'.\n\nRollback aborted.');return;
}
varprevEditor=edits;
varprevRev=edits;
varsummary='Reverted edits by [[Special:Contributions/'+escape(bundle.editor)+'|'+
escape(bundle.editor)+']] to last version by '+escape(prevEditor);
summary=summary.split(' ').join('%20');
varurl=bundle.article+'&action=edit&autosummary='+summary+'&oldid='+prevRev+
'&autoclick=wpSave&autominor=true&autowatch=false';
recent2.openBackgroundWindow(url);
};

recent2.anyChild=function(obj){
for(varpinobj){
returnobj;
}
returnnull;
};

recent2.doPopups=function(div){
if(typeof(window.setupTooltips)!='undefined'){setupTooltips(div);}
}

window.formatTime=function(timestamp){
vardate=newDate(timestamp);
varnums=;
for(vari=0;i<nums.length;++i)if(nums<10)nums='0'+nums;
returnnums.join(':');
}

window.showHideDetail=function(id,force,state){
vardiv=document.getElementById('diff_div_'+id);
varlk=document.getElementById('showdiff_link_'+id);
if(!div)return;
varbundle=window.bundles;
if(!div.innerHTML)div.innerHTML=(bundle.badDiffFragment?bundle.badDiffFragment:'')+bundle.diff;
if((force&&state==true)||(!force&&div.style.display=='none')){div.style.display='inline';lk.innerHTML='Hide';}
else{div.style.display='none';lk.innerHTML='Show';}

}

window.getFirstTagContent=function(parent,tag){
vare=parent.getElementsByTagName(tag);
if(e&&(e=e)){
varret=e.firstChild.nodeValue||e.nodeValue;
if(typeofret!=typeof'')return'';
returnret;
}
};

recent2.newCell=function(){
varnumCols=6;// this is optimized for widescreen monitors

varc=recent2.controls;
if(!c){return;}
if(!c.cellCount){
// start a table
c.cellCount=0;
c.table=document.createElement('table');
c.appendChild(c.table);
c.tbody=document.createElement('tbody');
c.table.appendChild(c.tbody);
}
if(!(c.cellCount%numCols)){
// start a row
c.curRow=document.createElement('tr');
c.tbody.appendChild(c.curRow);
}
// start a cell
c.curCell=document.createElement('td');
c.curRow.appendChild(c.curCell);
++c.cellCount;
};

recent2.newCheckbox=function(label,state,action,internalName,append){
// checkbox
recent2.newCell();
varret=document.createElement('input');
ret.type='checkbox';
ret.checked=state;
ret.onclick=function(){recent2.setBoxCookies();this.setVariables();};
ret.setVariables=action;
recent2.controls.curCell.appendChild(ret);
if(internalName){recent2.controls=ret;}
// label
varl=document.createElement('label');
l.innerHTML=label;
l.onclick=function(){ret.click();}
// recent2.controls.appendChild(l);
recent2.controls.curCell.appendChild(l);
recent2.checkboxes.push(ret);
returnret;
};

recent2.checkboxes=;

recent2.setBoxCookies=function(){
varn=1;
varval=0;
for(vari=0;i<recent2.checkboxes.length;++i){
val+=n*(recent2.checkboxes.checked?1:0);
n=n<<1;
}
document.cookie='recent2_checkboxes='+val+"; expires=Tue, 31-Dec-2030 23:59:59 GMT; path=/";
};

recent2.setCheckboxValuesFromCookie=function(){
varval=recent2.readCookie('recent2_checkboxes');
if(!val){return;}
val=parseInt(val,10);
for(vari=0;i<recent2.checkboxes.length;++i){
if(recent2.checkboxes.checked!=(val&1)){
recent2.checkboxes.checked=(val&1);
recent2.checkboxes.setVariables();
}
val=val>>1;
}
};

recent2.readCookie=function(name){
varnameEQ=name+"=";
varca=document.cookie.split(';');
for(vari=0;i<ca.length;i++){
varc=ca;
while(c.charAt(0)==' '){c=c.substring(1,c.length);}
if(c.indexOf(nameEQ)===0){returnc.substring(nameEQ.length,c.length);}
}
returnnull;
};

recent2.controlUI=function(){
recent2.controls=newOutputDiv('recent2.controls','top',true);

// control presets, will be changed by saved cookie settings
recent2.show_talkpages=true;
recent2.hideNonArticles=false;
recent2.showTemplates=false;
recent2.autoexpand=false;
recent2.delay_preset=false;
recent2.non_admin_rollback=!recent2.userIsSysop;
recent2.ignore_my_edits=false;
recent2.ignore_safe_pages=false;
recent2.hide_sysop_edits=false;
recent2.hide_rollbacker_edits=false;

// create controls
recent2.newCheckbox('Ignore talk pages',!recent2.show_talkpages,
function(){recent2.show_talkpages=!this.checked;},'talk');
recent2.newCheckbox('Ignore pages outside the article namespace',recent2.hideNonArticles,
function(){recent2.hideNonArticles=this.checked;},'hidenonarticles');
recent2.newCheckbox('... except for the Template namespace',recent2.showTemplates,
function(){recent2.showTemplates=this.checked;},'showtemplates');
recent2.newCheckbox('Automatically expand new content',recent2.autoexpand,
function(){recent2.autoexpand=this.checked;},'autoexpand');
recent2.newCheckbox('Only show edits unchanged after four updates',recent2.delay_preset,
function(){recent2.delay=(this.checked)?4:0;},'delayby4');
recent2.newCheckbox('Use "fake" rollback',recent2.non_admin_rollback,
function(){recent2.non_admin_rollback=this.checked;},'nonadminrollback');
recent2.newCheckbox('Ignore my edits',recent2.ignore_my_edits,
function(){recent2.ignore_my_edits=this.checked;},'ignoremyedits');
recent2.newCheckbox('Ignore safe pages',recent2.ignore_safe_pages,
function(){recent2.ignore_safe_pages=this.checked;},'ignoresafepages');
recent2.newCheckbox('Hide admin edits',recent2.hide_sysop_edits,
function(){recent2.hide_sysop_edits=this.checked;hideSysopEdits(recent2.hide_sysop_edits);},'hidesysopedits');
recent2.newCheckbox('Hide rollbacker edits',recent2.hide_sysop_edits,
function(){recent2.hide_rollbacker_edits=this.checked;hideRollbackerEdits(recent2.hide_rollbacker_edits);},'hiderollbackeredits');
recent2.newCheckbox('Use Huggle whitelist',recent2.hide_huggled_edits,
function(){recent2.hide_huggled_edits=this.checked;hideHuggledEdits(recent2.hide_huggled_edits);},'hidehugglededits');
// Placeholder
recent2.newCell();

varb=document.createElement('input');
b.type='button';
b.value='Pause updates';
b.onclick=function(){
b.value=(recent2.paused)?'Pause updates':'Resume updates';
recent2.togglePaused();
}
recent2.newCell();
recent2.controls.curCell.appendChild(b);
recent2.setCheckboxValuesFromCookie();
}

recent2.count=0;
window.loopRecentChanges=function(url,iterations){
if(!iterations)iterations=20;
loopRecentChanges.iterations=iterations;
loopRecentChanges.url=url;
grabRecentChanges(url);
setTimeout(function(){
if(recent2.paused||recent2.loading){++recent2.count;return;}
if(++recent2.count>=iterations&&!confirm('Continue monitoring recent changes?'))return;
recent2.count%=iterations;loopRecentChanges(url,iterations);
},recent2.updateSeconds*1000);
}

window.marvin=function(){

// store the contents of the H1
recent2.origFH=document.getElementById("firstHeading").firstChild.data;
// show a "loading" message
document.getElementById("firstHeading").firstChild.data="Loading the Anti-Vandal Tool...";
// keep people from messing with HotCat
document.getElementById("catlinks").style.display="none";

// check if user is a sysop
recent2.userIsSysop=false;
if(typeof(wgUserGroups)!='undefined'){
for(vari=0;i<wgUserGroups.length;++i){
if(wgUserGroups=='sysop'){
recent2.userIsSysop=true;
break;
}
}
}

// set chunk size for sysop list
if(recent2.userIsSysop){
recent2.apiAulimit=recent2.apiAulimitSysop;
}
else{
recent2.apiAulimit=recent2.apiAulimitUser;
}

// setup checkboxes
recent2.controlUI();

// start fetching recent changes
loopRecentChanges(recent2.feed,200);
}

// get the full sysop list in chunks
recent2.getSysops=function(startUser){
recent2.loading=true;
varparam='';
if(typeof(startUser)=='string'){
param='&aufrom='+encodeURIComponent(startUser);
}
recent2.download({
url:recent2.scriptPath+'api.php?action=query&maxage=7200&list=allusers&augroup=sysop&aulimit='+recent2.apiAulimit+'&format=json'+param,
onSuccess:recent2.processSysops,
onFailure:function(){setTimeout(recent2.getSysopList,15000);returntrue;}
});
}
recent2.sysopList='';
recent2.sysopCount=0;
recent2.processSysops=function(s){
varjson=s.responseText;
try{
eval('var o = '+json);
varusers=o.query.allusers;
}
catch(someError){
alert('Could not process admin list.\n\n"'+json.substring(0,400)+'"');
return;
}
for(vari=0;i<users.length;i++){
if(recent2.sysopList!=''){
recent2.sysopList+='|';
}
recent2.sysopList+=users.name.replace(/(\W)/g,'\\$1');
}
recent2.sysopCount+=i;
document.getElementById("firstHeading").firstChild.data=recent2.sysopCount+" admins";
if(users.length<recent2.apiAulimit){
recent2.sysopRegExp=newRegExp('^(?:'+recent2.sysopList+')$');
document.getElementById("firstHeading").firstChild.data="0 rollbackers";
recent2.loading=false;
}
else{
recent2.getSysops(users.name);
}
return;
}

// get the full rollbacker list in chunks, mostly a c/p of the code above
recent2.getRollbackers=function(startUser){
recent2.loading=true;
varparam='';
if(typeof(startUser)=='string'){
param='&aufrom='+encodeURIComponent(startUser);
}
recent2.download({
url:recent2.scriptPath+'api.php?action=query&maxage=7200&list=allusers&augroup=rollbacker&aulimit='+recent2.apiAulimit+'&format=json'+param,
onSuccess:recent2.processRollbackers,
onFailure:function(){setTimeout(recent2.getRollbackerList,15000);returntrue;}
});
}
recent2.rollbackerList='';
recent2.rollbackerCount=0;
recent2.processRollbackers=function(s){
varjson=s.responseText;
try{
eval('var o = '+json);
varusers=o.query.allusers;
}
catch(someError){
alert('Could not process rollbacker list.\n\n"'+json.substring(0,400)+'"');
return;
}
for(vari=0;i<users.length;i++){
if(recent2.rollbackerList!=''){
recent2.rollbackerList+='|';
}
recent2.rollbackerList+=users.name.replace(/(\W)/g,'\\$1');
}
recent2.rollbackerCount+=i;
document.getElementById("firstHeading").firstChild.data=recent2.rollbackerCount+" rollbackers";
if(users.length<recent2.apiAulimit){
recent2.rollbackerRegExp=newRegExp('^(?:'+recent2.rollbackerList+')$');
document.getElementById("firstHeading").firstChild.data=recent2.origFH;
recent2.loading=false;
}
else{
recent2.getRollbackers(users.name);
}
return;
}

// get Huggle's whitelisted user list, in one piece
recent2.getHuggleds=function(){
recent2.loading=true;
recent2.download({
url:recent2.scriptPath+recent2.huggleWhitelistPage,
onSuccess:recent2.processHuggleds,
onFailure:function(){setTimeout(recent2.getHuggleds,15000);returntrue;}
});
}
recent2.huggledList='';
recent2.processHuggleds=function(s){
try{
varusernames=s.responseText.split("\n").slice(2,-1);
}catch(someError){
alert('Could not process whitelist.\n\n"'+s.responseText.substring(0,400)+'"');
return;
}
for(vari=0;i<usernames.length;i++){
if(recent2.huggledList!=''){
recent2.huggledList+='|';
}
recent2.huggledList+=usernames.replace(/(\W)/g,'\\$1');
}
recent2.huggledRegExp=newRegExp('^(?:'+recent2.huggledList+')$');
recent2.loading=false;
return;
}

// **************************************************
// Installation
// **************************************************

recent2.addToolboxLink=function(url,name,id){
addPortletLink('p-tb',url,name,id);
}

window.addMarvin=function(){
recent2.addToolboxLink(recent2.articlePath+recent2.filterPage,
'Filter recent changes','toolbox_filter_changes');
recent2.addToolboxLink(recent2.articlePath+recent2.allRecentPage,
'All recent changes','toolbox_all_changes');
recent2.addToolboxLink(recent2.articlePath+recent2.recentIPPage,
'Recent IP edits','toolbox_IP_edits');
recent2.addToolboxLink(recent2.articlePath+recent2.monitorWatchlistPage,
'Monitor my watchlist','toolbox_watchlist_edits');
recent2.addToolboxLink(recent2.articlePath+recent2.spelldictPage,
'Live spellcheck','toolbox_spelling');
};

recent2.testPage=function(str){
returnRegExp(str.split(//).join(''),'i').test(document.location.href);
};

window.maybeStart=function(){
varloc=document.location.href;
if(recent2.testPage(recent2.filterPage)){
recent2.filter_badwords=true;
}elseif(recent2.testPage(recent2.allRecentPage)){
recent2.filter_badwords=false;
}elseif(recent2.testPage(recent2.recentIPPage)){
recent2.filter_anonsOnly=true;
}elseif(recent2.testPage(recent2.monitorWatchlistPage)){
recent2.filter_watchlist=true;
}elseif(recent2.testPage(recent2.spelldictPage)){
recent2.filter_spelling=true;
}else{
return;
}
setTimeout(marvin,1000);
}

// onload
$(maybeStart);
$(addMarvin);

// </nowiki></pre>

Text is available under the Creative Commons Attribution-ShareAlike License. Additional terms may apply.