User:Enzo Aquarius/recent2.js

/* From User:Lupin at wikipedia, edited only slightly by me to work here.

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

To use it, add the following code to your user javascript. document.write(' ');



//

recent2={ // Edit these to your liking. // Make sure there's a comma at the end of each line. badwordsUrl:         'User:Enzo Aquarius/badwords', filterPage:          'User:Enzo Aquarius/Filter_recent_changes', allRecentPage:       'User:Enzo Aquarius/All_recent_changes', recentIPPage:        'User:Enzo Aquarius/Recent_IP_edits', monitorWatchlistPage: 'User:Enzo Aquarius/Monitor_my_watchlist', spelldictUrl:        'User:Enzo Aquarius/Misspellings', spelldictPage:       'User:Enzo Aquarius/Live_spellcheck',

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

window.gettingBadWords=false; window.badWords=null;

recent2.getBadWords=function { window.gettingBadWords=true; recent2.download({ url: 'http://' + document.location.hostname + '/index.php?title=' + recent2.badwordsUrl + '&action=raw&ctype=text/css', onSuccess: recent2.processBadWords, onFailure: function { setTimeout(recent2.getBadWords, 15000); return true;}}); }

recent2.processBadWords=function(d) { var data=d.responseText.split('\n'); var ret=[]; for (var i=0; ibadwords: ' + s + ' '; continue; }     ret.push(s); continue; }   if (s.charAt(0)=='<') continue; ret.push(s.replace(RegExp('([-|.\\+:!,?*^${}\\[\\]])', 'g'), '\\$1')); } //                                                     123                       3       2|       4                     415           5  //                                                     (((    repeatedchar       )       )|       ( ...  | ... | ... ))( bdy      ) window.badWords=RegExp(" [+] \\s*\\s*.*?((([^-{}.\\s'=wI:*#0-9A-F])\\3{2,})|[^/]\\b(" + ret.join('|') + "))(\\b[^/]|[|]).*\\s* ", 'im'); }

window.gettingWatchlist=false; recent2.watchlist=null; recent2.getWatchlist=function { window.gettingWatchlist=true; recent2.download({url: 'http://' + document.location.hostname + '/wiki/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: 'http://' + document.location.hostname + '/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(' [+] \\s*\\s*.*?\\b(' + a.join('|') + ')\\b', 'i'); }

var feed='http://' + document.location.hostname + '/index.php?title=Special:Recentchanges&feed=rss';

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 (recent2.outputPosition=='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.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.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){ var initialId=processRecentChanges.id; var doc=req.responseXML.documentElement; var items=doc.getElementsByTagName('item'); var latest=processRecentChanges.lastDate; for (var i=items.length - 1; i>=0; --i) { var timestamp = Date.parse(getFirstTagContent(items[i],'pubDate')); if (timestamp <= processRecentChanges.lastDate) continue; latest = (timestamp > latest) ? timestamp : 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');

var article=getFirstTagContent(items[i], 'link'); if (recent2.delayedLines[article] && recent2.delayedLines[article].editor != editor) { delete recent2.delayedLines[article]; }

if (recent2.filter_anonsOnly && !recent2.ipUserRegex.test(editor)) continue;

var articleTitle=getFirstTagContent(items[i], 'title'); if (recent2.hideNonArticles) { var namespace=articleTitle.replace(/:.*/, ''); if (recent2.namespaces[namespace]) continue; }   if (! recent2.show_talkpages && articleTitle && /^Talk:|^[^:]*?[_ ]talk:/.test(articleTitle)) continue; if (recent2.filter_watchlist && articleTitle && ! window.watchlist[articleTitle.replace(/^Talk:/, '').replace(/[ _]talk:/, ':')]) continue; if (recent2.filter_badwords) { var badMatch=null; if (window.vandals[editor] > 0) { badMatch=[, , '[previously rolled back this editor]']; } else { badMatch=badWords.test(diffText); }// .test is meant to be faster than a full match if (badMatch) { if (badMatch===true) { badMatch=diffText.match(badWords); } articleTitle=getFirstTagContent(items[i], 'title'); var badWord=badMatch[2] || badMatch[4]; if (articleTitle.toLowerCase.indexOf(badWord.toLowerCase)>-1) { continue; } // avoid "Oral sex matched Oral sex" // highlighting badMatch[0]=badMatch[0].split(badWord).join(' '+badWord+' '); recent2.delayedLines[article]={timestamp: timestamp, article:article, count:recent2.count, articleTitle:articleTitle, editor:editor, badWord:badWord, badDiffFragment:badMatch[0], diff:diffText, summary:editSummary}; }   } else if (recent2.filter_spelling) { var splMatch=spellRe.test(diffText); if (splMatch) { splMatch = diffText.match(spellRe); var misspelling = splMatch[1]; //.replace(/^\s*/, ''); var badWord = ''+ misspelling + ''; diffText = diffText.replace(RegExp('('+misspelling+')', 'gi'), ' $1 '); 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})) continue; recent2.delayedLines[article]={timestamp: timestamp, article:article, count:recent2.count, articleTitle:articleTitle, editor:editor, diff:diffText, summary:editSummary}; } } /* end for loop */ var output=recent2.getDelayedLineOutput; 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; setTimeout(function {recent2.doPopups(bundle.output)}, 300); processRecentChanges.lastDate=latest; // - 1; // overlap better than missing some out, i think; FIXME do this properly var statusTail=greyFont+'done up to ' + formatTime(latest) + ' '; if (processRecentChanges.id > initialId) { statusTail+=' toggle these details |'; if (recent2.autoexpand) { setTimeout( function {  /* document.title=initialId+' '+processRecentChanges.id; */   showHideDetailRange(initialId, processRecentChanges.id); }, 250 ); } }  statusTail += ' remove earlier output</a>'; statusTail+=' '; bundle.status.innerHTML+=statusTail; } 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++; ret.push(recent2.doLine(recent2.delayedLines[a])); 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.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); } var pos=recent2.outputPosition; var output=newOutputDiv('recent2.lines', pos); output.innerHTML=outputBuffer; 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 wikiBase='http://' + document.location.hostname + '/wiki/'; var sysop = null; if (typeof sysops != 'undefined') sysop=sysops.test(bundle.editor); var lastDiffPage=bundle.article + '?diff=cur&oldid=prev'; bundle.url=lastDiffPage; saveBundle(bundle); var div=''; 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+'?action=history', 'hist') + ')' + ' ' + linkmaker(bundle.article, bundle.articleTitle) + ( bundle.badWord ? ' matched ' + bundle.badWord + ' . . ' : ' . . ') + linkmaker(wikiBase + 'User:' + bundle.editor,          bundle.editor)     + ' ('  +  linkmaker(wikiBase + 'User_talk:' + bundle.editor,             'talk')     + ' | ' +  linkmaker(wikiBase + 'User_talk:' + bundle.editor + '?action=edit' + '&autoedit=s#$#\\n%20' + '#&autosummary=Your%20recent%20edits', 'warn')    + ' | ' +  linkmaker(wikiBase + 'Special:Contributions/' + bundle.editor, 'contribs') + ' | ' +  linkmaker(wikiBase + 'Special:Blockip/' + bundle.editor,       'block')    +  '). . ' + ( bundle.summary ? '('+bundle.summary+') . . ' : '') + 'show details</a>' + ' . . [rollback</a>]' + ' <div id="diff_div_' + bundle.id + '" style="display: none">' + ' </li>' + ( div ? ' ' : '') ; };

recent2.correctSpelling=function (article, badword) { var url= 'http://' + document.location.hostname + '/wiki/'; url += 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.tryAdminRollback(id); } };

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(']*>)?(.*?)? [^<>]*? ');   // 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;      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 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 (!autoEdit) { 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 '+  ' ' +  'to your user javascript file.'); return; } var b=recent2.getBundleVandal(id); if (!b) { return; } var vandal=b.editor; var url='http://' + document.location.hostname + '/query.php?format=json&titles=' + b.articleTitle; url += '&what=revisions&rvlimit=100&rvcomments'; 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 p=o['pages']; var edits=recent2.anyChild(p)['revisions']; } catch ( someError ) { alert('JSON business failed.\n\n' + json.substring(0,100)       + '\n\nCannot rollback.'); return; } var i; for (i=0; i<edits.length; ++i) { if (edits[i]['user']!=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[0]['user']+'\nEdit summary: '+edits[0]['*']); 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 summary='Reverted edits by ' + escape(bundle.editor) + ' to last version by ' + escape(prevEditor); summary=summary.split(' ').join('%20'); var url=bundle.article + '?action=edit&autosummary=' + summary + '&oldid=' + prevRev + '&autoclick=wpSave&autominor=true'; window.open(url, '_blank'); }; //recent2.non_admin_rollback=true;

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); 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.newCheckbox=function(label, state, action, internalName) { // checkbox var ret=document.createElement('input'); ret.type='checkbox'; ret.checked = state; ret.onclick = function { recent2.setBoxCookies; this.setVariables; }; ret.setVariables = action; recent2.controls.appendChild(ret); if (internalName) { recent2.controls[internalName]=ret; } // label var l=document.createElement('label'); l.innerHTML=label; recent2.controls.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.controls.newline = function{ recent2.controls.appendChild(document.createElement('br')); }; recent2.newCheckbox('Ignore talk pages', !recent2.show_talkpages,      function { recent2.show_talkpages=!this.checked; },       'talk'); recent2.controls.newline; recent2.newCheckbox('Ignore pages outside the article namespace', false,      function { recent2.hideNonArticles = this.checked; },       'hidenonarticles'); recent2.controls.newline; recent2.newCheckbox('Automatically expand new content', recent2.autoexpand,      function { recent2.autoexpand = this.checked; },       'autoexpand'); recent2.controls.newline; recent2.newCheckbox('Only show edits unchanged after four updates', false,      function { recent2.delay = (this.checked) ? 4 : 0; },       'delayby4'); recent2.controls.newline; recent2.newCheckbox('Use non-admin rollback', false,      function { recent2.non_admin_rollback = this.checked; },       'nonadminrollback'); 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);  }, 30000); } window.marvin=function { recent2.show_talkpages=true; recent2.controlUI; loopRecentChanges(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 { var prefix = 'http://' + document.location.hostname + '/wiki/'; recent2.addToolboxLink(prefix + recent2.filterPage, 'Filter recent changes', 'toolbox_filter_changes'); recent2.addToolboxLink(prefix + recent2.allRecentPage, 'All recent changes', 'toolbox_all_changes'); recent2.addToolboxLink(prefix + recent2.recentIPPage, 'Recent IP edits', 'toolbox_IP_edits'); recent2.addToolboxLink(prefix + recent2.monitorWatchlistPage, 'Monitor my watchlist', 'toolbox_watchlist_edits'); recent2.addToolboxLink(prefix + recent2.spelldictPage, 'Live spellcheck', 'toolbox_spelling'); //document.getElementById('toolbox_filter_changes').onclick=marvin; }

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; setTimeout(marvin, 1000); } else if (recent2.testPage(recent2.allRecentPage)) { recent2.filter_badwords=false; setTimeout(marvin, 1000); } else if (recent2.testPage(recent2.recentIPPage)) { recent2.filter_anonsOnly=true; setTimeout(marvin, 1000); } else if (recent2.testPage(recent2.monitorWatchlistPage)) { recent2.filter_watchlist=true; setTimeout(marvin, 1000); } else if (recent2.testPage(recent2.spelldictPage)) { recent2.filter_spelling=true; setTimeout(marvin, 1000); } }

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

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

//