User:Wæng/recent2.js

/*

This is a copy of Lupin's fantastic anti-vandal tool, with modifications. Please do not transclude it into your own .js; at times it may (for development reasons) work the opposite of the way you expect. Copy it at your own risk; there is no warranty or support either express or implied. Comments are welcome on the talk page. Philip Trueman

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.

Currently, the RSS feed is full of holes and so this may miss many edits. http://bugzilla.wikimedia.org/show_bug.cgi?id=3942

N.B. Tim Starling says the bug is now fixed. Also, note that the feed WILL have holes, because by default Special:Recentchanges excludes botflagged edits.



//

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:        'Wikipedia:Lists_of_common_misspellings/For_machines', spelldictPage:       'User:Lupin/Live_spellcheck', safePages:           '([Ww]ikipedia:([Ii]ntroduction|[Ss]andbox|[Tt]utorial[^/]*/sandbox)|[Tt]emplate:(X[1-9]|Sandbox))', linkify:             true, // leave this alone dummy: null };

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

var x = window.XMLHttpRequest ? new XMLHttpRequest : window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : false;

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

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=''; } window.gettingBadWords=false; window.badWords=null;

// paths if ( typeof(wgServer)!='string' ||    typeof(wgArticlePath)!='string' ||     typeof(wgScriptPath)!='string') { recent2.articlePath= 'http://' + document.location.hostname + '/wiki/'; recent2.scriptPath= 'http://' + document.location.hostname + '/w/'; } else { recent2.articlePath=mw.config.get('wgServer')+mw.config.get('wgArticlePath').replace(/\$1/, ''); recent2.scriptPath=mw.config.get('wgServer')+mw.config.get('wgScriptPath')+'/'; }

// Note whether certain bots are running. window.CBrunning=false; window.VBrunning=false;

var rbwins = new Array; var sigs = new Array; var sigindex = 2;

var pulled = 0; var duplicates = 0; var watched = 0; var trusted = 0;

var allreverts = 0; var myreverts = 0; var lastsuccessfulreverts = ' ';

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

window.diffCellRe=RegExp("\\+\\s*" +                        "]*>\\s* \\s*(.*?)\\s*\\s*", 'gi');

// processBadWords: generate the badWords RegExp from // the downloaded data. // d is the xmlhttprequest object from the download recent2.processBadWords=function(d) { var data=d.responseText.split('\n'); var phrase=[]; var string=[]; for (var i=0; i (?: s=s.replace(/\(?!\?/g, '(?:'); // check that s represents a valid regexp try { var r=new RegExp(s); } catch (err) { var errDiv=newOutputDiv('recent2_error', recent2.outputPosition); errDiv.innerHTML='Warning: ignoring odd-looking regexp on line '+i +' of badwords: ' + s + ' '; continue; }			if (isPhrase) phrase.push(s); else string.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('|') + ")|(^|[^/\\w])(" + phrase.join('|') + ")(?![/\\w]))", 'gi'); };

window.gettingWatchlist=false; recent2.watchlist=null;

recent2.getWatchlist=function { window.gettingWatchlist=true; recent2.download({url: recent2.articlePath + 'Special:Watchlist/edit',       onSuccess: recent2.processWatchlist,        onFailure: function  { setTimeout(getWatchlist, 15000); return true; }}); };

recent2.processWatchlist=function(req, bundle) { var watchlist={}; var lines=req.responseText.split('\n'); for (var i=0; i -1) { var article=lines[i].replace(/.*title="(.*?)">.*/, '$1'); watchlist[article]=true; } }  window.watchlist=watchlist; };

window.gettingSpelldict=false; window.spelldict=null;

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

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

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

window.newOutputDiv=function(klass, position, immortal) { var h1=document.getElementsByTagName('h1')[0]; var ret=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; }   return newOutputDiv(klass, 'bottom'); } if (!immortal) { ret.id=newOutputDiv.uid++; } window.outputDivs.push(ret); return ret; }; window.newOutputDiv.alerted=false; window.newOutputDiv.uid=0; window.outputDivs=[];

window.grabRecentChanges=function(feed) { if (! window.badWords && recent2.filter_badwords ) { if ( ! window.gettingBadWords ) { recent2.getBadWords; } return setTimeout(function{grabRecentChanges(feed);}, 500); } if (! window.watchlist && recent2.filter_watchlist) { if (! window.gettingWatchlist ) recent2.getWatchlist; return setTimeout(function{grabRecentChanges(feed);}, 500); } if (! window.spelldict && recent2.filter_spelling) { if (! window.gettingSpelldict) recent2.getSpelldict; return setTimeout(function{grabRecentChanges(feed);}, 500); } var pos=recent2.outputPosition; if (pos=='top') { var output=newOutputDiv('recent2.lines', pos); var status=newOutputDiv('recent2.status', pos); } else { var status=newOutputDiv('recent2.status', pos); var output=newOutputDiv('recent2.lines', pos); } status.style.borderStyle='solid'; status.style.borderColor='orange'; status.innerHTML=greyFont+'(' + recent2.count + ') updating... ';

// 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}); };

var greyFont='';

window.feedFailed=function(x,bundle) { try { bundle.status.innerHTML+=greyFont+'failed: '+x.statusText + ' '; } catch (err) { bundle.status.innerHTML+=greyFont+'failed badly: '+err+' '; } return true; };

recent2.newWindows=true;

window.linkmaker=function(url, text) { var s='' + text + ''; return s; };

recent2.pageblankRegex=RegExp('Blanked the page'); recent2.pagereplaceRegex=RegExp('Replaced.*content with'); recent2.revertedRegex=RegExp('Revert'); recent2.awbRegex=RegExp('(AWB|[Aa]dvisor.js|Reflinks|dashes)'); recent2.disambigRegex=RegExp('(^Disambiguat|^Categorization |^Stub-sorting |Repairing link to disambiguation page )'); recent2.partialrollbackRegex=RegExp('evert.*by[^\\.0-9]*([0-9][\\.0-9]*)[^\\.0-9]*to.*by[^\\.0-9]*([0-9][\\.0-9]*)[^\\.0-9]'); recent2.undoboteditRegex=RegExp('Undid revision.*([a-zA-Z]*Bot).*to');

recent2.ipUserRegex=RegExp('(User:)?((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +                          '(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])'); recent2.outputSeparator=' ';

recent2.delayedLines={}; recent2.delay=0; recent2.templateNamespace = 'Template'; recent2.namespaces={'Media':1, "Special":1, "User":1, "User talk":1, "Wikipedia":1, "Wikipedia talk":1, "Image":1, "Image 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};

window.processRecentChanges=function(req, bundle){ recent2.initialId=processRecentChanges.id; recent2.latest=processRecentChanges.lastDate; var doc; if (doc=req.responseXML.documentElement) { if (recent2.items=doc.getElementsByTagName('item')) { if ((recent2.itemsCurrent=recent2.items.length) > 0) { if (sigindex == 1) { sigs.shift; sigs[1] = new Object;} else if (sigindex == 0) { sigindex = 1; sigs[1] = new Object;} else { sigindex = 0; sigs[0] = new Object;}

pulled = 0; duplicates = 0; watched = 0; trusted = 0;

allreverts = 0; myreverts = 0; lastsuccessfulreverts = ' ';

recent2.bundleRef = bundle; processRecentChangesSingle; // start processing one diff every 50 ms       return; }   }  }   processRecentChangesDisplay(bundle); return; }

recent2.safePagesRe=new RegExp('^' + recent2.safePages + '$'); recent2.changeDelay=50; // 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--; var i = recent2.itemsCurrent; var items = recent2.items; if (i < 0) { processRecentChangesDisplay(recent2.bundleRef); return; } pulled++;

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

var diffText=getFirstTagContent(items[i],'description').split(' ').join(' \n'); var editSummary=diffText.replace(RegExp('^ (.*?) [\\s\\S]*'), '$1'); var editor=getFirstTagContent(items[i], 'creator') || getFirstTagContent(items[i], 'dc:creator');

// NB article is the link attribute - a fully qualified URL var article=getFirstTagContent(items[i], 'link'); if (recent2.delayedLines[article] && recent2.delayedLines[article].editor != editor) { delete recent2.delayedLines[article]; }

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

// Here we completely recast the logic for eliminating edits we have already seen. Instead of relying on // timestamps, we keep a record of the signatures of each edit, and don't show edits whose signatures we  // saw in the last bundle var sig = timestamp.toString + editor + articleTitle; sigs[sigindex][sig] = true; if ((sigindex == 1) && (sigs[0][sig] == true)) { duplicates++; nextChangeSoon(true); return; }

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

if (recent2.hideNonArticles) { var namespace=articleTitle.replace(/:.*/, ''); if (recent2.namespaces[namespace] &&       ( ( 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.watchlist[articleTitle.replace(/^Talk:/, '').replace(/[ _]talk:/, ':')]) { nextChangeSoon(true); return; }

watched++;

// Track reverts of watched changes if (recent2.revertedRegex.test(editSummary)) {    allreverts++; if (wgUserName==editor) {      myreverts++; lastsuccessfulreverts = lastsuccessfulreverts+articleTitle+' '; } }

// Mod here - we don't want to skip partial rollbacks by anyone if (!recent2.ipUserRegex.test(editor) && !recent2.partialrollbackRegex.test(editSummary)) {   if (recent2.filter_anonsOnly) { nextChangeSoon(true); return; }

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

// TODO: Make this a number that we can count down so that we can tell when CB stops running if (editor=='ClueBot') { window.CBrunning=true; } if (editor=='VoABot II') { window.VBrunning=true; }

// Modification here. Skip edits by certain trusted editors. This is a crude way to do it, // but it seems to work. Philip Trueman 11May2007

// bots if (editor=='ClueBot') { trusted++; nextChangeSoon(true); return; } if (editor=='VoABot II') { trusted++; nextChangeSoon(true); return; } if (editor=='SmackBot') { trusted++; nextChangeSoon(true); return; }

// active anti-vandalisers if (editor=='Anetode') { trusted++; nextChangeSoon(true); return; } if (editor=='Antandrus') { trusted++; nextChangeSoon(true); return; } if (editor=='DerHexer') { trusted++; nextChangeSoon(true); return; } if (editor=='Caltas') { trusted++; nextChangeSoon(true); return; } if (editor=='Gilliam') { trusted++; nextChangeSoon(true); return; } if (editor=='Gwernol') { trusted++; nextChangeSoon(true); return; } if (editor=='Gurch') { trusted++; nextChangeSoon(true); return; } if (editor=='Zzuuzz') { trusted++; nextChangeSoon(true); return; } if (editor=='Stemonitis') { trusted++; nextChangeSoon(true); return; } if (editor=='Euryalus') { trusted++; nextChangeSoon(true); return; } if (editor=='Onorem') { trusted++; nextChangeSoon(true); return; } if (editor=='Riana') { trusted++; nextChangeSoon(true); return; } if (editor=='Deor') { trusted++; nextChangeSoon(true); return; } if (editor=='Bobo192') { trusted++; nextChangeSoon(true); return; } if (editor=='Discospinster') { trusted++; nextChangeSoon(true); return; } if (editor=='Moonriddengirl') { trusted++; nextChangeSoon(true); return; } if (editor=='Wknight94') { trusted++; nextChangeSoon(true); return; } if (editor=='Akradecki') { trusted++; nextChangeSoon(true); return; } if (editor=='NewEnglandYankee') { trusted++; nextChangeSoon(true); return; } if (editor=='Oda Mari') { trusted++; nextChangeSoon(true); return; } if (editor=='Slangman') { trusted++; nextChangeSoon(true); return; } if (editor=='HalfShadow') { trusted++; nextChangeSoon(true); return; } if (editor=='Accurizer') { trusted++; nextChangeSoon(true); return; } if (editor=='Dawn Bard') { trusted++; nextChangeSoon(true); return; } if (editor=='Barneca') { trusted++; nextChangeSoon(true); return; } if (editor=='Makwy2') { trusted++; nextChangeSoon(true); return; } if (editor=='Salmar') { trusted++; nextChangeSoon(true); return; } if (editor=='Mlouns') { trusted++; nextChangeSoon(true); return; } if (editor=='SEWilco') { trusted++; nextChangeSoon(true); return; } if (editor=='GaryColemanFan') { trusted++; nextChangeSoon(true); return; } if (editor=='GlassCobra') { trusted++; nextChangeSoon(true); return; } if (editor=='Mdsummermsw') { trusted++; nextChangeSoon(true); return; } if (editor=='AlexiusHoratius') { trusted++; nextChangeSoon(true); return; } if (editor=='Mikkalai') { trusted++; nextChangeSoon(true); return; }

// This one is a little riskier. If the edit summary contained the word 'Revert' (NOT 'Undid') // will ignore the edit. TODO: Have a better test for whether the reversion was done with // an advanced tool such as TWINKLE or VandalProof if ((recent2.revertedRegex.test(editSummary)) && !window.vandals[editor]) { trusted++; nextChangeSoon(true); return; }

// clean-up merchants if (editor=='Ahoerstemeier') { trusted++; nextChangeSoon(true); return; } if (editor=='Rjwilmsi') { trusted++; nextChangeSoon(true); return; } if (editor=='TexasAndroid') { trusted++; nextChangeSoon(true); return; } if (editor=='Gaius Cornelius') { trusted++; nextChangeSoon(true); return; } if (editor=='Lightmouse') { trusted++; nextChangeSoon(true); return; } if (editor=='Kevinalewis') { trusted++; nextChangeSoon(true); return; } if (editor=='ShelfSkewed') { trusted++; nextChangeSoon(true); return; } if (editor=='Wikidudeman') { trusted++; nextChangeSoon(true); return; } if (editor=='ElinorD') { trusted++; nextChangeSoon(true); return; } if (editor=='SeanMack') { trusted++; nextChangeSoon(true); return; } if (editor=='Gogo Dodo') { trusted++; nextChangeSoon(true); return; } if (editor=='Nbauman') { trusted++; nextChangeSoon(true); return; } if (editor=='DMacks') { trusted++; nextChangeSoon(true); return; } if (editor=='Dudesleeper') { trusted++; nextChangeSoon(true); return; } if (editor=='Thumperward') { trusted++; nextChangeSoon(true); return; } if (editor=='Signalhead') { trusted++; nextChangeSoon(true); return; } if (editor=='Frecklefoot') { trusted++; nextChangeSoon(true); return; } if (editor=='MikeVitale') { trusted++; nextChangeSoon(true); return; } if (editor=='Johnbod') { trusted++; nextChangeSoon(true); return; } if (editor=='Utcursch') { trusted++; nextChangeSoon(true); return; } if (editor=='SandyGeorgia') { trusted++; nextChangeSoon(true); return; } if (editor=='WLU') { trusted++; nextChangeSoon(true); return; } if (editor=='Ioeth') { trusted++; nextChangeSoon(true); return; } if (editor=='Tabletop') { trusted++; nextChangeSoon(true); return; } if (editor=='Fabrictramp') { trusted++; nextChangeSoon(true); return; } if (editor=='Awadewit') { trusted++; nextChangeSoon(true); return; } if (editor=='Malleus Fatuorum') { trusted++; nextChangeSoon(true); return; } if (editor=='Aude') { trusted++; nextChangeSoon(true); return; } if (editor=='Scottandrewhutchins') { trusted++; nextChangeSoon(true); return; } if (editor=='Biscuittin') { trusted++; nextChangeSoon(true); return; } if (editor=='Woohookitty') { trusted++; nextChangeSoon(true); return; } if (editor=='Icairns') { trusted++; nextChangeSoon(true); return; } if (editor=='DGG') { trusted++; nextChangeSoon(true); return; } if (editor=='Andreasegde') { trusted++; nextChangeSoon(true); return; } if (editor=='G.-M. Cupertino') { trusted++; nextChangeSoon(true); return; } if (editor=='Rich Farmbrough') { trusted++; nextChangeSoon(true); return; } if (editor=='Wikidemo') { trusted++; nextChangeSoon(true); return; } if (editor=='LilHelpa') { trusted++; nextChangeSoon(true); return; } if (editor=='Scientizzle') { trusted++; nextChangeSoon(true); return; } if (editor=='Acalamari') { trusted++; nextChangeSoon(true); return; } if (editor=='Allstarecho') { trusted++; nextChangeSoon(true); return; } if (editor=='Dbachmann') { trusted++; nextChangeSoon(true); return; }

// This one is also a little risky. if ((recent2.awbRegex.test(editSummary)) && !window.vandals[editor]) { trusted++; nextChangeSoon(true); return; }

// This one is also a little risky. if ((recent2.disambigRegex.test(editSummary)) && !window.vandals[editor]) { trusted++; nextChangeSoon(true); return; }

// single issue fanatics if (editor=='BrownHairedGirl') { trusted++; nextChangeSoon(true); return; } if (editor=='Handicapper') { trusted++; nextChangeSoon(true); return; } if (editor=='Jwalte04') { trusted++; nextChangeSoon(true); return; } if (editor=='Nlu') { trusted++; nextChangeSoon(true); return; } if (editor=='Alientraveller') { trusted++; nextChangeSoon(true); return; } if (editor=='Rigadoun') { trusted++; nextChangeSoon(true); return; } if (editor=='Blofeld of SPECTRE') { trusted++; nextChangeSoon(true); return; } if (editor=='AnonEMouse') { trusted++; nextChangeSoon(true); return; } if (editor=='GrahamColm') { trusted++; nextChangeSoon(true); return; } if (editor=='Tenebrae') { trusted++; nextChangeSoon(true); return; } if (editor=='Deep Shadow') { trusted++; nextChangeSoon(true); return; } if (editor=='UtherSRG') { trusted++; nextChangeSoon(true); return; } if (editor=='Geologyguy') { trusted++; nextChangeSoon(true); return; } if (editor=='Jimfbleak') { trusted++; nextChangeSoon(true); return; } if (editor=='Soxrock') { trusted++; nextChangeSoon(true); return; } if (editor=='TTN') { trusted++; nextChangeSoon(true); return; } if (editor=='Lugnuts') { trusted++; nextChangeSoon(true); return; } if (editor=='Ksnow') { trusted++; nextChangeSoon(true); return; } if (editor=='Piotrus') { trusted++; nextChangeSoon(true); return; } if (editor=='Markussep') { trusted++; nextChangeSoon(true); return; } if (editor=='M-le-mot-dit') { trusted++; nextChangeSoon(true); return; } if (editor=='Mattythewhite') { trusted++; nextChangeSoon(true); return; } if (editor=='Everyking') { trusted++; nextChangeSoon(true); return; } if (editor=='JzG') { trusted++; nextChangeSoon(true); return; } if (editor=='Dickeybird') { trusted++; nextChangeSoon(true); return; } if (editor=='Arcadian') { trusted++; nextChangeSoon(true); return; } if (editor=='Neelix') { trusted++; nextChangeSoon(true); return; } if (editor=='Binksternet') { trusted++; nextChangeSoon(true); return; } if (editor=='Beetstra') { trusted++; nextChangeSoon(true); return; } if (editor=='Katharineamy') { trusted++; nextChangeSoon(true); return; } if (editor=='Gggh') { trusted++; nextChangeSoon(true); return; }

// various others if (editor=='Parkwells') { trusted++; nextChangeSoon(true); return; } if (editor=='Bearcat') { trusted++; nextChangeSoon(true); return; } if (editor=='Dougweller') { trusted++; nextChangeSoon(true); return; } if (editor=='Rodhullandemu') { trusted++; nextChangeSoon(true); return; } if (editor=='Hu12') { trusted++; nextChangeSoon(true); return; } if (editor=='MastCell') { trusted++; nextChangeSoon(true); return; } if (editor=='Flyer22') { trusted++; nextChangeSoon(true); return; } if (editor=='Casliber') { trusted++; nextChangeSoon(true); return; } if (editor=='Natalie Erin') { trusted++; nextChangeSoon(true); return; } if (editor=='Ckatz') { trusted++; nextChangeSoon(true); return; } if (editor=='Cirt') { trusted++; nextChangeSoon(true); return; } if (editor=='Skomorokh') { trusted++; nextChangeSoon(true); return; } if (editor=='Orangemike') { trusted++; nextChangeSoon(true); return; } if (editor=='EyeSerene') { trusted++; nextChangeSoon(true); return; } if (editor=='Bencherlite') { trusted++; nextChangeSoon(true); return; } if (editor=='Debresser') { trusted++; nextChangeSoon(true); return; } if (editor=='Levineps') { trusted++; nextChangeSoon(true); return; } if (editor=='Ryulong') { trusted++; nextChangeSoon(true); return; } if (editor=='Dweller') { trusted++; nextChangeSoon(true); return; } if (editor=='Hullaballoo Wolfowitz') { trusted++; nextChangeSoon(true); return; } if (editor=='Benjiboi') { trusted++; nextChangeSoon(true); return; } if (editor=='Giftlite') { trusted++; nextChangeSoon(true); return; } if (editor=='Snigbrook') { trusted++; nextChangeSoon(true); return; } if (editor=='Rama') { trusted++; nextChangeSoon(true); return; } if (editor=='RepublicanJacobite') { trusted++; nextChangeSoon(true); return; } if (editor=='AnemoneProjectors') { trusted++; nextChangeSoon(true); return; } if (editor=='Gamaliel') { trusted++; nextChangeSoon(true); return; } if (editor=='Ealdgyth') { trusted++; nextChangeSoon(true); return; } if (editor=='Tanthalus39') { trusted++; nextChangeSoon(true); return; } if (editor=='Materialscientist') { trusted++; nextChangeSoon(true); return; } if (editor=='Anonymous Dissident') { trusted++; nextChangeSoon(true); return; } if (editor=='SMcCandlish') { trusted++; nextChangeSoon(true); return; } if (editor=='Mild Bill Hiccup') { trusted++; nextChangeSoon(true); return; } if (editor=='Majorly') { trusted++; nextChangeSoon(true); return; } if (editor=='Jehochman') { trusted++; nextChangeSoon(true); return; } if (editor=='Geregen2') { trusted++; nextChangeSoon(true); return; } if (editor=='Philip Baird Shearer') { trusted++; nextChangeSoon(true); return; } if (editor=='Epeefleche') { trusted++; nextChangeSoon(true); return; } if (editor=='Zombie433') { trusted++; nextChangeSoon(true); return; } if (editor=='ACSE') { trusted++; nextChangeSoon(true); return; } if (editor=='Felix Folio Secundus') { trusted++; nextChangeSoon(true); return; } if (editor=='Serendipodous') { trusted++; nextChangeSoon(true); return; } if (editor=='Tassedethe') { trusted++; nextChangeSoon(true); return; } if (editor=='R\'n\'B') { trusted++; nextChangeSoon(true); return; } if (editor=='Arpingstone') { trusted++; nextChangeSoon(true); return; } if (editor=='WereSpielChequers') { trusted++; nextChangeSoon(true); return; } if (editor=='ThinkBlue') { trusted++; nextChangeSoon(true); return; } if (editor=='TenPoundHammer') { trusted++; nextChangeSoon(true); return; } if (editor=='North Shoreman') { trusted++; nextChangeSoon(true); return; } if (editor=='Tivedshambo') { trusted++; nextChangeSoon(true); return; } if (editor=='Spitfire') { trusted++; nextChangeSoon(true); return; } if (editor=='Barek') { trusted++; nextChangeSoon(true); return; } if (editor=='Invertzoo') { trusted++; nextChangeSoon(true); return; } if (editor=='RFD') { trusted++; nextChangeSoon(true); return; } if (editor=='Chris the speller') { trusted++; nextChangeSoon(true); return; } if (editor=='Koavf') { trusted++; nextChangeSoon(true); return; } if (editor=='WhisperToMe') { trusted++; nextChangeSoon(true); return; } if (editor=='SGGH') { trusted++; nextChangeSoon(true); return; } if (editor=='Cybercobra') { trusted++; nextChangeSoon(true); return; } if (editor=='Ukexpat') { trusted++; nextChangeSoon(true); return; } if (editor=='Neddyseagoon') { trusted++; nextChangeSoon(true); return; } if (editor=='Modernist') { trusted++; nextChangeSoon(true); return; } if (editor=='Olegwiki') { trusted++; nextChangeSoon(true); return; } if (editor=='Darrenhusted') { trusted++; nextChangeSoon(true); return; } if (editor=='Darwinek') { trusted++; nextChangeSoon(true); return; } if (editor=='BorgQueen') { trusted++; nextChangeSoon(true); return; } if (editor=='Rje') { trusted++; nextChangeSoon(true); return; } if (editor=='Davewild') { trusted++; nextChangeSoon(true); return; } if (editor=='MuffledThud') { trusted++; nextChangeSoon(true); return; } if (editor=='Literaturegeek') { trusted++; nextChangeSoon(true); return; } if (editor=='Moni3') { trusted++; nextChangeSoon(true); return; } if (editor=='Horkana') { trusted++; nextChangeSoon(true); return; } if (editor=='All Hallow\'s Wraith') { trusted++; nextChangeSoon(true); return; } if (editor=='Parrot of Doom') { trusted++; nextChangeSoon(true); return; } if (editor=='Emeraude') { trusted++; nextChangeSoon(true); return; } if (editor=='SlimVirgin') { trusted++; nextChangeSoon(true); return; } if (editor=='Nightscream') { trusted++; nextChangeSoon(true); return; } if (editor=='Edgar181') { trusted++; nextChangeSoon(true); return; } if (editor=='Attilios') { trusted++; nextChangeSoon(true); return; } if (editor=='ScienceApologist') { trusted++; nextChangeSoon(true); return; } if (editor=='DCGeist') { trusted++; nextChangeSoon(true); return; } if (editor=='Ericorbit') { trusted++; nextChangeSoon(true); return; } if (editor=='Timrollpickering') { trusted++; nextChangeSoon(true); return; } if (editor=='Chris Capoccia') { trusted++; nextChangeSoon(true); return; } if (editor=='Ash') { trusted++; nextChangeSoon(true); return; } if (editor=='Drbreznjev') { trusted++; nextChangeSoon(true); return; } if (editor=='DragonflySixtyseven') { trusted++; nextChangeSoon(true); return; } if (editor=='ChrisTheDude') { trusted++; nextChangeSoon(true); return; } if (editor=='Plasticspork') { trusted++; nextChangeSoon(true); return; } if (editor=='Kintetsubuffalo') { trusted++; nextChangeSoon(true); return; } if (editor=='JeanColumbia') { trusted++; nextChangeSoon(true); return; } if (editor=='Drmies') { trusted++; nextChangeSoon(true); return; } if (editor=='Philip Cross') { trusted++; nextChangeSoon(true); return; } if (editor=='Mjroots') { trusted++; nextChangeSoon(true); return; } if (editor=='NiciVampireHeart') { trusted++; nextChangeSoon(true); return; } if (editor=='Gimmetrow') { trusted++; nextChangeSoon(true); return; } if (editor=='SteveMiamiBeach') { trusted++; nextChangeSoon(true); return; } if (editor=='Tyrenius') { trusted++; nextChangeSoon(true); return; } if (editor=='Piano non troppo') { trusted++; nextChangeSoon(true); return; } if (editor=='Piledhigheranddeeper') { trusted++; nextChangeSoon(true); return; } if (editor=='Pinethicket') { trusted++; nextChangeSoon(true); return; } if (editor=='DocWatson42') { trusted++; nextChangeSoon(true); return; } if (editor=='McGeddon') { trusted++; nextChangeSoon(true); return; } if (editor=='Keith D') { trusted++; nextChangeSoon(true); return; } if (editor=='Snowmanradio') { trusted++; nextChangeSoon(true); return; } if (editor=='Paul Erik') { trusted++; nextChangeSoon(true); return; } if (editor=='Charles Matthews') { trusted++; nextChangeSoon(true); return; } }

// If the bots are running we will ignore all edits whose edit summaries contain the // words 'Blanked the page' or 'Replaced page with'. This is partly because they are faster // than most humans, and partly because these edits tend to generate large diffs which take // longer to scroll down through. if (window.CBrunning===true && recent2.pageblankRegex.test(editSummary)) { nextChangeSoon(true); return; } if (window.CBrunning===true && recent2.pagereplaceRegex.test(editSummary)) { nextChangeSoon(true); return; } if (window.VBrunning===true && recent2.pageblankRegex.test(editSummary)) { nextChangeSoon(true); return; } if (window.VBrunning===true && recent2.pagereplaceRegex.test(editSummary)) { nextChangeSoon(true); return; }

// filter against badwords regexp if (recent2.filter_badwords) { var badMatch=null; var diffCell=null; var previousVandal= window.vandals[editor]; var matchesRe=''; var matchesPlain=''; diffCellRe.lastIndex=0; // An edit summary that is the article title is often a sign of a bad edit var badEditSummary= (articleTitle==editSummary); // Highly experimental - 1 // The idea of this one is that a bot or anti-vandalism tool that reverts an IP to a previous version by   // a similar IP may have missed some dynamic IP vandalism var partRevert= recent2.partialrollbackRegex.exec(editSummary); if (partRevert) {     var a = partRevert[1].split('.'); var b = partRevert[2].split('.');

if ((a[0]==b[0]) && (a[1]==b[1])) {           badEditSummary=true; }   }    // Highly experimental - 2 // The idea of this one is an Undo of an edit by a Bot is likely to be bad var botUndo= recent2.undoboteditRegex.test(editSummary); if (botUndo) {       badEditSummary=true; }   // Why do the test if the editor is a known vandal? if (!previousVandal && !badEditSummary) { 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[1])) { break; } }   }    if (badMatch===true || previousVandal || badEditSummary) { badWords.lastIndex=0; var reMatch; while (diffCell && (reMatch=badWords.exec(diffCell[1]))) { var badWord=reMatch[2] || reMatch[4] || reMatch[6] || ''; 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(/([^\w ])/g, '\\$1'); matchesRe+=badWord+'|'; }       }      }      matchesRe=matchesRe.replace(/\|$/, ''); if (!previousVandal && !badEditSummary && matchesRe=='') { nextChangeSoon; return; } matchesPlain=matchesPlain.replace(/, $/, ''); // highlighting var highlighted=diffCell && diffCell[1]; if (matchesRe) { highlighted=highlighted.replace(RegExp('('+matchesRe+')', 'g'),                                       ' $1 '); }     // linkify highlighted=recent2.doLinkify(highlighted); diffText=recent2.doLinkify(diffText);

if (previousVandal) { matchesPlain = '[Previously rolled back this editor] ' + matchesPlain; }

if (badEditSummary) { matchesPlain = '[Suspicious edit summary] ' + matchesPlain; }

recent2.delayedLines[article]={ timestamp: timestamp, article:article, count:recent2.count, articleTitle:articleTitle, editor:editor, badWord:matchesPlain, badDiffFragment:highlighted, diff:diffText, summary:editSummary };   }  } else if (recent2.filter_spelling) { var splMatch=null; while (diffCell=diffCellRe.exec(diffText)) { if (splMatch=spellRe.test(diffCell[1])) { break; } }   if (splMatch) { splMatch = diffCell[1].match(spellRe); var misspelling = splMatch[1]; //.replace(/^\s*/, ''); var badWord = ''+ misspelling + ''; diffText = diffText.replace(RegExp('('+misspelling+')', 'gi'),                                 ' $1 '); // linkify diffText=recent2.doLinkify(diffText); recent2.delayedLines[article] = { timestamp: timestamp, article:article, count:recent2.count, articleTitle:articleTitle, editor:editor, badWord:badWord, badDiffFragment:'', diff:diffText, summary: editSummary };   }  } else { var article=getFirstTagContent(items[i], 'link'); var articleTitle=getFirstTagContent(items[i], '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[article]={ timestamp: timestamp, article:article, count:recent2.count, articleTitle:articleTitle, editor:editor, diff:diffText, summary:editSummary }; }  // schedule next iteration nextChangeSoon; return; }

window.processRecentChangesDisplay=function(bundle){ var output=recent2.getDelayedLineOutput; //console.log(output); var outputString=''; if (recent2.outputPosition=='top') { outputString=output.join(recent2.outputSeparator); } else { for (var i=output.length-1; i>=0; --i) { outputString+=output[i] + (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; var statusTail=greyFont+'done up to ' + formatTime(recent2.latest) + ' ' + pulled + '/' + duplicates + '/' + watched + '/' + trusted + ' ' + myreverts + '/' + allreverts + '  ' + lastsuccessfulreverts + ' '; 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+=' '; bundle.status.innerHTML+=statusTail; return; }

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

var inheritColor='color:inherit;color:expression(parentElement.currentStyle.color)'; var externalLinkStyle='text-decoration:none;' var internalLinkStyle='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, '');    var url=encodeURI(p1);    url=url.replace(/\"/g, '%22');    url=url.replace(/\'/g, '%27');    url=url.replace(/#/g, '%23');    var ti=p1.replace(/\"/g, '&quot;');    return('<a href="'+url+'" style="' + externalLinkStyle + inheritColor + '" title="'+ti+'">'+p+'</a>');  });

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

txt=txt.replace(/((\[\[)([^\|\[\]\{\}\n]*)([^\]\n]*)(]\]))/g, function (p,p1,p2,p3) {   p3=p3.replace(/<[^>]*>/g, '');    var url=encodeURI(p3);    url=url.replace(/\"/g, '%22');    url=url.replace(/\'/g, '%27');    url=url.replace(/#/g, '%23');    url=recent2.articlePath+url;    var ti=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 { var ret=[]; var id=processRecentChanges.id; for (var a in recent2.delayedLines) { if (recent2.delayedLines[a] && typeof recent2.delayedLines[a].count == typeof 1 &&       recent2.count - recent2.delayedLines[a].count >= recent2.delay) { recent2.delayedLines[a].id=id++; var line=(recent2.doLine(recent2.delayedLines[a])); if (line) { ret.push(line); } delete recent2.delayedLines[a]; } }  processRecentChanges.id=id; return ret; }

window.deleteEarlierOutputDivs=function(cur) { for(var i=0; i<outputDivs.length; ++i) { if (!outputDivs[i] || !outputDivs[i].id) continue; if (outputDivs[i].id >= 0 && outputDivs[i].id < cur) { // FIXME BUG: if we go from the bottom up, then we'll delete one too many or too few, or something :-)     outputDivs[i].parentNode.removeChild(outputDivs[i]);      outputDivs[i]=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 var div=document.getElementById('diff_div_' + start); if (!div) {alert('no such div: diff_div_' + start); return; } var state=false; // hide if (div.style.display=='none') state=true; // show for (var i=start; i<end; ++i) { showHideDetail(i, true, state); } }

window.toggleSysopEdits=function { var divs=document.getElementsByTagName('div'); for (var i=0; i<divs.length; ++i) { if (divs[i].className=='sysop_edit_line') divs[i].style.display= ( toggleSysopEdits.hidden ? 'none' : 'inline' ); } toggleSysopEdits.hidden = ! toggleSysopEdits.hidden; }

window.bundles={};

window.vandalColour = function(vandal) { var num=window.vandals[vandal]; if (!num) return ''; switch (num) { case 1: return '#DDFFDD'; case 2: return '#BBFFBB'; } var i= 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; var count=0; for (var i=0; i<document.links.length && count < max; ++i) { if(document.links[i].innerHTML==action + ' details' && document.links[i].href.indexOf('javascript:') == 0) { ++count; eval(document.links[i].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; return true; } else recent2.pausedOutput=false; var outputBuffer=''; while (recent2.pendingLines.length) { outputBuffer+=recent2.doLine(recent2.pendingLines.pop); if (recent2.pendingLines.length) { outputBuffer+=recent2.outputSeparator; } } var pos=recent2.outputPosition; var output=newOutputDiv('recent2.lines', pos); output.innerHTML=outputBuffer; setTimeout(function {recent2.doPopups(output)}, 300); return false; }

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

recent2.doLine=function(bundle) { if (recent2.pausedOutput) { recent2.pendingLines.push(bundle); return ''; } //if (recent2.filter_spelling) { return recent2.doSpellLine(bundle); } var sysop = null; if (typeof sysops != 'undefined') sysop=sysops.test(bundle.editor); //alert(bundle.article); var art = bundle.article.split('&') //alert(art[0]); bundle.article=art[0]; var lastDiffPage=bundle.article + '&diff=cur&oldid=prev'; bundle.url=lastDiffPage; saveBundle(bundle); var div=''; if (window.vandals[bundle.editor]) { if (window.vandals[bundle.editor] > 0) { div='<div style="background-color:' + vandalColour(bundle.editor) + '">'} } else if (sysop) {div='<div class="sysop_edit_line">'}; return div + '<li>' + formatTime(bundle.timestamp) + ' ' + //latest + ' ' + processRecentChanges.lastDate + ' ' + '(' + linkmaker(lastDiffPage, 'last') + ')' + ' (' + linkmaker(bundle.article+'&limit=20&action=history', 'hist') + ')' + ' ' + linkmaker(bundle.article, bundle.articleTitle) + ( bundle.badWord ? ' matched ' + bundle.badWord + ' . . ' : ' . . ') + linkmaker(recent2.articlePath + 'User:' + bundle.editor,          bundle.editor)     + ' ('  +  linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor,             'talk')     + ' | ' +  linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor+'?limit=20&action=history', 'talk_hist')     + ' | ' +  linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor + '?action=edit&section=new' + '&autoedit=s#$#\\n=== ===\\n%20' + '#&autosummary=Warnings', 'new-v1')    + ' | ' +  linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor + '?action=edit' + '&autoedit=s#$#\\n{'+'{subst:uw-test1|' + bundle.articleTitle + '}}%20' + '#&autosummary=Warnings', 'uw-test')    + ' | ' +  linkmaker(recent2.articlePath + 'User_talk:' + bundle.editor + '?action=edit' + '&autoedit=s#$#\\n{'+'{subst:uw-vandalism1|' + bundle.articleTitle + '}}%20' + '#&autosummary=Warnings', 'uw-vand')    + ' | ' +  linkmaker(recent2.articlePath + 'Special:Contributions/' + bundle.editor, 'contribs') + ' | ' +  linkmaker(recent2.articlePath + 'Wikipedia:Administrator_intervention_against_vandalism?action=edit&section=1' + '&autoedit=s#$#\\n*%20Vandalism%20on%20' + bundle.articleTitle + '%20after%20' + '#&autosummary=Reporting ' + bundle.editor + '', 'reportIP')    + ' | ' +  linkmaker(recent2.articlePath + 'Wikipedia:Administrator_intervention_against_vandalism?action=edit&section=1' + '&autoedit=s#$#\\n*%20Vandalism%20on%20' + bundle.articleTitle + '%20after%20' + '#&autosummary=Reporting ' + bundle.editor + '', 'reportV')    + ' | ' +  linkmaker(recent2.articlePath + 'Wikipedia:Usernames_for_administrator_attention?action=edit&section=1' + '&autoedit=s#$#\\n*%20Promotional username%20' + '#&autosummary=Reporting ' + bundle.editor + '', 'UAA') // ' | ' + linkmaker(recent2.articlePath + 'Special:Blockip/' + bundle.editor,       'block')  +  '). . ' + ( bundle.summary ? '('+bundle.summary+') . . ' : '') + '<a href="javascript:showHideDetail(' + bundle.id + ')" id="showdiff_link_' + bundle.id + '">show details</a>' + ' . . [<a href="javascript:tryRollback(' + bundle.id + ')" class="recent2_rollback">rollback</a>]' + ' <div id="diff_div_' + bundle.id + '" style="display: none">' + ' </li>' + ( div ? ' ' : '') ; };

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

window.saveBundle= function(bundle) { var z={}; for (var prop in bundle) { z[prop]=bundle[prop]; } window.bundles[bundle.id]=z; }

window.vandals={};

window.tryRollback=function(id) { if (recent2.non_admin_rollback) { recent2.tryNonAdminRollback(id); } else { recent2.tryFastAdminRollback(id); } // PT addition showHideDetail(id, true, false); // TODO: get the div of the next displayed diff and invoke scrollIntoView on it };

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

recent2.tryAdminRollback=function(id){ var b=recent2.getBundleVandal(id); if (!b) { return; } var vandal=b.editor; var onSuccess=function (x, bundle) { var rollRe=RegExp('<a href="(/w/index.php[^"]*?action=rollback[^"]*?from=([^&]*)[^"]*?)".*?( (.*?) )?');   // match[0]: useless    // match[1]: url (escaped)    // match[2]: last editor (escaped)    // match[4]: last edit summary (wikiText - FIXME strip this to plain text)    var match=rollRe.exec(x.responseText);    if (!match) {      alert('No rollback link found.' +            '\nMaybe you should try the non-admin rollback by checking the checkbox above?\n' +            'Alternatively, this may be a bug.');      return;    }    var lastEditor=match[2].split('+').join(' ');    var lastSummary=match[4] || ;    // var vandal=b.editor; // from the closure    if (lastEditor != vandal) {      var summary=lastSummary.replace(RegExp('<[^>]*?>','g'),);      if (!summary) summary=lastSummary;      this.focus;      alert( 'Could not rollback - someone else has edited since the vandal.\n\nPage: '+ b.articleTitle + '\nVandal: '+vandal+'\nLast editor: '+lastEditor+'\nEdit summary: '+summary);     return;    }    var rollbackUrl=match[1].split('&amp;').join('&');    // confirm('Rollback edits by '+vandal + ' to '+b.articleTitle+'?') &&    // window.open(rollbackUrl, '_blank');    var newWin = window.open(rollbackUrl, '_blank');    // Send the new window to the back    newWin.blur;    // Record this window    rbwins[rbwins.length] = newWin;    // Limit the number of open windows on a first-in first-out basis    if (rbwins.length > 10)    {      if (!rbwins[0].closed)        rbwins[0].close;      rbwins.shift;    }  }  var onFailure = function(x,bundle) {    alert('HTTP failed when trying to get rollback link in url\n' + bundle.url + '\n\nHTTP status text: ' + x.statusText);   return true;  }  recent2.download({ url:b.url, onSuccess: onSuccess, id: b.id, onFailure:onFailure}); };

recent2.tryNonAdminRollback=function(id) { if (typeof(autoEdit)=='undefined') { alert('You need to have autoedit functionality for non-admin rollback.\n\n' +         'This is included in Navigation popups - see WP:POP.\n\n'+          'Alternatively, you can try adding '+          '{'+'{subst:js|User:Lupin/autoedit.js}} ' +          'to your user javascript file.'); return; } var b=recent2.getBundleVandal(id); if (!b) { return; } var vandal=b.editor; var url=recent2.scriptPath + 'api.php?action=query&format=json&titles=' + b.articleTitle + '&prop=revisions&rvlimit=30'; var onSuccess=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) { var json=x.responseText; try { eval('var o='+json); var edits=recent2.anyChild(o.query.pages).revisions; } catch ( someError ) { alert('JSON business failed.\n\n' + json.substring(0,200)                              + '\n\nCannot rollback.'); return; } var i; for (i=0; i<edits.length; ++i) { if (edits[i]['user']!=bundle.editor) { if (window.vandals[edits[i]['user']]) {       alert('Would roll back to previous vandal! Rollback of ' + bundle.articleTitle + ' aborted!\n\n'); return; }     /* The plan here is to see if the previous editor is an IP close to the vandal's IP */ /* To start with we will just warn the user rather than roll back further          */ else if ((recent2.ipUserRegex.test(edits[i]['user'])) && (recent2.ipUserRegex.test(bundle.editor))) {     var a = edits[i]['user'].split('.'); var b = bundle.editor.split('.');

if ((a[0]==b[0]) && (a[1]==b[1])) {         alert( 'Possible chameleon IP vandal! Rollback of ' + bundle.articleTitle + ' aborted!\n\n'); return; }       else break; }     else 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[0]['user']+           '\nEdit summary: '+edits[0]['comment']); return; } if (i==edits.length) { alert(bundle.editor + ' seems to be the only editor to ' + bundle.articleTitle +         '.\n\nRollback aborted.'); return; } var prevEditor=edits[i]['user']; var prevRev=edits[i]['revid']; var edCount=i.toString + ((i===1) ? ' edit' : ' edits'); var summary='Reverted due to suspected vandalism ' + edCount + ' by ' + escape(bundle.editor) + ' to last version by ' + escape(prevEditor); summary=summary.split(' ').join('%20');

var art = bundle.article.split('&') //alert(art[0]); bundle.article=art[0];

var url=bundle.article + '&action=edit&autosummary=' + summary + '&oldid=' + prevRev + '&autoclick=wpSave&actoken=' + autoClickToken + '&autominor=true'; var newWin = window.open(url, '_blank'); // Send the new window to the back newWin.blur; // Record this window rbwins[rbwins.length] = newWin; // Limit the number of open windows on a first-in first-out basis if (rbwins.length > 10) {   if (!rbwins[0].closed) rbwins[0].close; rbwins.shift; } }; //recent2.non_admin_rollback=true;

recent2.tryFastAdminRollback=function(id) { var b=recent2.getBundleVandal(id); if (!b) { return; } var vandal=b.editor; var url=recent2.scriptPath + 'api.php?action=query&format=json&titles=' + b.articleTitle + '&prop=revisions&rvlimit=30&rvprop=user|comment&rvtoken=rollback'; var onSuccess=function(x,y){ recent2.processHistoryQueryFR(x,y,b); } recent2.download({ url: url, onSuccess: onSuccess, id: b.id}); // fixme: onFailure };

recent2.processHistoryQueryFR=function(x,downloadBundle, bundle) { var json=x.responseText; try { eval('var o='+json); var edits=recent2.anyChild(o.query.pages).revisions; } catch ( someError ) { alert('JSON business failed.\n\n' + json.substring(0,200)                              + '\n\nCannot rollback.'); return; } var i; for (i=0; i<edits.length; ++i) { if (edits[i]['user']!=bundle.editor) { if (window.vandals[edits[i]['user']]) {       alert('Would roll back to previous vandal! Rollback of ' + bundle.articleTitle + ' aborted!\n\n'); return; }     /* The plan here is to see if the previous editor is an IP close to the vandal's IP */ /* To start with we will just warn the user rather than roll back further          */ else if ((recent2.ipUserRegex.test(edits[i]['user'])) && (recent2.ipUserRegex.test(bundle.editor))) {     var a = edits[i]['user'].split('.'); var b = bundle.editor.split('.');

if ((a[0]==b[0]) && (a[1]==b[1])) {         alert( 'Possible chameleon IP vandal! Rollback of ' + bundle.articleTitle + ' aborted!\n\n'); return; }       else break; }     else 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[0]['user']+           '\nEdit summary: '+edits[0]['comment']); return; } if (i==edits.length) { alert(bundle.editor + ' seems to be the only editor to ' + bundle.articleTitle +         '.\n\nRollback aborted.'); return; } var rollbacktoken=edits[0]['rollbacktoken']; // var prevEditor=edits[i]['user']; // var edCount=i.toString + ((i===1) ? ' edit' : ' edits'); // var summary='Reverted ' + edCount + ' by ' + //  escape(bundle.editor) + ' to last version by ' + escape(prevEditor); // summary=summary.split(' ').join('%20');

var req = sajax_init_object;

var url=wgScriptPath + "/api.php";

var params="action=rollback&token="+encodeURIComponent(rollbacktoken)+"&title="+encodeURIComponent(bundle.articleTitle)+"&user="+encodeURIComponent(bundle.editor)+"&format=json";

//alert(params); 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) { response = eval('(' + req.responseText + ')'); //alert(req.responseText); delete req; } }  req.send(params); };

recent2.anyChild=function(obj) { for (var p in obj) { return obj[p]; } return null; };

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

window.formatTime=function(timestamp) { var date=new Date(timestamp); var nums=[date.getHours, date.getMinutes, date.getSeconds]; for (var i=0; i<nums.length; ++i) if (nums[i]<10) nums[i]='0'+nums[i]; return nums.join(':'); }

window.showHideDetail = function(id, force, state) { var div=document.getElementById('diff_div_' + id); var lk=document.getElementById('showdiff_link_' + id); if (!div) return; var bundle=window.bundles[id]; 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 details'; } else { div.style.display='none';  lk.innerHTML='show details'; }

}

window.getFirstTagContent=function(parent, tag) { var e=parent.getElementsByTagName(tag); if (e && (e=e[0]) ) { var ret = e.firstChild.nodeValue || e.nodeValue; if (typeof ret != typeof ) return ; return ret; } };

recent2.newCell=function { var numCols=3;

var c=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; var ret=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[internalName]=ret; } // label var l=document.createElement('label'); l.innerHTML=label; l.onclick=function{ ret.click; } // recent2.controls.appendChild(l); recent2.controls.curCell.appendChild(l); recent2.checkboxes.push(ret); return ret; };

recent2.checkboxes=[];

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

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

recent2.readCookie=function(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') { c = c.substring(1,c.length); } if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); } } return null; };

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

recent2.newCheckbox('Ignore talk pages', !recent2.show_talkpages,      function { recent2.show_talkpages=!this.checked; }, 'talk'); recent2.newCheckbox('Ignore pages outside the article namespace', false,      function { recent2.hideNonArticles = this.checked; }, 'hidenonarticles'); recent2.newCheckbox('... except for the Template namespace', false,      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', false,      function { recent2.delay = (this.checked) ? 4 : 0; }, 'delayby4'); recent2.newCheckbox('Use non-admin rollback', false,      function { recent2.non_admin_rollback = this.checked; }, 'nonadminrollback'); recent2.newCheckbox('Ignore my edits', false,      function { recent2.ignore_my_edits = this.checked; }, 'ignoremyedits'); recent2.newCheckbox('Ignore safe pages', false,      function { recent2.ignore_safe_pages = this.checked; }, 'ignoresafepages'); var b=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.count; return; }    if (++recent2.count >= iterations && ! confirm('Continue monitoring recent changes?') ) return;   recent2.count %= iterations; loopRecentChanges(url, iterations);  }, 10000); } window.marvin=function { // this isn't really used (not accessible from the UI), so don't worry about it window.sysops=RegExp("^(\\-\\- April|23skidoo|Lupin)$"); recent2.show_talkpages=true; recent2.controlUI; loopRecentChanges(recent2.feed, 200); }

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

recent2.addlilink=function(tabs, url, name, id, title, key){ var na = document.createElement('a'); na.href = url; na.appendChild(document.createTextNode(name)); var li = document.createElement('li'); if(id) li.id = id; li.appendChild(na); tabs.appendChild(li); if(id) { if(key && title) ta[id] = [key, title]; else if(key)    ta[id] = [key, '']; else if(title)  ta[id] = ['', title]; }   // re-render the title and accesskeys from existing code in wikibits.js    akeytt; return li; }

recent2.addToolboxLink=function(url, name, id){ var tb = document.getElementById('p-tb').getElementsByTagName('ul')[0]; recent2.addlilink(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) { return RegExp(str.split(/[_ ]/).join('[_ ]'), 'i').test(document.location.href); };

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

// onload addOnloadHook(maybeStart); addOnloadHook(addMarvin);

//// testing code //recent2.filter_badwords=true; //recent2.filter_spelling=true; //setTimeout(marvin,1000);

//