/** * Anti-Vandal Tool * * 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. * @author: ] * @author: Helder (https://github.com/he7d3r) * @source: ] * * Dual license: * @license CC-BY 3.0 * @license GFDL 1.2 or any later version */ /*jshint camelcase: false, curly: true, eqeqeq: false, immed: true, latedef: true, newcap: true, noarg: true, noempty: true, nonew: true, quotmark: single, trailing: true, undef: false, unused: false, bitwise: false, forin: false, onevar: false, boss: true, eqnull: true, evil: true, funcscope: true, laxbreak: true, scripturl: true, shadow: true, wsh: true, nonstandard: true */ /*global mw, $, wikEdUseWikEd, WikEdUpdateFrame, setupTooltips, grabRecentChanges, processRecentChangesSingle, processRecentChanges, feedFailed, newOutputDiv, processRecentChangesDisplay, getFirstTagContent, nextChangeSoon, diffCellRe, badWords, spellRe, formatTime, maybeStart, showHideDetailRange, outputDivs, showHideDetail, loopRecentChanges, saveBundle, vandalColour, linkmaker, spelldict, hideSysopEdits, marvin, addMarvin, AVTAutoEdit, self */ //

mw.messages.set( {
	'avt-all-rc': 'All recent changes',
	'avt-auto-click': 'The "$1" button has been automatically clicked. ' +
		'Please wait for the next page to load.',
	'avt-auto-click-button-missing': 'Anti-Vandal Tool\n\nautoclick: could not find button "$1".',
	'avt-block': 'block',
	'avt-continue-question': 'Continue monitoring recent changes?',
	'avt-contribs': 'contribs',
	'avt-done': 'done up to $1',
	'avt-entry-not-found': 'Could not find an entry for $1.',
	'avt-error-HTTP-rollback': 'HTTP failed when trying to get rollback link in url\n$1' +
		'\n\nHTTP status text: $2',
	'avt-error-JSON': 'JSON business failed.\n\n$1\n\nCannot rollback.',
	'avt-error-no-bundle': 'No bundle! Please tell Lupin how to reproduce this error - it should not really happen.',
	'avt-error-no-rollback-link': 'No rollback link found.\n' +
		'Maybe you should try the non-admin rollback by checking the checkbox above?\n' +
		'Alternatively, this may be a bug.',
	'avt-error-sysop-list': 'Could not process admin list.\n\n"$1"',
	'avt-error-unable-to-rollback': 'Could not rollback - someone else has edited since the vandal.\n\n' +
		'Page: $1\nVandal: $2\nLast editor: $3\nEdit summary: $4',
	'avt-except-templates': '... except for the Template namespace',
	'avt-expand-content': 'Automatically expand new content',
	'avt-failed': 'failed: $1',
	'avt-failed-badly': 'failed badly: $1',
	'avt-filter-rc': 'Filter recent changes',
	'avt-hide': 'Hide',
	'avt-hist': 'hist',
	'avt-ignore-my-edits': 'Ignore my edits',
	'avt-ignore-outside-main': 'Ignore pages outside the article namespace',
	'avt-ignore-safe-pages': 'Ignore safe pages',
	'avt-ignore-sysop-edits': 'Hide admin edits',
	'avt-ignore-talk-pages': 'Ignore talk pages',
	'avt-ip-rc': 'Recent IP edits',
	'avt-last': 'last',
	'avt-matched': ' matched $1',
	'avt-missing-div': 'no such div: diff_div_$1',
	'avt-non-admin-rollback': 'Use non-admin rollback',
	'avt-only-unchanged': 'Only show edits unchanged after four updates',
	'avt-pause': 'Pause updates',
	'avt-remove-output': 'remove earlier output',
	'avt-resume': 'Resume updates',
	'avt-reverted-edits': 'Reverted edits by ] (]) to last version by $2',
	'avt-rollback': 'rollback',
	'avt-rollback-aborted': '$1 seems to be the only editor to $2.\n\nRollback aborted.',
	'avt-rolled-back': ' $1',
	'avt-select-correction': 'Which correction should I use?\nPlease either type a number or another correction.\n',
	'avt-show': 'Show',
	'avt-show-new': 'show new output',
	'avt-spelling-rc': 'Live spellcheck',
	'avt-talk': 'talk',
	'avt-toggle-details': 'toggle these details',
	'avt-unknown-position': 'Unknown position $1 in recent2.js, newOutputDiv.',
	'avt-updating': '($1) updating...',
	'avt-uw-test': 'uw-test',
	'avt-uw-vand': 'uw-vand',
	'avt-warning-regex': 'Warning: ignoring odd-looking regexp on line $1 ' +
		'of ]:'
		// FIXME: Remove this hack once ] is fixed
		.replace( /\$2/g, 'User:Lupin/badwords' ),
	'avt-watched-rc': 'Monitor my watchlist'
} );
var recent2={
	// Edit these to your liking.
	// Make sure there's a comma at the end of each line.
	badwordsPage:         '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',
	spelldictPage:        'Misplaced Pages:Lists_of_common_misspellings/For_machines',
	liveSpellcheckPage:   'User:Lupin/Live_spellcheck',
	safePages:            '(ikipedia:(ntroduction|andbox|utorial*/sandbox)|emplate:(X|Template sandbox))',
	linkify:              true,
	updateSeconds:        30,
	// FIXME: Use 
    and add a border to each
  • 's outputSeparator: '
    ', apiAulimitUser: 500, apiAulimitSysop: 5000, backgroundWindowsMax: 10, // leave this last one alone dummy: null }; /** * Downloads some data * * @param {Object} bundle Object with the following properties: * @param {string} bundle.url * @param {Function} (xmlhttprequest, bundle) Function to be executed when the download is done * @param {Function} (xmlhttprequest, bundle) Function to be executed when the download fails * @param {string} OK too, passed to onSuccess and onFailure * @return {Object} Object with a close function to close the notification * FIXME: Use jQuery and/or mw.Api */ recent2.download=function(bundle) { var x = window.XMLHttpRequest ? new XMLHttpRequest() : window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : false; if (x) { x.onreadystatechange=function() { if( x.readyState==4 ) { recent2.downloadComplete(x,bundle); } }; x.open('GET',bundle.url,true); x.send(null); } return x; }; recent2.downloadComplete=function(x,bundle) { if(x.status==200){ if(bundle.onSuccess){ bundle.onSuccess(x,bundle); } } else { if(bundle.onFailure){ bundle.onFailure(x,bundle); } else { alert(x.statusText); } } }; if (! recent2.outputPosition) { recent2.outputPosition=''; } window.gettingBadWords=false; window.badWords=null; // paths if ( typeof(mw.config.get('wgServer'))!='string' || typeof(mw.config.get('wgArticlePath'))!='string' || typeof(mw.config.get('wgScriptPath'))!='string') { recent2.articlePath= '//' + document.location.hostname + '/'; recent2.scriptPath= '//' + 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')+'/'; } recent2.getBadWords=function() { window.gettingBadWords=true; recent2.download({ url: recent2.scriptPath + 'index.php?title=' + // reload every 2 h recent2.badwordsPage + '&action=raw&ctype=text/css&max-age=7200', onSuccess: recent2.processBadWords, onFailure: function () { setTimeout(recent2.getBadWords, 15000); return true;}}); }; window.diffCellRe=/\+<\/td>\s*\s*\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) { 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 ) .html( mw.message( 'avt-warning-regex', i, recent2.badwordsPage ).parse() ) .append( $( '
    ' ).text( s ) );
    				continue;
    			}
    			if (isPhrase) {
    				phrase.push(s);
    			} else {
    				string.push(s);
    			}
    		} else {
    			// treat this line as a non-regexp and escape it.
    			phrase.push( mw.util.escapeRegExp(s) );
    		}
    	}
    	//                      123                                3       2|4                        4|5         56                        67        71
    	//                      (((    repeated char               )       )|(   ... | strings | ...  )|( border  )(   ... | phrases | ...  )( border ))
    	window.badWords=new RegExp('(((.\\s\'=wI:*#0-9a-f])\\3{2,})|(' + string.join('|') + ')|(^|)(' + phrase.join('|') + ')(?!))', 'gi');
    };
    window.gettingWatchlist=false;
    recent2.watchlist=null;
    recent2.getWatchlist=function() {
    	window.gettingWatchlist=true;
    	// FIXME: Use the MediaWiki API (action=query&list=watchlistraw)
    	recent2.download({url: recent2.articlePath + 'Special:Watchlist/raw',
    	onSuccess: recent2.processWatchlist,
    	onFailure: function () { setTimeout(recent2.getWatchlist, 15000); return true; }});
    };
    recent2.processWatchlist=function(req, bundle) {
    	var watchlist={};
    	var lines=req.responseText.split('\n');
    	var inList=false;
    	var article = '';
    	for (var i=0; i < lines.length; ++i) {
    		if (inList || lines.indexOf('